diff options
136 files changed, 65916 insertions, 2 deletions
@@ -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 0000000000..9bf4a2864f --- /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 <p></p> + * 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<QObject *> + * 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 <p>...</p> 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 0000000000..777240c831 --- /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 <qregexp.h> +#include "atom.h" +#include "location.h" +#include <stdio.h> + +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 0000000000..584e8fcd08 --- /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 <qstringlist.h> + +#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 0000000000..a722bb109e --- /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 <qregexp.h> +#include <qstringlist.h> + +#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 0000000000..ee30f85a05 --- /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 <qstring.h> + +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<QString, QString> + + 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 0000000000..791e08062b --- /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 <QMetaObject> +#include "codemarker.h" +#include "config.h" +#include "node.h" +#include <qdebug.h> +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +QString CodeMarker::defaultLang; +QList<CodeMarker *> 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<CodeMarker *>::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<CodeMarker *>::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<CodeMarker *>::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<CodeMarker *>::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<CodeMarker *>::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<const Node *>(string.toUInt()); + } + else { + return reinterpret_cast<const Node *>(string.toULongLong()); + } +} + +QString CodeMarker::stringForNode(const Node *node) +{ + if (sizeof(const Node *) == sizeof(ulong)) { + return QString::number(reinterpret_cast<quintptr>(node)); + } + else { + return QString::number(reinterpret_cast<qulonglong>(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<const FunctionNode *>(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<const TypedefNode*>(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<InnerNode *, int> 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<InnerNode*, int> 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<const FunctionNode*>(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<Section>& 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<const ClassNode *>(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<const FunctionNode *>(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<Parameter> ¶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<const PropertyNode*>(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<Section> CodeMarker::qmlSections(const QmlClassNode* , + SynopsisStyle ) +{ + return QList<Section>(); +} + +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 0000000000..894d838a3b --- /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 <qpair.h> + +#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<QPair<InnerNode *, int> > 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<QString, Node *> memberMap; + QMap<QString, Node *> reimpMemberMap; + QList<QPair<InnerNode *, int> > 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<Section> sections(const InnerNode *inner, + SynopsisStyle style, + Status status) = 0; + virtual QList<Section> 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<Section>& sectionList, const FastSection& fastSection, bool includeKeys = false); + +private: + QString macName(const Node *parent, const QString &name = QString()); + + static QString defaultLang; + static QList<CodeMarker *> markers; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp new file mode 100644 index 0000000000..5173a404f2 --- /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 <qdebug.h> + +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 *> CodeParser::parsers; +bool CodeParser::showInternal = false; +QMap<QString,QString> 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<CodeParser *>::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<CodeParser *>::ConstIterator p = parsers.begin(); + while (p != parsers.end()) { + (*p)->terminateParser(); + ++p; + } +} + +CodeParser *CodeParser::parserForLanguage(const QString& language) +{ + QList<CodeParser *>::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<CodeParser *>::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<CodeParser *>::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<QString> CodeParser::commonMetaCommands() +{ + return QSet<QString>() << 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<QmlClassNode*>(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<FakeNode *>(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<FakeNode *>(node); + fake->setTitle(arg); + if (fake->subType() == Node::Example) { + ExampleNode::exampleNodeMap.insert(fake->title(),static_cast<ExampleNode*>(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 0000000000..f522567ddb --- /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 <QSet> + +#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<QString> 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<CodeParser *> parsers; + static bool showInternal; + static QMap<QString,QString> nameToTitle; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp new file mode 100644 index 0000000000..f97b6ca93f --- /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 <QDir> +#include <QVariant> +#include <QFile> +#include <QTemporaryFile> +#include <QTextStream> +#include <qdebug.h> +#include "config.h" +#include <stdlib.h> + +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<MetaStackEntry> +{ +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<QString> Config::overrideOutputFormats; +QMap<QString, QString> 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<QString> 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<QString> Config::getStringSet(const QString& var) const +{ + return QSet<QString>::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<QString,QStringList>::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<sl.size(); ++i) { + t.append(QDir::cleanPath(sl[i])); + } + } + } + return t; +} + +/*! + Calls getRegExpList() with the control variable \a var and + iterates through the resulting list of regular expressions, + concatening them with some extras characters to form a single + QRegExp, which is returned/ + + \sa getRegExpList() + */ +QRegExp Config::getRegExp(const QString& var) const +{ + QString pattern; + QList<QRegExp> subRegExps = getRegExpList(var); + QList<QRegExp>::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<QRegExp> Config::getRegExpList(const QString& var) const +{ + QStringList strs = getStringList(var); + QStringList::ConstIterator s = strs.begin(); + QList<QRegExp> 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<QString> Config::subVars(const QString& var) const +{ + QSet<QString> 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<QString> &excludedDirs, + const QSet<QString> &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<QString> &excludedDirs, + const QSet<QString> &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 0000000000..461e0b969c --- /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 <QMap> +#include <QSet> +#include <QStringList> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +typedef QMultiMap<QString, QString> 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<QString> getOutputFormats() const; + QString getString(const QString& var) const; + QSet<QString> getStringSet(const QString& var) const; + QStringList getStringList(const QString& var) const; + QStringList getCleanPathList(const QString& var) const; + QRegExp getRegExp(const QString& var) const; + QList<QRegExp> getRegExpList(const QString& var) const; + QSet<QString> subVars(const QString& var) const; + void subVarsAndValues(const QString& var, QStringMultiMap& t) const; + QStringList getAllFiles(const QString& filesVar, + const QString& dirsVar, + const QSet<QString> &excludedDirs = QSet<QString>(), + const QSet<QString> &excludedFiles = QSet<QString>()); + + static QStringList getFilesHere(const QString& dir, + const QString& nameFilter, + const QSet<QString> &excludedDirs = QSet<QString>(), + const QSet<QString> &excludedFiles = QSet<QString>()); + 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<QString> overrideOutputFormats; + +private: + static bool isMetaKeyChar(QChar ch); + void load(Location location, const QString& fileName); + + QString prog; + Location loc; + Location lastLoc; + QMap<QString, Location> locMap; + QMap<QString, QStringList> stringListValueMap; + QMap<QString, QString> stringValueMap; + + static QMap<QString, QString> uncompressedFiles; + static QMap<QString, QString> 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 0000000000..cc68865cc0 --- /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 <qdebug.h> +#include <ctype.h> + +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<Parameter>::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<const EnumNode *>(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<const TypedefNode *>(node); + if (typedeff->associatedEnum()) { + synopsis = "flags " + name; + } + else { + synopsis = "typedef " + name; + } + break; + case Node::Property: + property = static_cast<const PropertyNode *>(node); + synopsis = name + " : " + typified(property->qualifiedDataType()); + break; + case Node::Variable: + variable = static_cast<const VariableNode *>(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<const QmlPropertyNode*>(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<const QmlPropertyNode*>(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<Section> CppCodeMarker::sections(const InnerNode *inner, + SynopsisStyle style, + Status status) +{ + QList<Section> sections; + + if (inner->type() == Node::Class) { + const ClassNode *classe = static_cast<const ClassNode *>(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<FunctionNode *>(*r); + if (func->isMacro()) + insert(macros, *r, style, status); + else + insert(relatedNonMembers, *r, style, status); + } + else { + insert(relatedNonMembers, *r, style, status); + } + ++r; + } + + QStack<const ClassNode *> 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<const VariableNode *>(*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<RelatedClass>::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<FunctionNode *>(*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<FunctionNode *>(*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<const ClassNode *> 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<RelatedClass>::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<FunctionNode *>(*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 *>(node); + return new TargetNode(static_cast<InnerNode*>(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<QString, int> types; + QMap<QString, int> 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<Section> CppCodeMarker::qmlSections(const QmlClassNode* qmlClassNode, + SynopsisStyle style) +{ + QList<Section> 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<const QmlPropGroupNode*>(*c); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + const QmlPropertyNode* pn = static_cast<const QmlPropertyNode*>(*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<const QmlPropertyNode*>(*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<const QmlPropertyNode*>(*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<const FunctionNode*>(*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<const FunctionNode*>(*c); + if (mn->isAttached()) + insert(qmlattachedmethods,*c,style,Okay); + else + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + if (qcn->qmlBase() != 0) { + qcn = static_cast<const QmlClassNode*>(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<const QmlPropGroupNode*>(*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<const QmlPropertyNode*>(*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<const FunctionNode*>(*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<const FunctionNode*>(*c); + if (mn->isAttached()) + insert(qmlattachedmethods,*c,style,Okay); + else + insert(qmlmethods,*c,style,Okay); + } + ++c; + } + if (qcn->qmlBase() != 0) { + qcn = static_cast<const QmlClassNode*>(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<const QmlPropGroupNode*>(*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<const QmlClassNode*>(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 0000000000..b898f9e3bd --- /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<Section> sections(const InnerNode *innerNode, + SynopsisStyle style, + Status status); + virtual QList<Section> 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 0000000000..b83e617ee3 --- /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 <qfile.h> +#include <stdio.h> +#include <errno.h> +#include "codechunk.h" +#include "config.h" +#include "cppcodeparser.h" +#include "tokenizer.h" +#include "tree.h" +#include <qdebug.h> + +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<QString>", "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<const EnumNode *>( + 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<QString, QString> 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<FunctionNode *>(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<FunctionNode *>(candidates.first()); + + overloads = candidates; + candidates.clear(); + for (int i = 0; i < overloads.count(); ++i) { + FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i)); + QList<Parameter> params1 = overload->parameters(); + QList<Parameter> 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<FunctionNode *>(candidates.first()); + + candidates.clear(); + for (int i = 0; i < overloads.count(); ++i) { + FunctionNode *overload = static_cast<FunctionNode *>(overloads.at(i)); + QList<Parameter> params1 = overload->parameters(); + QList<Parameter> 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<FunctionNode *>(candidates.first()); + + return 0; + } + } + delete clone; + } + return func; +} + +/*! + Returns the set of strings reopresenting the topic commands. + */ +QSet<QString> CppCodeParser::topicCommands() +{ + return QSet<QString>() << 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<Parameter> 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<ClassNode *>(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<ClassNode*>(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<const ClassNode*>(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... + + <type> <element>::<name> + <type> <QML-module>::<element>::<name> + + 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 <QML-module> 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... + + <type> <element>::<name>(<param>, <param>, ...) + <type> <QML-module>::<element>::<name>(<param>, <param>, ...) + + 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<const QmlClassNode*>(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<QString> 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<InnerNode *>(tre->findNode(QStringList(arg), + Node::Fake)); + } + else { + QStringList newPath = arg.split("::"); + pseudoParent = + static_cast<InnerNode*>(tre->findNode(QStringList(newPath), + Node::Class)); + if (!pseudoParent) + pseudoParent = + static_cast<InnerNode*>(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<QmlPropertyNode*>(node); + qpn->setDefault(); + } + else if (node->type() == Node::Fake && node->subType() == Node::QmlPropertyGroup) { + QmlPropGroupNode* qpgn = static_cast<QmlPropGroupNode*>(node); + qpgn->setDefault(); + } + } + else if (command == COMMAND_QMLREADONLY) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + qpn->setReadOnly(1); + } + else if (node->type() == Node::Fake && node->subType() == Node::QmlPropertyGroup) { + QmlPropGroupNode* qpgn = static_cast<QmlPropGroupNode*>(node); + qpgn->setReadOnly(1); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(*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<QString> metaCommands = doc.metaCommandsUsed(); + QSet<QString>::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<NamespaceNode*>(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<EnumNode*>(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<QString> topicCommandsAllowed = topicCommands(); + QSet<QString> otherMetacommandsAllowed = otherMetaCommands(); + QSet<QString> 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<QString> 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<Doc> 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<Doc>::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<InnerNode *>(*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 0000000000..f33ff6430d --- /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 <qregexp.h> + +#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<QString> 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<QString> 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<QString, Node::Type> 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<QString> usedNamespaces; + QMap<QString, QString> sequentialIteratorClasses; + QMap<QString, QString> mutableSequentialIteratorClasses; + QMap<QString, QString> associativeIteratorClasses; + QMap<QString, QString> 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 0000000000..a4c5cbeea0 --- /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 <ctype.h> +#include <qdebug.h> +#include <qlist.h> +#include <qiterator.h> +#include <qtextcodec.h> +#include <QUuid> + +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 <xref> 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(); // </t> + } + 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{<apiDesc>}. 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{<section>} + 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(); // </p> + } + return ++sectionNestingLevel; +} + +/*! + If the section nesting level is greater than 0, decrement + it. If it becomes 0, output a \c {</section>}. Return the + decremented section nesting level. + */ +int DitaXmlGenerator::leaveSection() +{ + if (sectionNestingLevel > 0) { + --sectionNestingLevel; + if (sectionNestingLevel == 0) + writeEndTag(); // </section> or </apiDesc> + } + 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<QString> editionNames = config.subVars(CONFIG_EDITION); + QSet<QString>::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<QString, QString>::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(); // </shortdesc> or </p> + 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(); // </codeblock> + } + break; + case Atom::Qml: + writeStartTag(DT_codeblock); + xmlWriter().writeAttribute("outputclass","qml"); + writeText(trimmedTrailing(atom->string()), marker, relative); + writeEndTag(); // </codeblock> + break; + case Atom::CodeNew: + writeStartTag(DT_p); + xmlWriter().writeCharacters("you can rewrite it as"); + writeEndTag(); // </p> + writeStartTag(DT_codeblock); + writeText(trimmedTrailing(atom->string()), marker, relative); + writeEndTag(); // </codeblock> + break; + case Atom::CodeOld: + writeStartTag(DT_p); + xmlWriter().writeCharacters("For example, if you have code like"); + writeEndTag(); // </p> + // fallthrough + case Atom::CodeBad: + writeStartTag(DT_codeblock); + writeCharacters(trimmedTrailing(plainCode(atom->string()))); + writeEndTag(); // </codeblock> + 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(); // </sectiondiv>, </bodydiv>, or </p> + if (divNestingLevel > 0) + --divNestingLevel; + } + break; + case Atom::FootnoteLeft: + // ### For now + if (in_para) { + writeEndTag(); // </p> + in_para = false; + } + xmlWriter().writeCharacters("<!-- "); + break; + case Atom::FootnoteRight: + // ### For now + 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(); // </sub> + 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<Node*> 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 <QString, Node *> groups = tree_->groups(); + foreach (const QString &groupName, editionGroupMap[editionName]) { + QList<Node *> 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<const FakeNode *>(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<Section> sections; + QList<Section>::ConstIterator s; + for (int i=0; i<LastSinceType; ++i) + sections.append(Section(sinceTitle(i),QString(),QString(),QString())); + + NodeMultiMap::const_iterator n = nsmap.value().constBegin(); + while (n != nsmap.value().constEnd()) { + const Node* node = n.value(); + switch (node->type()) { + 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<const FunctionNode*>(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(); // </ul> + + 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(); // </p> + 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(); // </xref> + xmlWriter().writeCharacters(":"); + writeEndTag(); // </p> + + 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<const ExampleNode*>(relative); + if (cen->imageFileName().isEmpty()) { + ExampleNode* en = const_cast<ExampleNode*>(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(); // </alt> + } + writeEndTag(); // </image> + if (currentTag() != DT_xref) + writeEndTag(); // </fig> + } + break; + case Atom::ImageText: + // nothing + break; + case Atom::ImportantLeft: + writeStartTag(DT_note); + xmlWriter().writeAttribute("type","important"); + break; + case Atom::ImportantRight: + writeEndTag(); // </note> + break; + case Atom::NoteLeft: + writeStartTag(DT_note); + xmlWriter().writeAttribute("type","note"); + break; + case Atom::NoteRight: + writeEndTag(); // </note> + 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(); // </p> + 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(); // </stentry> + writeStartTag(DT_stentry); + xmlWriter().writeCharacters("Value"); + writeEndTag(); // </stentry> + writeStartTag(DT_stentry); + xmlWriter().writeCharacters("Description"); + writeEndTag(); // </stentry> + writeEndTag(); // </sthead> + } + else { + writeStartTag(DT_simpletable); + xmlWriter().writeAttribute("outputclass","valuelist"); + writeStartTag(DT_sthead); + writeStartTag(DT_stentry); + xmlWriter().writeCharacters("Constant"); + writeEndTag(); // </stentry> + writeStartTag(DT_stentry); + xmlWriter().writeCharacters("Value"); + writeEndTag(); // </stentry> + writeEndTag(); // </sthead> + } + } + 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(); // </tt> + writeEndTag(); // </stentry> + writeStartTag(DT_stentry); + + QString itemValue; + if (relative->type() == Node::Enum) { + const EnumNode *enume = static_cast<const EnumNode *>(relative); + itemValue = enume->itemValue(atom->next()->string()); + } + + if (itemValue.isEmpty()) + xmlWriter().writeCharacters("?"); + else { + writeStartTag(DT_tt); + writeCharacters(protectEnc(itemValue)); + writeEndTag(); // </tt> + } + skipAhead = 1; + } + break; + case Atom::ListTagRight: + if (atom->string() == ATOM_LIST_TAG) + writeEndTag(); // </dt> + break; + case Atom::ListItemLeft: + if (atom->string() == ATOM_LIST_TAG) { + writeStartTag(DT_dd); + } + else if (atom->string() == ATOM_LIST_VALUE) { + if (threeColumnEnumValueTable) { + writeEndTag(); // </stentry> + 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(); // </dd> + } + else if (atom->string() == ATOM_LIST_VALUE) { + writeEndTag(); // </stentry> + writeEndTag(); // </strow> + } + else { + writeEndTag(); // </li> + } + break; + case Atom::ListRight: + if (atom->string() == ATOM_LIST_BULLET) { + writeEndTag(); // </ul> + } + else if (atom->string() == ATOM_LIST_TAG) { + writeEndTag(); // </dl> + } + else if (atom->string() == ATOM_LIST_VALUE) { + writeEndTag(); // </simpletable> + } + else { + writeEndTag(); // </ol> + } + 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(); // </p> + in_para = false; + } + break; + case Atom::QuotationLeft: + writeStartTag(DT_lq); + break; + case Atom::QuotationRight: + writeEndTag(); // </lq> + break; + case Atom::RawString: + if (atom->string() == " ") + break; + if (atom->string().startsWith(QLatin1Char('&'))) + writeCharacters(atom->string()); + else if (atom->string() == "<sup>*</sup>") { + writeStartTag(DT_sup); + writeCharacters("*"); + writeEndTag(); // </sup> + } + else if (atom->string() == "<sup>®</sup>") { + writeStartTag(DT_tm); + xmlWriter().writeAttribute("tmtype","reg"); + writeEndTag(); // </tm> + } + else { + writeStartTag(DT_pre); + xmlWriter().writeAttribute("outputclass","raw-html"); + writeCharacters(atom->string()); + writeEndTag(); // </pre> + } + break; + case Atom::SectionLeft: +#if 0 + if (inApiDesc) { + writeEndTag(); // </apiDesc> + 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(); // </title> (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(); // </p> + 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(); // </tbody> + writeEndTag(); // </tgroup> + writeEndTag(); // </table> + inTableHeader = false; + inTableBody = false; + tableColumnCount = 0; + break; + case Atom::TableHeaderLeft: + if (inTableBody) { + writeEndTag(); // </tbody> + writeEndTag(); // </tgroup> + writeEndTag(); // </table> + 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(); // </row> + if (matchAhead(atom, Atom::TableHeaderLeft)) { + skipAhead = 1; + writeStartTag(DT_row); + xmlWriter().writeAttribute("valign","top"); + } + else { + writeEndTag(); // </thead> + 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(); // </row> + break; + case Atom::TableItemLeft: + { + QString values; + writeStartTag(DT_entry); + for (int i=0; i<atom->count(); ++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(); // </entry> + else { + writeEndTag(); // </entry> + } + 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(); // </p> + in_para = false; + } + writeStartTag(DT_p); + writeGuidAttribute(Doc::canonicalTitle(atom->string())); + xmlWriter().writeAttribute("outputclass","target"); + //xmlWriter().writeCharacters(protectEnc(atom->string())); + writeEndTag(); // </p> + break; + case Atom::UnhandledFormat: + writeStartTag(DT_b); + xmlWriter().writeAttribute("outputclass","error"); + xmlWriter().writeCharacters("<Missing DITAXML>"); + writeEndTag(); // </b> + break; + case Atom::UnknownCommand: + writeStartTag(DT_b); + xmlWriter().writeAttribute("outputclass","error unknown-command"); + writeCharacters(protectEnc(atom->string())); + writeEndTag(); // </b> + 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 <cxxClass> 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<Section>::ConstIterator s; + + QString title; + QString rawTitle; + QString fullTitle; + if (inner->type() == Node::Namespace) { + const NamespaceNode* nsn = const_cast<NamespaceNode*>(static_cast<const NamespaceNode*>(inner)); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Namespace"; + + /* + Note: Because the C++ specialization we are using + has no <cxxNamespace> element, we are using the + <cxxClass> element with an outputclass attribute + set to "namespace" . + */ + generateHeader(inner, fullTitle); + generateBrief(inner, marker); // <shortdesc> + writeProlog(inner); + + writeStartTag(DT_cxxClassDetail); + writeStartTag(DT_cxxClassDefinition); + writeLocation(nsn); + writeEndTag(); // <cxxClassDefinition> + + enterApiDesc(QString(),title); + Text brief = nsn->doc().briefText(); // zzz + if (!brief.isEmpty()) { + writeStartTag(DT_p); + generateText(brief, nsn, marker); + writeEndTag(); // </p> + } + generateIncludes(nsn, marker); + generateStatus(nsn, marker); + generateThreadSafeness(nsn, marker); + generateSince(nsn, marker); + + enterSection("h2","Detailed Description"); + generateBody(nsn, marker); + generateAlsoList(nsn, marker); + leaveSection(); + leaveSection(); // </apiDesc> + + bool needOtherSection = false; + QList<Section> 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(); // </title> + 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(); // </title> + 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(); // </title> + s = summarySections.begin(); + while (s != summarySections.end()) { + if (s->members.isEmpty()) + generateSectionInheritedList(*s, inner, marker); + ++s; + } + } + leaveSection(); + } + + writeEndTag(); // </cxxClassDetail> + + // not included: <related-links> + // not included: <cxxClassNested> + + QList<Section> 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(); // </cxxClass> + } + else if (inner->type() == Node::Class) { + const ClassNode* cn = const_cast<ClassNode*>(static_cast<const ClassNode*>(inner)); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Class"; + + generateHeader(inner, fullTitle); + generateBrief(inner, marker); // <shortdesc> + writeProlog(inner); + + writeStartTag(DT_cxxClassDetail); + writeStartTag(DT_cxxClassDefinition); + writeStartTag(DT_cxxClassAccessSpecifier); + xmlWriter().writeAttribute("value",inner->accessString()); + writeEndTag(); // <cxxClassAccessSpecifier> + if (cn->isAbstract()) { + writeStartTag(DT_cxxClassAbstract); + xmlWriter().writeAttribute("name","abstract"); + xmlWriter().writeAttribute("value","abstract"); + writeEndTag(); // </cxxClassAbstract> + } + writeDerivations(cn, marker); // <cxxClassDerivations> + + // not included: <cxxClassTemplateParameters> + + writeLocation(cn); + writeEndTag(); // <cxxClassDefinition> + + enterApiDesc(QString(),title); + Text brief = cn->doc().briefText(); // zzz + if (!brief.isEmpty()) { + writeStartTag(DT_p); + generateText(brief, cn, marker); + writeEndTag(); // </p> + } + 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(); // </apiDesc> + + bool needOtherSection = false; + QList<Section> 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(); // </p> + 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(); // </p> + 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(); // </p> + s = summarySections.begin(); + while (s != summarySections.end()) { + if (s->members.isEmpty()) + generateSectionInheritedList(*s, inner, marker); + ++s; + } + } + leaveSection(); + } + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxClassDetail> + + // not included: <related-links> + // not included: <cxxClassNested> + + QList<Section> 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(); // </cxxClass> + } + else if ((inner->type() == Node::Fake) && (inner->subType() == Node::HeaderFile)) { + const FakeNode* fn = const_cast<FakeNode*>(static_cast<const FakeNode*>(inner)); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle; + + /* + Note: Because the C++ specialization we are using + has no <cxxHeaderFile> element, we are using the + <cxxClass> element with an outputclass attribute + set to "headerfile" . + */ + generateHeader(inner, fullTitle); + generateBrief(inner, marker); // <shortdesc> + 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(); // </p> + } + 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(); // </apiDesc> + + bool needOtherSection = false; + QList<Section> 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(); // </p> + 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(); // </p> + 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(); // </p> + s = summarySections.begin(); + while (s != summarySections.end()) { + if (s->members.isEmpty()) + generateSectionInheritedList(*s, inner, marker); + ++s; + } + } + leaveSection(); + } + + writeEndTag(); // </cxxClassDetail> + + // not included: <related-links> + // not included: <cxxClassNested> + + QList<Section> 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(); // </cxxClass> + } + else if ((inner->type() == Node::Fake) && (inner->subType() == Node::QmlClass)) { + const QmlClassNode* qcn = const_cast<QmlClassNode*>(static_cast<const QmlClassNode*>(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); // <shortdesc> + 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(); // </p> + } + 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(); // </apiDesc> + + QList<Section> 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(); // </p> + generateQmlSummary(*s,qcn,marker); + //generateSection(s->members, inner, marker, CodeMarker::Summary); + //generateSectionInheritedList(*s, inner, marker); + } + ++s; + } + leaveSection(); + } + + QList<Section> 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(); // </p> + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + generateDetailedQmlMember(*m, qcn, marker); + ++m; + } + } + ++s; + } + leaveSection(); + } + writeEndTag(); // </cxxClassDetail> + writeEndTag(); // </cxxClass> + } +} + + +/*! + 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(); // </xref> + writeEndTag(); // </li> +} + +/*! + 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<const DitaMapNode*>(fake); + writeDitaMap(dmn); + return; + } + + QList<Section> sections; + QList<Section>::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); // <shortdesc> + 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(); // </p> + 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(); // </section> + writeEndTag(); // </body> + writeRelatedLinks(fake, marker); + writeEndTag(); // </topic> +} + +/*! + This function writes a \e{<link>} element inside a + \e{<related-links>} element. + + \sa writeRelatedLinks() + */ +void DitaXmlGenerator::writeLink(const Node* node, + const QString& text, + const QString& role) +{ + if (node) { + QString link = fileName(node) + QLatin1Char('#') + node->guid(); + if (link.endsWith("#")) + qDebug() << "LINK ENDS WITH #:" << link << outFileName(); + writeStartTag(DT_link); + writeHrefAttribute(link); + xmlWriter().writeAttribute("role", role); + writeStartTag(DT_linktext); + writeCharacters(text); + writeEndTag(); // </linktext> + writeEndTag(); // </link> + } +} + +/*! + This function writes a \e{<related-links>} element, which + contains the \c{next}, \c{previous}, and \c{start} + links for topic pages that have them. Note that the + value of the \e role attribute is \c{parent} for the + \c{start} link. + */ +void DitaXmlGenerator::writeRelatedLinks(const FakeNode* node, CodeMarker* marker) +{ + const Node* linkNode = 0; + QPair<QString,QString> linkPair; + if (node && !node->links().empty()) { + writeStartTag(DT_relatedLinks); + if (node->links().contains(Node::PreviousLink)) { + linkPair = node->links()[Node::PreviousLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + writeLink(linkNode, linkPair.second, "previous"); + } + if (node->links().contains(Node::NextLink)) { + linkPair = node->links()[Node::NextLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + writeLink(linkNode, linkPair.second, "next"); + } + if (node->links().contains(Node::StartLink)) { + linkPair = node->links()[Node::StartLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + writeLink(linkNode, linkPair.second, "parent"); + } + writeEndTag(); // </related-links> + } +} + +/*! + Returns "xml" for this subclass of class Generator. + */ +QString DitaXmlGenerator::fileExtension(const Node * /* node */) const +{ + return "xml"; +} + +/*! + Writes an XML file header to the current XML stream. This + depends on which kind of DITA XML file is being generated, + which is determined by the \a node type and subtype and the + \a subpage flag. If the \subpage flag is true, a \c{<topic>} + header is written, regardless of the type of \a node. + */ +void DitaXmlGenerator::generateHeader(const Node* node, + const QString& name, + bool subpage) +{ + if (!node) + return; + + DitaTag mainTag = DT_cxxClass; + DitaTag nameTag = DT_apiName; + QString doctype; + QString dtd; + QString base; + QString version; + QString outputclass; + + if (node->type() == Node::Class) { + mainTag = DT_cxxClass; + nameTag = DT_apiName; + dtd = "dtd/cxxClass.dtd"; + version = "0.7.0"; + doctype = "<!DOCTYPE " + ditaTags[mainTag] + + " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" + + version + "//EN\" \"" + dtd + "\">"; + } + else if (node->type() == Node::Namespace) { + mainTag = DT_cxxClass; + nameTag = DT_apiName; + dtd = "dtd/cxxClass.dtd"; + version = "0.7.0"; + doctype = "<!DOCTYPE " + ditaTags[mainTag] + + " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" + + version + "//EN\" \"" + dtd + "\">"; + outputclass = "namespace"; + } + else if (node->type() == Node::Fake || subpage) { + if (node->subType() == Node::HeaderFile) { + mainTag = DT_cxxClass; + nameTag = DT_apiName; + dtd = "dtd/cxxClass.dtd"; + version = "0.7.0"; + doctype = "<!DOCTYPE " + ditaTags[mainTag] + + " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" + + version + "//EN\" \"" + dtd + "\">"; + outputclass = "headerfile"; + } + else if (node->subType() == Node::QmlClass) { + mainTag = DT_cxxClass; + nameTag = DT_apiName; + dtd = "dtd/cxxClass.dtd"; + version = "0.7.0"; + doctype = "<!DOCTYPE " + ditaTags[mainTag] + + " PUBLIC \"-//NOKIA//DTD DITA C++ API Class Reference Type v" + + version + "//EN\" \"" + dtd + "\">"; + outputclass = "QML-class"; + } + else { + mainTag = DT_topic; + nameTag = DT_title; + dtd = "dtd/topic.dtd"; + doctype = "<!DOCTYPE " + ditaTags[mainTag] + + " PUBLIC \"-//OASIS//DTD DITA Topic//EN\" \"" + dtd + "\">"; + switch (node->subType()) { + case Node::Page: + outputclass = node->pageTypeString(); + break; + case Node::Group: + outputclass = "group"; + break; + case Node::Example: + outputclass = "example"; + break; + case Node::File: + outputclass = "file"; + break; + case Node::Image: // not used + outputclass = "image"; + break; + case Node::Module: + outputclass = "module"; + break; + case Node::ExternalPage: // not used + outputclass = "externalpage"; + break; + default: + outputclass = "page"; + } + } + } + + xmlWriter().writeDTD(doctype); + xmlWriter().writeComment(node->doc().location().fileName()); + writeStartTag(mainTag); + QString id = node->guid(); + xmlWriter().writeAttribute("id",id); + if (!outputclass.isEmpty()) + xmlWriter().writeAttribute("outputclass",outputclass); + writeStartTag(nameTag); // <title> or <apiName> + writeCharacters(name); + writeEndTag(); // </title> or </apiName> +} + +/*! + Outputs the \e brief command as a <shortdesc> element. + */ +void DitaXmlGenerator::generateBrief(const Node* node, CodeMarker* marker) +{ + Text brief = node->doc().briefText(true); // zzz + if (!brief.isEmpty()) { + generateText(brief, node, marker); + } +} + +/*! + Writes the \c {#include ...} required to include the class + or namespace in a compilation. + */ +void DitaXmlGenerator::generateIncludes(const InnerNode* inner, CodeMarker* marker) +{ + if (!inner->includes().isEmpty()) { + writeStartTag(DT_codeblock); + writeText(marker->markedUpIncludes(inner->includes()), marker, inner); + writeEndTag(); // </codeblock> + } +} + +/*! + zzz + Generates a table of contents beginning at \a node. + Currently just returns without writing anything. + */ +void DitaXmlGenerator::generateTableOfContents(const Node* node, + CodeMarker* marker, + Doc::Sections sectionUnit, + int numColumns, + const Node* relative) + +{ + return; + if (!node->doc().hasTableOfContents()) + return; + QList<Atom *> toc = node->doc().tableOfContents(); + if (toc.isEmpty()) + return; + + QString nodeName = ""; + if (node != relative) + nodeName = node->name(); + + QStringList sectionNumber; + int columnSize = 0; + + QString tdTag; + if (numColumns > 1) { + tdTag = "<td>"; /* width=\"" + QString::number((100 + numColumns - 1) / numColumns) + "%\">";*/ + out() << "<table class=\"toc\">\n<tr class=\"topAlign\">" + << tdTag << '\n'; + } + + // disable nested links in table of contents + inContents = true; + inLink = true; + + for (int i = 0; i < toc.size(); ++i) { + Atom *atom = toc.at(i); + + int nextLevel = atom->string().toInt(); + if (nextLevel > (int)sectionUnit) + continue; + + if (sectionNumber.size() < nextLevel) { + do { + out() << "<ul>"; + sectionNumber.append("1"); + } while (sectionNumber.size() < nextLevel); + } + else { + while (sectionNumber.size() > nextLevel) { + out() << "</ul>\n"; + sectionNumber.removeLast(); + } + sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); + } + int numAtoms; + Text headingText = Text::sectionHeading(atom); + + if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) { + out() << "</ul></td>" << tdTag << "<ul>\n"; + columnSize = 0; + } + out() << "<li>"; + out() << "<xref href=\"" + << nodeName + << "#" + << Doc::canonicalTitle(headingText.toString()) + << "\">"; + generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); + out() << "</xref></li>\n"; + + ++columnSize; + } + while (!sectionNumber.isEmpty()) { + out() << "</ul>\n"; + sectionNumber.removeLast(); + } + + if (numColumns > 1) + out() << "</td></tr></table>\n"; + + inContents = false; + inLink = false; +} + +/*! + zzz + Revised for the new doc format. + Generates a table of contents beginning at \a node. + */ +void DitaXmlGenerator::generateTableOfContents(const Node* node, + CodeMarker* marker, + QList<Section>* sections) +{ + QList<Atom*> toc; + if (node->doc().hasTableOfContents()) + toc = node->doc().tableOfContents(); + if (toc.isEmpty() && !sections && (node->subType() != Node::Module)) + return; + + QStringList sectionNumber; + int detailsBase = 0; + + // disable nested links in table of contents + inContents = true; + inLink = true; + + out() << "<div class=\"toc\">\n"; + out() << "<h3>Contents</h3>\n"; + sectionNumber.append("1"); + out() << "<ul>\n"; + + if (node->subType() == Node::Module) { + if (moduleNamespaceMap.contains(node->name())) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><xref href=\"#" + << registerRef("namespaces") + << "\">Namespaces</xref></li>\n"; + } + if (moduleClassMap.contains(node->name())) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><xref href=\"#" + << registerRef("classes") + << "\">Classes</xref></li>\n"; + } + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><xref href=\"#" + << registerRef("details") + << "\">Detailed Description</xref></li>\n"; + for (int i = 0; i < toc.size(); ++i) { + if (toc.at(i)->string().toInt() == 1) { + detailsBase = 1; + break; + } + } + } + else if (sections && (node->type() == Node::Class)) { + QList<Section>::ConstIterator s = sections->begin(); + while (s != sections->end()) { + if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><xref href=\"#" + << registerRef((*s).pluralMember) + << "\">" << (*s).name + << "</xref></li>\n"; + } + ++s; + } + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><xref href=\"#" + << registerRef("details") + << "\">Detailed Description</xref></li>\n"; + for (int i = 0; i < toc.size(); ++i) { + if (toc.at(i)->string().toInt() == 1) { + detailsBase = 1; + break; + } + } + } + + for (int i = 0; i < toc.size(); ++i) { + Atom *atom = toc.at(i); + int nextLevel = atom->string().toInt() + detailsBase; + if (sectionNumber.size() < nextLevel) { + do { + sectionNumber.append("1"); + } while (sectionNumber.size() < nextLevel); + } + else { + while (sectionNumber.size() > nextLevel) { + sectionNumber.removeLast(); + } + sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); + } + int numAtoms; + Text headingText = Text::sectionHeading(atom); + QString s = headingText.toString(); + out() << "<li class=\"level" + << sectionNumber.size() + << "\">"; + out() << "<xref href=\"" + << "#" + << Doc::canonicalTitle(s) + << "\">"; + generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); + out() << "</xref></li>\n"; + } + while (!sectionNumber.isEmpty()) { + sectionNumber.removeLast(); + } + out() << "</ul>\n"; + out() << "</div>\n"; + inContents = false; + inLink = false; +} + +void DitaXmlGenerator::generateLowStatusMembers(const InnerNode* inner, + CodeMarker* marker, + CodeMarker::Status status) +{ + QString attribute; + if (status == CodeMarker::Compat) + attribute = "Qt3-support"; + else if (status == CodeMarker::Obsolete) + attribute = "obsolete"; + else + return; + + QList<Section> sections = marker->sections(inner, CodeMarker::Detailed, status); + QMutableListIterator<Section> j(sections); + while (j.hasNext()) { + if (j.next().members.size() == 0) + j.remove(); + } + if (sections.isEmpty()) + return; + + QList<Section>::ConstIterator s = sections.begin(); + while (s != sections.end()) { + if ((*s).name == "Member Function Documentation") { + writeFunctions((*s),inner,marker,attribute); + } + else if ((*s).name == "Member Type Documentation") { + writeEnumerations((*s),marker,attribute); + writeTypedefs((*s),marker,attribute); + } + else if ((*s).name == "Member Variable Documentation") { + writeDataMembers((*s),marker,attribute); + } + else if ((*s).name == "Property Documentation") { + writeProperties((*s),marker,attribute); + } + else if ((*s).name == "Macro Documentation") { + //writeMacros((*s),marker,attribute); + } + ++s; + } +} + +/*! + Write the XML for the class hierarchy to the current XML stream. + */ +void DitaXmlGenerator::generateClassHierarchy(const Node* relative, + CodeMarker* marker, + const QMap<QString,const Node*>& classMap) +{ + if (classMap.isEmpty()) + return; + + NodeMap topLevel; + NodeMap::ConstIterator c = classMap.begin(); + while (c != classMap.end()) { + const ClassNode* classe = static_cast<const ClassNode*>(*c); + if (classe->baseClasses().isEmpty()) + topLevel.insert(classe->name(), classe); + ++c; + } + + QStack<NodeMap > stack; + stack.push(topLevel); + + writeStartTag(DT_ul); + while (!stack.isEmpty()) { + if (stack.top().isEmpty()) { + stack.pop(); + writeEndTag(); // </ul> + if (!stack.isEmpty()) + writeEndTag(); // </li> + } + else { + const ClassNode *child = + static_cast<const ClassNode *>(*stack.top().begin()); + writeStartTag(DT_li); + generateFullName(child, relative, marker); + writeEndTag(); // </li> + stack.top().erase(stack.top().begin()); + + NodeMap newTop; + foreach (const RelatedClass &d, child->derivedClasses()) { + if (d.access != Node::Private && !d.node->doc().isEmpty()) + newTop.insert(d.node->name(), d.node); + } + if (!newTop.isEmpty()) { + stack.push(newTop); + writeStartTag(DT_li); + writeStartTag(DT_ul); + } + } + } +} + +/*! + Write XML for the contents of the \a nodeMap to the current + XML stream. + */ +void DitaXmlGenerator::generateAnnotatedList(const Node* relative, + CodeMarker* marker, + const NodeMap& nodeMap) +{ + if (nodeMap.isEmpty()) + return; + writeStartTag(DT_table); + xmlWriter().writeAttribute("outputclass","annotated"); + writeStartTag(DT_tgroup); + xmlWriter().writeAttribute("cols","2"); + writeStartTag(DT_tbody); + + foreach (const QString& name, nodeMap.keys()) { + const Node* node = nodeMap[name]; + + if (node->status() == Node::Obsolete) + continue; + + writeStartTag(DT_row); + writeStartTag(DT_entry); + writeStartTag(DT_p); + generateFullName(node, relative, marker); + writeEndTag(); // </p> + writeEndTag(); // <entry> + + if (!(node->type() == Node::Fake)) { + Text brief = node->doc().trimmedBriefText(name); + if (!brief.isEmpty()) { + writeStartTag(DT_entry); + writeStartTag(DT_p); + generateText(brief, node, marker); + writeEndTag(); // </p> + writeEndTag(); // <entry> + } + } + else { + writeStartTag(DT_entry); + writeStartTag(DT_p); + writeCharacters(protectEnc(node->doc().briefText().toString())); // zzz + writeEndTag(); // </p> + writeEndTag(); // <entry> + } + writeEndTag(); // </row> + } + writeEndTag(); // </tbody> + writeEndTag(); // </tgroup> + writeEndTag(); // </table> +} + +/*! + This function finds the common prefix of the names of all + the classes in \a classMap and then generates a compact + list of the class names alphabetized on the part of the + name not including the common prefix. You can tell the + function to use \a comonPrefix as the common prefix, but + normally you let it figure it out itself by looking at + the name of the first and last classes in \a classMap. + */ +void DitaXmlGenerator::generateCompactList(const Node* relative, + CodeMarker* marker, + const NodeMap& classMap, + bool includeAlphabet, + QString commonPrefix) +{ + const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' + + if (classMap.isEmpty()) + return; + + /* + If commonPrefix is not empty, then the caller knows what + the common prefix is and has passed it in, so just use that + one. But if the commonPrefix is empty (it normally is), then + compute a common prefix using this simple algorithm. Note we + assume the prefix length is 1, i.e. we will have a single + character as the common prefix. + */ + int commonPrefixLen = commonPrefix.length(); + if (commonPrefixLen == 0) { + QVector<int> count(26); + for (int i=0; i<26; ++i) + count[i] = 0; + + NodeMap::const_iterator iter = classMap.begin(); + while (iter != classMap.end()) { + if (!iter.key().contains("::")) { + QChar c = iter.key()[0]; + if ((c >= 'A') && (c <= 'Z')) { + int idx = c.unicode() - QChar('A').unicode(); + ++count[idx]; + } + } + ++iter; + } + int highest = 0; + int idx = -1; + for (int i=0; i<26; ++i) { + if (count[i] > highest) { + highest = count[i]; + idx = i; + } + } + idx += QChar('A').unicode(); + QChar common(idx); + commonPrefix = common; + commonPrefixLen = 1; + +#if 0 + /* + The algorithm below eventually failed, so it was replaced + with the simple (perhaps too simple) algorithm above. + + The caller didn't pass in a common prefix, so get the common + prefix by looking at the class names of the first and last + classes in the class map. Discard any namespace names and + just use the bare class names. For Qt, the prefix is "Q". + + Note that the algorithm used here to derive the common prefix + from the first and last classes in alphabetical order (QAccel + and QXtWidget in Qt 2.1), fails if either class name does not + begin with Q. + */ + QString first; + QString last; + NodeMap::const_iterator iter = classMap.begin(); + while (iter != classMap.end()) { + if (!iter.key().contains("::")) { + first = iter.key(); + break; + } + ++iter; + } + + if (first.isEmpty()) + first = classMap.begin().key(); + + iter = classMap.end(); + while (iter != classMap.begin()) { + --iter; + if (!iter.key().contains("::")) { + last = iter.key(); + break; + } + } + + if (last.isEmpty()) + last = classMap.begin().key(); + + if (classMap.size() > 1) { + while (commonPrefixLen < first.length() + 1 && + commonPrefixLen < last.length() + 1 && + first[commonPrefixLen] == last[commonPrefixLen]) + ++commonPrefixLen; + } + + commonPrefix = first.left(commonPrefixLen); +#endif + } + + /* + Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z, + underscore (_). QAccel will fall in paragraph 10 (A) and + QXtWidget in paragraph 33 (X). This is the only place where we + assume that NumParagraphs is 37. Each paragraph is a NodeMap. + */ + NodeMap paragraph[NumParagraphs+1]; + QString paragraphName[NumParagraphs+1]; + QSet<char> usedParagraphNames; + + NodeMap::ConstIterator c = classMap.begin(); + while (c != classMap.end()) { + QStringList pieces = c.key().split("::"); + QString key; + int idx = commonPrefixLen; + if (!pieces.last().startsWith(commonPrefix)) + idx = 0; + if (pieces.size() == 1) + key = pieces.last().mid(idx).toLower(); + else + key = pieces.last().toLower(); + + int paragraphNr = NumParagraphs - 1; + + if (key[0].digitValue() != -1) { + paragraphNr = key[0].digitValue(); + } + else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { + paragraphNr = 10 + key[0].unicode() - 'a'; + } + + paragraphName[paragraphNr] = key[0].toUpper(); + usedParagraphNames.insert(key[0].toLower().cell()); + paragraph[paragraphNr].insert(key, c.value()); + ++c; + } + + /* + Each paragraph j has a size: paragraph[j].count(). In the + discussion, we will assume paragraphs 0 to 5 will have sizes + 3, 1, 4, 1, 5, 9. + + We now want to compute the paragraph offset. Paragraphs 0 to 6 + start at offsets 0, 3, 4, 8, 9, 14, 23. + */ + int paragraphOffset[NumParagraphs + 1]; // 37 + 1 + paragraphOffset[0] = 0; + for (int i=0; i<NumParagraphs; i++) // i = 0..36 + paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count(); + + int curParNr = 0; + int curParOffset = 0; + QMap<QChar,QString> cmap; + + /* + Output the alphabet as a row of links. + */ + if (includeAlphabet) { + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","alphabet"); + for (int i = 0; i < 26; i++) { + QChar ch('a' + i); + if (usedParagraphNames.contains(char('a' + i))) { + writeStartTag(DT_xref); + // formathtml + QString guid = lookupGuid(outFileName(),QString(ch)); + QString attr = outFileName() + QString("#%1").arg(guid); + xmlWriter().writeAttribute("href", attr); + xmlWriter().writeCharacters(QString(ch.toUpper())); + writeEndTag(); // </xref> + } + } + writeEndTag(); // </p> + } + + /* + Output a <p> element to contain all the <dl> elements. + */ + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","compactlist"); + + for (int i=0; i<classMap.count()-1; i++) { + while ((curParNr < NumParagraphs) && + (curParOffset == paragraph[curParNr].count())) { + ++curParNr; + curParOffset = 0; + } + + /* + Starting a new paragraph means starting a new <dl>. + */ + if (curParOffset == 0) { + if (i > 0) { + writeEndTag(); // </dlentry> + writeEndTag(); // </dl> + } + writeStartTag(DT_dl); + writeStartTag(DT_dlentry); + writeStartTag(DT_dt); + if (includeAlphabet) { + QChar c = paragraphName[curParNr][0].toLower(); + writeGuidAttribute(QString(c)); + } + xmlWriter().writeAttribute("outputclass","sublist-header"); + xmlWriter().writeCharacters(paragraphName[curParNr]); + writeEndTag(); // </dt> + } + + /* + Output a <dd> for the current offset in the current paragraph. + */ + writeStartTag(DT_dd); + if ((curParNr < NumParagraphs) && + !paragraphName[curParNr].isEmpty()) { + NodeMap::Iterator it; + it = paragraph[curParNr].begin(); + for (int i=0; i<curParOffset; i++) + ++it; + + /* + Previously, we used generateFullName() for this, but we + require some special formatting. + */ + writeStartTag(DT_xref); + // formathtml + writeHrefAttribute(linkForNode(it.value(), relative)); + + QStringList pieces; + if (it.value()->subType() == Node::QmlClass) + pieces << it.value()->name(); + else + pieces = fullName(it.value(), relative, marker).split("::"); + xmlWriter().writeCharacters(protectEnc(pieces.last())); + writeEndTag(); // </xref> + if (pieces.size() > 1) { + xmlWriter().writeCharacters(" ("); + generateFullName(it.value()->parent(),relative,marker); + xmlWriter().writeCharacters(")"); + } + } + writeEndTag(); // </dd> + curParOffset++; + } + writeEndTag(); // </dlentry> + writeEndTag(); // </dl> + writeEndTag(); // </p> +} + +/*! + Write XML for a function index to the current XML stream. + */ +void DitaXmlGenerator::generateFunctionIndex(const Node* relative, + CodeMarker* marker) +{ + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","alphabet"); + for (int i = 0; i < 26; i++) { + QChar ch('a' + i); + writeStartTag(DT_xref); + // formathtml + QString guid = lookupGuid(outFileName(),QString(ch)); + QString attr = outFileName() + QString("#%1").arg(guid); + xmlWriter().writeAttribute("href", attr); + xmlWriter().writeCharacters(QString(ch.toUpper())); + writeEndTag(); // </xref> + + } + writeEndTag(); // </p> + + char nextLetter = 'a'; + char currentLetter; + + writeStartTag(DT_ul); + QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin(); + while (f != funcIndex.end()) { + writeStartTag(DT_li); + currentLetter = f.key()[0].unicode(); + while (islower(currentLetter) && currentLetter >= nextLetter) { + writeStartTag(DT_p); + writeGuidAttribute(QString(nextLetter)); + xmlWriter().writeAttribute("outputclass","target"); + xmlWriter().writeCharacters(QString(nextLetter)); + writeEndTag(); // </p> + nextLetter++; + } + xmlWriter().writeCharacters(protectEnc(f.key())); + xmlWriter().writeCharacters(":"); + + NodeMap::ConstIterator s = (*f).begin(); + while (s != (*f).end()) { + generateFullName((*s)->parent(), relative, marker, *s); + ++s; + } + writeEndTag(); // </li> + ++f; + } + writeEndTag(); // </ul> +} + +/*! + Write the legalese texts as XML to the current XML stream. + */ +void DitaXmlGenerator::generateLegaleseList(const Node* relative, + CodeMarker* marker) +{ + QMap<Text, const Node*>::ConstIterator it = legaleseTexts.begin(); + while (it != legaleseTexts.end()) { + Text text = it.key(); + generateText(text, relative, marker); + writeStartTag(DT_ul); + do { + writeStartTag(DT_li); + generateFullName(it.value(), relative, marker); + writeEndTag(); // </li> + ++it; + } while (it != legaleseTexts.end() && it.key() == text); + writeEndTag(); //</ul> + } +} + +/*! + Generate the text for the QML item described by \a node + and write it to the current XML stream. + */ +void DitaXmlGenerator::generateQmlItem(const Node* node, + const Node* relative, + CodeMarker* marker, + bool summary) +{ + QString marked = marker->markedUpQmlItem(node,summary); + QRegExp tag("(<[^@>]*>)"); + if (marked.indexOf(tag) != -1) { + QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length())); + marked.replace(tag.pos(1), tag.cap(1).length(), tmp); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); + + marked.replace("<@extra>", "<tt>"); + marked.replace("</@extra>", "</tt>"); + + if (summary) { + marked.remove("<@type>"); + marked.remove("</@type>"); + } + writeText(marked, marker, relative); +} + +/*! + Write the XML for the overview list to the current XML stream. + */ +void DitaXmlGenerator::generateOverviewList(const Node* relative, CodeMarker* /* marker */) +{ + QMap<const FakeNode*, QMap<QString, FakeNode*> > fakeNodeMap; + QMap<QString, const FakeNode*> groupTitlesMap; + QMap<QString, FakeNode*> uncategorizedNodeMap; + QRegExp singleDigit("\\b([0-9])\\b"); + + const NodeList children = tree_->root()->childNodes(); + foreach (Node* child, children) { + if (child->type() == Node::Fake && child != relative) { + FakeNode* fakeNode = static_cast<FakeNode*>(child); + + // Check whether the page is part of a group or is the group + // definition page. + QString group; + bool isGroupPage = false; + if (fakeNode->doc().metaCommandsUsed().contains("group")) { + group = fakeNode->doc().metaCommandArgs("group")[0]; + isGroupPage = true; + } + + // there are too many examples; they would clutter the list + if (fakeNode->subType() == Node::Example) + continue; + + // not interested either in individual (Qt Designer etc.) manual chapters + if (fakeNode->links().contains(Node::ContentsLink)) + continue; + + // Discard external nodes. + if (fakeNode->subType() == Node::ExternalPage) + continue; + + QString sortKey = fakeNode->fullTitle().toLower(); + if (sortKey.startsWith("the ")) + sortKey.remove(0, 4); + sortKey.replace(singleDigit, "0\\1"); + + if (!group.isEmpty()) { + if (isGroupPage) { + // If we encounter a group definition page, we add all + // the pages in that group to the list for that group. + foreach (Node* member, fakeNode->groupMembers()) { + if (member->type() != Node::Fake) + continue; + FakeNode* page = static_cast<FakeNode*>(member); + if (page) { + QString sortKey = page->fullTitle().toLower(); + if (sortKey.startsWith("the ")) + sortKey.remove(0, 4); + sortKey.replace(singleDigit, "0\\1"); + fakeNodeMap[const_cast<const FakeNode*>(fakeNode)].insert(sortKey, page); + groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode*>(fakeNode); + } + } + } + else if (!isGroupPage) { + // If we encounter a page that belongs to a group then + // we add that page to the list for that group. + const FakeNode* groupNode = + static_cast<const FakeNode*>(tree_->root()->findNode(group, Node::Fake)); + if (groupNode) + fakeNodeMap[groupNode].insert(sortKey, fakeNode); + //else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + }// else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + }// else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + } + } + + // We now list all the pages found that belong to groups. + // If only certain pages were found for a group, but the definition page + // for that group wasn't listed, the list of pages will be intentionally + // incomplete. However, if the group definition page was listed, all the + // pages in that group are listed for completeness. + + if (!fakeNodeMap.isEmpty()) { + foreach (const QString& groupTitle, groupTitlesMap.keys()) { + const FakeNode* groupNode = groupTitlesMap[groupTitle]; + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","h3"); + writeStartTag(DT_xref); + // formathtml + xmlWriter().writeAttribute("href",linkForNode(groupNode, relative)); + writeCharacters(protectEnc(groupNode->fullTitle())); + writeEndTag(); // </xref> + writeEndTag(); // </p> + if (fakeNodeMap[groupNode].count() == 0) + continue; + + writeStartTag(DT_ul); + foreach (const FakeNode* fakeNode, fakeNodeMap[groupNode]) { + QString title = fakeNode->fullTitle(); + if (title.startsWith("The ")) + title.remove(0, 4); + writeStartTag(DT_li); + writeStartTag(DT_xref); + // formathtml + xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative)); + writeCharacters(protectEnc(title)); + writeEndTag(); // </xref> + writeEndTag(); // </li> + } + writeEndTag(); // </ul> + } + } + + if (!uncategorizedNodeMap.isEmpty()) { + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","h3"); + xmlWriter().writeCharacters("Miscellaneous"); + writeEndTag(); // </p> + writeStartTag(DT_ul); + foreach (const FakeNode *fakeNode, uncategorizedNodeMap) { + QString title = fakeNode->fullTitle(); + if (title.startsWith("The ")) + title.remove(0, 4); + writeStartTag(DT_li); + writeStartTag(DT_xref); + // formathtml + xmlWriter().writeAttribute("href",linkForNode(fakeNode, relative)); + writeCharacters(protectEnc(title)); + writeEndTag(); // </xref> + writeEndTag(); // </li> + } + writeEndTag(); // </ul> + } +} + +/*! + Write the XML for a standard section of a page, e.g. + "Public Functions" or "Protected Slots." The section + is written too the current XML stream as a table. + */ +void DitaXmlGenerator::generateSection(const NodeList& nl, + const Node* relative, + CodeMarker* marker, + CodeMarker::SynopsisStyle style) +{ + if (!nl.isEmpty()) { + writeStartTag(DT_ul); + NodeList::ConstIterator m = nl.begin(); + while (m != nl.end()) { + if ((*m)->access() != Node::Private) { + writeStartTag(DT_li); + QString marked = getMarkedUpSynopsis(*m, relative, marker, style); + writeText(marked, marker, relative); + writeEndTag(); // </li> + } + ++m; + } + writeEndTag(); // </ul> + } +} + +/*! + Writes the "inherited from" list to the current XML stream. + */ +void DitaXmlGenerator::generateSectionInheritedList(const Section& section, + const Node* relative, + CodeMarker* marker) +{ + if (section.inherited.isEmpty()) + return; + writeStartTag(DT_ul); + QList<QPair<InnerNode*,int> >::ConstIterator p = section.inherited.begin(); + while (p != section.inherited.end()) { + writeStartTag(DT_li); + QString text; + text.setNum((*p).second); + text += QLatin1Char(' '); + if ((*p).second == 1) + text += section.singularMember; + else + text += section.pluralMember; + text += " inherited from "; + writeCharacters(text); + writeStartTag(DT_xref); + // formathtml + // zzz + text = fileName((*p).first) + QLatin1Char('#'); + text += DitaXmlGenerator::cleanRef(section.name.toLower()); + xmlWriter().writeAttribute("href",text); + text = protectEnc(marker->plainFullName((*p).first, relative)); + writeCharacters(text); + writeEndTag(); // </xref> + writeEndTag(); // </li> + ++p; + } + writeEndTag(); // </ul> +} + +/*! + Get the synopsis from the \a node using the \a relative + node if needed, and mark up the synopsis using \a marker. + Use the style to decide which kind of sysnopsis to build, + normally \c Summary or \c Detailed. Return the marked up + string. + */ +QString DitaXmlGenerator::getMarkedUpSynopsis(const Node* node, + const Node* relative, + CodeMarker* marker, + CodeMarker::SynopsisStyle style) +{ + QString marked = marker->markedUpSynopsis(node, relative, style); + QRegExp tag("(<[^@>]*>)"); + if (marked.indexOf(tag) != -1) { + QString tmp = protectEnc(marked.mid(tag.pos(1), tag.cap(1).length())); + marked.replace(tag.pos(1), tag.cap(1).length(), tmp); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i> \\1<sub>\\2</sub></i>"); +#if 0 + marked.replace("<@param>","<i>"); + marked.replace("</@param>","</i>"); +#endif + if (style == CodeMarker::Summary) { + marked.remove("<@name>"); // was "<b>" + marked.remove("</@name>"); // was "</b>" + } + + if (style == CodeMarker::Subpage) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.remove(extraRegExp); + } +#if 0 + else { + marked.replace("<@extra>","<tt>"); + marked.replace("</@extra>","</tt>"); + } +#endif + + if (style != CodeMarker::Detailed) { + marked.remove("<@type>"); + marked.remove("</@type>"); + } + return marked; +} + +/*! + Renamed from highlightedCode() in the html generator. Writes + the \a markedCode to the current XML stream. + */ +void DitaXmlGenerator::writeText(const QString& markedCode, + CodeMarker* marker, + const Node* relative) +{ + QString src = markedCode; + QString html; + QStringRef arg; + QStringRef par1; + + const QChar charLangle = '<'; + const QChar charAt = '@'; + + /* + First strip out all the extraneous markup. The table + below contains the markup we want to keep. Everything + else that begins with "<@" or "</@" is stripped out. + */ + static const QString spanTags[] = { + "<@link ", "<@link ", + "<@type>", "<@type>", + "<@headerfile>", "<@headerfile>", + "<@func>", "<@func>", + "<@func ", "<@func ", + "<@param>", "<@param>", + "<@extra>", "<@extra>", + "</@link>", "</@link>", + "</@type>", "</@type>", + "</@headerfile>", "</@headerfile>", + "</@func>", "</@func>", + "</@param>", "</@param>", + "</@extra>", "</@extra>" + }; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle) { + bool handled = false; + for (int k = 0; k != 13; ++k) { + const QString & tag = spanTags[2 * k]; + if (tag == QStringRef(&src, i, tag.length())) { + html += spanTags[2 * k + 1]; + i += tag.length(); + handled = true; + break; + } + } + if (!handled) { + ++i; + if (src.at(i) == charAt || + (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) { + // drop 'our' unknown tags (the ones still containing '@') + while (i < n && src.at(i) != QLatin1Char('>')) + ++i; + ++i; + } + else { + // retain all others + html += charLangle; + } + } + } + else { + html += src.at(i); + ++i; + } + } + + // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" + // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags + src = html; + html = QString(); + static const QString markTags[] = { + // 0 1 2 3 4 5 + "link", "type", "headerfile", "func", "param", "extra" + }; + + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + for (int k = 0; k != 6; ++k) { + if (parseArg(src, markTags[k], &i, n, &arg, &par1)) { + const Node* n = 0; + if (k == 0) { // <@link> + if (!html.isEmpty()) { + writeCharacters(html); + html.clear(); + } + n = CodeMarker::nodeForString(par1.toString()); + QString link = linkForNode(n, relative); + addLink(link, arg); + } + else if (k == 4) { // <@param> + if (!html.isEmpty()) { + writeCharacters(html); + html.clear(); + } + writeStartTag(DT_i); + writeCharacters(" " + arg.toString()); + writeEndTag(); // </i> + } + else if (k == 5) { // <@extra> + if (!html.isEmpty()) { + writeCharacters(html); + html.clear(); + } + writeStartTag(DT_tt); + writeCharacters(arg.toString()); + writeEndTag(); // </tt> + } + else { + if (!html.isEmpty()) { + writeCharacters(html); + html.clear(); + } + par1 = QStringRef(); + QString link; + n = marker->resolveTarget(arg.toString(), tree_, relative); + if (n && n->subType() == Node::QmlBasicType) { + if (relative && relative->subType() == Node::QmlClass) { + link = linkForNode(n,relative); + addLink(link, arg); + } + else { + writeCharacters(arg.toString()); + } + } + else { + // (zzz) Is this correct for all cases? + link = linkForNode(n,relative); + addLink(link, arg); + } + } + break; + } + } + } + else { + html += src.at(i++); + } + } + + if (!html.isEmpty()) { + writeCharacters(html); + } +} + +void DitaXmlGenerator::generateLink(const Atom* atom, + const Node* /* relative */, + CodeMarker* marker) +{ + static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); + + if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { + // hack for C++: move () outside of link + int k = funcLeftParen.pos(1); + writeCharacters(protectEnc(atom->string().left(k))); + if (link.isEmpty()) { + if (showBrokenLinks) + writeEndTag(); // </i> + } + else + writeEndTag(); // </xref> + inLink = false; + writeCharacters(protectEnc(atom->string().mid(k))); + } + else if (marker->recognizeLanguage("Java")) { + // hack for Java: remove () and use <tt> when appropriate + bool func = atom->string().endsWith("()"); + bool tt = (func || atom->string().contains(camelCase)); + if (tt) + writeStartTag(DT_tt); + if (func) + writeCharacters(protectEnc(atom->string().left(atom->string().length() - 2))); + else + writeCharacters(protectEnc(atom->string())); + writeEndTag(); // </tt> + } + else + writeCharacters(protectEnc(atom->string())); +} + +QString DitaXmlGenerator::cleanRef(const QString& ref) +{ + QString clean; + + if (ref.isEmpty()) + return clean; + + clean.reserve(ref.size() + 20); + const QChar c = ref[0]; + const uint u = c.unicode(); + + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) { + clean += c; + } + else if (u == '~') { + clean += "dtor."; + } + else if (u == '_') { + clean += "underscore."; + } + else { + clean += QLatin1Char('A'); + } + + for (int i = 1; i < (int) ref.length(); i++) { + const QChar c = ref[i]; + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9') || u == '-' || + u == '_' || u == ':' || u == '.') { + clean += c; + } + else if (c.isSpace()) { + clean += QLatin1Char('-'); + } + else if (u == '!') { + clean += "-not"; + } + else if (u == '&') { + clean += "-and"; + } + else if (u == '<') { + clean += "-lt"; + } + else if (u == '=') { + clean += "-eq"; + } + else if (u == '>') { + clean += "-gt"; + } + else if (u == '#') { + clean += QLatin1Char('#'); + } + else { + clean += QLatin1Char('-'); + clean += QString::number((int)u, 16); + } + } + return clean; +} + +QString DitaXmlGenerator::registerRef(const QString& ref) +{ + QString clean = DitaXmlGenerator::cleanRef(ref); + + for (;;) { + QString& prevRef = refMap[clean.toLower()]; + if (prevRef.isEmpty()) { + prevRef = ref; + break; + } + else if (prevRef == ref) + break; + clean += QLatin1Char('x'); + } + return clean; +} + +/*! + Calls protect() with the \a string. Returns the result. + */ +QString DitaXmlGenerator::protectEnc(const QString& string) +{ + return protect(string, outputEncoding); +} + +QString DitaXmlGenerator::protect(const QString& string, const QString& ) //outputEncoding) +{ +#define APPEND(x) \ + if (xml.isEmpty()) { \ + xml = string; \ + xml.truncate(i); \ +} \ + xml += (x); + + QString xml; + int n = string.length(); + + for (int i = 0; i < n; ++i) { + QChar ch = string.at(i); + + if (ch == QLatin1Char('&')) { + APPEND("&"); + } + else if (ch == QLatin1Char('<')) { + APPEND("<"); + } + else if (ch == QLatin1Char('>')) { + APPEND(">"); + } + else if (ch == QLatin1Char('"')) { + APPEND("""); + } +#if 0 + else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F) || + (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) || + (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) { + // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator + APPEND("&#x"); + xml += QString::number(ch.unicode(), 16); + xml += QLatin1Char(';'); + } +#endif + else { + if (!xml.isEmpty()) + xml += ch; + } + } + + if (!xml.isEmpty()) + return xml; + return string; + +#undef APPEND +} + +/*! + Constructs a file name appropriate for the \a node + and returns the file name. + */ +QString DitaXmlGenerator::fileBase(const Node* node) const +{ + QString result; + result = PageGenerator::fileBase(node); +#if 0 + if (!node->isInnerNode()) { + switch (node->status()) { + case Node::Compat: + result += "-qt3"; + break; + case Node::Obsolete: + result += "-obsolete"; + break; + default: + ; + } + } +#endif + return result; +} + +QString DitaXmlGenerator::guidForNode(const Node* node) +{ + switch (node->type()) { + case Node::Namespace: + case Node::Class: + default: + break; + case Node::Enum: + return node->guid(); + case Node::Typedef: + { + const TypedefNode* tdn = static_cast<const TypedefNode*>(node); + if (tdn->associatedEnum()) + return guidForNode(tdn->associatedEnum()); + } + return node->guid(); + case Node::Function: + { + const FunctionNode* fn = static_cast<const FunctionNode*>(node); + if (fn->associatedProperty()) { + return guidForNode(fn->associatedProperty()); + } + else { + QString ref = fn->name(); + if (fn->overloadNumber() != 1) { + ref += QLatin1Char('-') + QString::number(fn->overloadNumber()); + } + } + return fn->guid(); + } + case Node::Fake: + if (node->subType() != Node::QmlPropertyGroup) + break; + case Node::QmlProperty: + case Node::Property: + return node->guid(); + case Node::QmlSignal: + return node->guid(); + case Node::QmlSignalHandler: + return node->guid(); + case Node::QmlMethod: + return node->guid(); + case Node::Variable: + return node->guid(); + case Node::Target: + return node->guid(); + } + return QString(); +} + +/*! + Constructs a file name appropriate for the \a node and returns + it. If the \a node is not a fake node, or if it is a fake node but + it is neither an external page node nor an image node, call the + PageGenerator::fileName() function. + */ +QString DitaXmlGenerator::fileName(const Node* node) +{ + if (node->type() == Node::Fake) { + if (static_cast<const FakeNode*>(node)->subType() == Node::ExternalPage) + return node->name(); + if (static_cast<const FakeNode*>(node)->subType() == Node::Image) + return node->name(); + } + return PageGenerator::fileName(node); +} + +QString DitaXmlGenerator::linkForNode(const Node* node, const Node* relative) +{ + if (node == 0 || node == relative) + return QString(); + if (!node->url().isEmpty()) + return node->url(); + if (fileBase(node).isEmpty()) + return QString(); + if (node->access() == Node::Private) + return QString(); + + QString fn = fileName(node); + if (node && relative && node->parent() != relative) { + if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) { + if (node->parent()->isAbstract()) { + /* + This is a bit of a hack. What we discover with + the three 'if' statements immediately above, + is that node's parent is marked \qmlabstract + but the link appears in a qdoc comment for a + subclass of the node's parent. This means the + link should refer to the file for the relative + node, not the file for node. + */ + fn = fileName(relative); +#if DEBUG_ABSTRACT + qDebug() << "ABSTRACT:" << node->parent()->name() + << node->name() << relative->name() + << node->parent()->type() << node->parent()->subType() + << relative->type() << relative->subType() << outFileName(); +#endif + } + } + } + QString link = fn; + + if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) { + QString guid = guidForNode(node); + if (relative && fn == fileName(relative) && guid == guidForNode(relative)) { + return QString(); + } + link += QLatin1Char('#'); + link += guid; + } + /* + If the output is going to subdirectories, then if the + two nodes will be output to different directories, then + the link must go up to the parent directory and then + back down into the other subdirectory. + */ + if (node && relative && (node != relative)) { + if (node->outputSubdirectory() != relative->outputSubdirectory()) + link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + } + return link; +} + +QString DitaXmlGenerator::refForAtom(Atom* atom, const Node* /* node */) +{ + if (atom->type() == Atom::SectionLeft) + return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); + if (atom->type() == Atom::Target) + return Doc::canonicalTitle(atom->string()); + return QString(); +} + +void DitaXmlGenerator::generateFullName(const Node* apparentNode, + const Node* relative, + CodeMarker* marker, + const Node* actualNode) +{ + if (actualNode == 0) + actualNode = apparentNode; + writeStartTag(DT_xref); + // formathtml + QString href = linkForNode(actualNode, relative); + writeHrefAttribute(href); + writeCharacters(protectEnc(fullName(apparentNode, relative, marker))); + writeEndTag(); // </xref> +} + +void DitaXmlGenerator::findAllClasses(const InnerNode* node) +{ + NodeList::const_iterator c = node->childNodes().constBegin(); + while (c != node->childNodes().constEnd()) { + if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) { + if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) { + QString className = (*c)->name(); + if ((*c)->parent() && + (*c)->parent()->type() == Node::Namespace && + !(*c)->parent()->name().isEmpty()) + className = (*c)->parent()->name()+"::"+className; + + if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) { + if ((*c)->status() == Node::Compat) { + compatClasses.insert(className, *c); + } + else if ((*c)->status() == Node::Obsolete) { + obsoleteClasses.insert(className, *c); + } + else { + nonCompatClasses.insert(className, *c); + if ((*c)->status() == Node::Main) + mainClasses.insert(className, *c); + } + } + + QString moduleName = (*c)->moduleName(); + if (moduleName == "Qt3SupportLight") { + moduleClassMap[moduleName].insert((*c)->name(), *c); + moduleName = "Qt3Support"; + } + if (!moduleName.isEmpty()) + moduleClassMap[moduleName].insert((*c)->name(), *c); + + QString serviceName = + (static_cast<const ClassNode *>(*c))->serviceName(); + if (!serviceName.isEmpty()) + serviceClasses.insert(serviceName, *c); + } + else if ((*c)->type() == Node::Fake && + (*c)->subType() == Node::QmlClass && + !(*c)->doc().isEmpty()) { + QString qmlClassName = (*c)->name(); + qmlClasses.insert(qmlClassName,*c); + } + else if ((*c)->isInnerNode()) { + findAllClasses(static_cast<InnerNode *>(*c)); + } + } + ++c; + } +} + +void DitaXmlGenerator::findAllFunctions(const InnerNode* node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { + findAllFunctions(static_cast<const InnerNode*>(*c)); + } + else if ((*c)->type() == Node::Function) { + const FunctionNode* func = static_cast<const FunctionNode*>(*c); + if ((func->status() > Node::Obsolete) && + !func->isInternal() && + (func->metaness() != FunctionNode::Ctor) && + (func->metaness() != FunctionNode::Dtor)) { + funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c); + } + } + } + ++c; + } +} + +void DitaXmlGenerator::findAllLegaleseTexts(const InnerNode* node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if (!(*c)->doc().legaleseText().isEmpty()) + legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c); + if ((*c)->isInnerNode()) + findAllLegaleseTexts(static_cast<const InnerNode *>(*c)); + } + ++c; + } +} + +void DitaXmlGenerator::findAllNamespaces(const InnerNode* node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { + findAllNamespaces(static_cast<const InnerNode *>(*c)); + if ((*c)->type() == Node::Namespace) { + const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c); + // Ensure that the namespace's name is not empty (the root + // namespace has no name). + if (!nspace->name().isEmpty()) { + namespaceIndex.insert(nspace->name(), *c); + QString moduleName = (*c)->moduleName(); + if (moduleName == "Qt3SupportLight") { + moduleNamespaceMap[moduleName].insert((*c)->name(), *c); + moduleName = "Qt3Support"; + } + if (!moduleName.isEmpty()) + moduleNamespaceMap[moduleName].insert((*c)->name(), *c); + } + } + } + } + ++c; + } +} + +/*! + We're writing an attribute that indicates that the text + data is a heading, hence, h1, h2, h3... etc, and we must + decide which number to use. + */ +int DitaXmlGenerator::hOffset(const Node* node) +{ + switch (node->type()) { + case Node::Namespace: + case Node::Class: + return 2; + case Node::Fake: + return 1; + case Node::Enum: + case Node::Typedef: + case Node::Function: + case Node::Property: + default: + return 3; + } +} + +bool DitaXmlGenerator::isThreeColumnEnumValueTable(const Atom* atom) +{ + while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) { + if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight)) + return true; + atom = atom->next(); + } + return false; +} + +const Node* DitaXmlGenerator::findNodeForTarget(const QString& target, + const Node* relative, + CodeMarker* marker, + const Atom* atom) +{ + const Node* node = 0; + + if (target.isEmpty()) { + node = relative; + } + else if (target.endsWith(".html")) { + node = tree_->root()->findNode(target, Node::Fake); + } + else if (marker) { + node = marker->resolveTarget(target, tree_, relative); + if (!node) + node = tree_->findFakeNodeByTitle(target, relative); + if (!node && atom) { + node = tree_->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative); + } + } + + if (!node) + relative->doc().location().warning(tr("Cannot link to '%1'").arg(target)); + + return node; +} + +const QPair<QString,QString> DitaXmlGenerator::anchorForNode(const Node* node) +{ + QPair<QString,QString> anchorPair; + anchorPair.first = PageGenerator::fileName(node); + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode*>(node); + anchorPair.second = fakeNode->title(); + } + + return anchorPair; +} + +QString DitaXmlGenerator::getLink(const Atom* atom, + const Node* relative, + CodeMarker* marker, + const Node** node) +{ + QString link; + *node = 0; + inObsoleteLink = false; + + if (atom->string().contains(QLatin1Char(':')) && + (atom->string().startsWith("file:") + || atom->string().startsWith("http:") + || atom->string().startsWith("https:") + || atom->string().startsWith("ftp:") + || atom->string().startsWith("mailto:"))) { + + link = atom->string(); + } + else { + QStringList path; + if (atom->string().contains('#')) + path = atom->string().split('#'); + else + path.append(atom->string()); + + Atom* targetAtom = 0; + QString first = path.first().trimmed(); + + if (first.isEmpty()) { + *node = relative; + } + else if (first.endsWith(".html")) { + *node = tree_->root()->findNode(first, Node::Fake); + } + else { + *node = marker->resolveTarget(first, tree_, relative); + if (!*node) { + *node = tree_->findFakeNodeByTitle(first, relative); + } + if (!*node) { + *node = tree_->findUnambiguousTarget(first, targetAtom, relative); + } + } + + if (*node) { + if (!(*node)->url().isEmpty()) { + return (*node)->url(); + } + else { + path.removeFirst(); + } + } + else { + *node = relative; + } + + if (*node && (*node)->status() == Node::Obsolete) { + if (relative && (relative->parent() != *node) && + (relative->status() != Node::Obsolete)) { + bool porting = false; + if (relative->type() == Node::Fake) { + const FakeNode* fake = static_cast<const FakeNode*>(relative); + if (fake->title().startsWith("Porting")) + porting = true; + } + QString name = marker->plainFullName(relative); + if (!porting && !name.startsWith("Q3")) { + if (obsoleteLinks) { + relative->doc().location().warning(tr("Link to obsolete item '%1' in %2") + .arg(atom->string()) + .arg(name)); + } + inObsoleteLink = true; + } + } + } + + while (!path.isEmpty()) { + targetAtom = tree_->findTarget(path.first(), *node); + if (targetAtom == 0) + break; + path.removeFirst(); + } + + if (path.isEmpty()) { + link = linkForNode(*node, relative); + if (*node && (*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (targetAtom) { + if (link.isEmpty()) + link = outFileName(); + QString guid = lookupGuid(link,refForAtom(targetAtom,*node)); + link += QLatin1Char('#') + guid; + } + else if (!link.isEmpty() && *node && link.endsWith(".xml")) { + link += QLatin1Char('#') + (*node)->guid(); + } + } + /* + If the output is going to subdirectories, then if the + two nodes will be output to different directories, then + the link must go up to the parent directory and then + back down into the other subdirectory. + */ + if (link.startsWith("images/")) { + link.prepend(QString("../")); + } + else if (*node && relative && (*node != relative)) { + if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) { + link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/'))); + } + } + } + if (!link.isEmpty() && link[0] == '#') { + link.prepend(outFileName()); + qDebug() << "LOCAL LINK:" << link; + } + return link; +} + +/*! + This function can be called if getLink() returns an empty + string. + */ +QString DitaXmlGenerator::getDisambiguationLink(const Atom *, CodeMarker *) +{ + qDebug() << "Unimplemented function called: " + << "QString DitaXmlGenerator::getDisambiguationLink()"; + return QString(); +} + +void DitaXmlGenerator::generateIndex(const QString& fileBase, + const QString& url, + const QString& title) +{ + tree_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title); +} + +void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) +{ + Text text; + + switch (node->status()) { + case Node::Obsolete: + if (node->isInnerNode()) + Generator::generateStatus(node, marker); + break; + case Node::Compat: + if (node->isInnerNode()) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is part of the Qt 3 support library." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << " It is provided to keep old source code working. " + << "We strongly advise against " + << "using it in new code. See "; + + const FakeNode *fakeNode = tree_->findFakeNodeByTitle("Porting To Qt 4"); + Atom *targetAtom = 0; + if (fakeNode && node->type() == Node::Class) { + QString oldName(node->name()); + oldName.remove(QLatin1Char('3')); + targetAtom = tree_->findTarget(oldName,fakeNode); + } + + if (targetAtom) { + QString fn = fileName(fakeNode); + QString guid = lookupGuid(fn,refForAtom(targetAtom,fakeNode)); + text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid); + } + else + text << Atom(Atom::Link, "Porting to Qt 4"); + + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, "Porting to Qt 4") + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << " for more information." + << Atom::ParaRight; + } + generateText(text, node, marker); + break; + default: + Generator::generateStatus(node, marker); + } +} + +void DitaXmlGenerator::beginLink(const QString& link) +{ + this->link = link; + if (link.isEmpty()) + return; + writeStartTag(DT_xref); + // formathtml + writeHrefAttribute(link); + inLink = true; +} + +void DitaXmlGenerator::endLink() +{ + if (inLink) { + if (link.isEmpty()) { + if (showBrokenLinks) + writeEndTag(); // </i> + } + else { + if (inObsoleteLink) { + writeStartTag(DT_sup); + xmlWriter().writeCharacters("(obsolete)"); + writeEndTag(); // </sup> + } + writeEndTag(); // </xref> + } + } + inLink = false; + inObsoleteLink = false; +} + +/*! + Generates the summary for the \a section. Only used for + sections of QML element documentation. + + Currently handles only the QML property group. + */ +void DitaXmlGenerator::generateQmlSummary(const Section& section, + const Node* relative, + CodeMarker* marker) +{ + if (!section.members.isEmpty()) { + writeStartTag(DT_ul); + NodeList::ConstIterator m; + m = section.members.begin(); + while (m != section.members.end()) { + writeStartTag(DT_li); + generateQmlItem(*m,relative,marker,true); + writeEndTag(); // </li> + ++m; + } + writeEndTag(); // </ul> + } +} + +/*! + Outputs the DITA detailed documentation for a section + on a QML element reference page. + */ +void DitaXmlGenerator::generateDetailedQmlMember(const Node* node, + const InnerNode* relative, + CodeMarker* marker) +{ + QString marked; + const QmlPropertyNode* qpn = 0; + if (node->subType() == Node::QmlPropertyGroup) { + const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + writeStartTag(DT_ul); + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + qpn = static_cast<const QmlPropertyNode*>(*p); + writeStartTag(DT_li); + writeGuidAttribute((Node*)qpn); + QString attr; + int ro = qpn->getReadOnly(); + if (ro < 0) { + if (!qpn->isWritable(tree_)) + attr = "read-only"; + } + else if (ro > 0) + attr = "read-only"; + if (qpgn->isDefault()) { + if (!attr.isEmpty()) + attr += QLatin1Char(' '); + attr += "default"; + } + if (!attr.isEmpty()) + xmlWriter().writeAttribute("outputclass",attr); + generateQmlItem(qpn, relative, marker, false); + writeEndTag(); // </li> + } + ++p; + } + writeEndTag(); // </ul> + } + else if (node->type() == Node::QmlProperty) { + qpn = static_cast<const QmlPropertyNode*>(node); + /* + If the QML property node has a single subproperty, + override, replace qpn with that override node and + proceed as normal. + */ + if (qpn->qmlPropNodes().size() == 1) { + Node* n = qpn->qmlPropNodes().at(0); + if (n->type() == Node::QmlProperty) + qpn = static_cast<const QmlPropertyNode*>(n); + } + /* + Now qpn either has no overrides, or it has more + than 1. If it has none, proceed to output as nortmal. + */ + if (qpn->qmlPropNodes().isEmpty()) { + writeStartTag(DT_ul); + writeStartTag(DT_li); + writeGuidAttribute((Node*)qpn); + QString attr; + int ro = qpn->getReadOnly(); + if (ro < 0) { + const ClassNode* cn = qpn->declarativeCppNode(); + if (cn && !qpn->isWritable(tree_)) + attr = "read-only"; + } + else if (ro > 0) + attr = "read-only"; + if (qpn->isDefault()) { + if (!attr.isEmpty()) + attr += QLatin1Char(' '); + attr += "default"; + } + if (!attr.isEmpty()) + xmlWriter().writeAttribute("outputclass",attr); + generateQmlItem(qpn, relative, marker, false); + writeEndTag(); // </li> + writeEndTag(); // </ul> + } + else { + /* + The QML property node has multiple override nodes. + Process the whole list as we would for a QML property + group. + */ + NodeList::ConstIterator p = qpn->qmlPropNodes().begin(); + writeStartTag(DT_ul); + while (p != qpn->qmlPropNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p); + writeStartTag(DT_li); + writeGuidAttribute((Node*)q); + QString attr; + int ro = qpn->getReadOnly(); + if (ro < 0) { + if (!qpn->isWritable(tree_)) + attr = "read-only"; + } + else if (ro > 0) + attr = "read-only"; + if (qpn->isDefault()) { + if (!attr.isEmpty()) + attr += QLatin1Char(' '); + attr += "default"; + } + if (!attr.isEmpty()) + xmlWriter().writeAttribute("outputclass",attr); + generateQmlItem(q, relative, marker, false); + writeEndTag(); // </li> + } + ++p; + } + writeEndTag(); // </ul> + } + } + else if (node->type() == Node::QmlSignal) { + Node* n = const_cast<Node*>(node); + writeStartTag(DT_ul); + writeStartTag(DT_li); + writeGuidAttribute(n); + marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed); + writeText(marked, marker, relative); + writeEndTag(); // </li> + writeEndTag(); // </ul> + } + else if (node->type() == Node::QmlSignalHandler) { + Node* n = const_cast<Node*>(node); + writeStartTag(DT_ul); + writeStartTag(DT_li); + writeGuidAttribute(n); + marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed); + writeText(marked, marker, relative); + writeEndTag(); // </li> + writeEndTag(); // </ul> + } + else if (node->type() == Node::QmlMethod) { + Node* n = const_cast<Node*>(node); + writeStartTag(DT_ul); + writeStartTag(DT_li); + writeGuidAttribute(n); + marked = getMarkedUpSynopsis(n, relative, marker, CodeMarker::Detailed); + writeText(marked, marker, relative); + writeEndTag(); // </li> + writeEndTag(); // </ul> + } + generateStatus(node, marker); + generateBody(node, marker); + generateThreadSafeness(node, marker); + generateSince(node, marker); + generateAlsoList(node, marker); +} + +/*! + Output the "Inherits" line for the QML element, + if there should be one. + */ +void DitaXmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker) +{ + if (!qcn) + return; + const FakeNode* base = qcn->qmlBase(); + if (base) { + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","inherits"); + Text text; + text << "[Inherits "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, base->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, qcn, marker); + writeEndTag(); // </p> + } +} + +/*! + Output the "Inherit by" list for the QML element, + if it is inherited by any other elements. + */ +void DitaXmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, + CodeMarker* marker) +{ + if (qcn) { + NodeList subs; + QmlClassNode::subclasses(qcn->name(),subs); + if (!subs.isEmpty()) { + Text text; + text << Atom::ParaLeft << "Inherited by "; + appendSortedQmlNames(text,qcn,subs,marker); + text << Atom::ParaRight; + generateText(text, qcn, marker); + } + } +} + +/*! + Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" + line for the QML element, if there should be one. + + If there is no class node, or if the class node status + is set to Node::Internal, do nothing. + */ +void DitaXmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn, + CodeMarker* marker) +{ + const ClassNode* cn = qcn->classNode(); + if (cn && (cn->status() != Node::Internal)) { + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","instantiates"); + Text text; + text << "["; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, qcn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " instantiates the C++ class "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, qcn, marker); + writeEndTag(); // </p> + } +} + +/*! + Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]" + line for the class, if there should be one. + + If there is no QML element, or if the class node status + is set to Node::Internal, do nothing. + */ +void DitaXmlGenerator::generateInstantiatedBy(const ClassNode* cn, + CodeMarker* marker) +{ + if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) { + const QmlClassNode* qcn = cn->qmlElement(); + writeStartTag(DT_p); + xmlWriter().writeAttribute("outputclass","instantiated-by"); + Text text; + text << "["; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " is instantiated by QML element "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, qcn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << "]"; + generateText(text, cn, marker); + writeEndTag(); // </p> + } +} + +/*! + Return the full qualification of the node \a n, but without + the name of \a n itself. e.g. A::B::C + */ +QString DitaXmlGenerator::fullQualification(const Node* n) +{ + QString fq; + InnerNode* in = n->parent(); + while (in) { + if ((in->type() == Node::Class) || + (in->type() == Node::Namespace)) { + if (in->name().isEmpty()) + break; + if (fq.isEmpty()) + fq = in->name(); + else + fq = in->name() + "::" + fq; + } + else + break; + in = in->parent(); + } + return fq; +} + +/*! + Outputs the <cxxClassDerivations> element. + \code + <cxxClassDerivations> + <cxxClassDerivation> + ... + </cxxClassDerivation> + ... + </cxxClassDerivations> + \endcode + + The <cxxClassDerivation> element is: + + \code + <cxxClassDerivation> + <cxxClassDerivationAccessSpecifier value="public"/> + <cxxClassBaseClass href="class_base">Base</cxxClassBaseClass> + </cxxClassDerivation> + \endcode + */ +void DitaXmlGenerator::writeDerivations(const ClassNode* cn, CodeMarker* marker) +{ + QList<RelatedClass>::ConstIterator r; + + if (!cn->baseClasses().isEmpty()) { + writeStartTag(DT_cxxClassDerivations); + r = cn->baseClasses().begin(); + while (r != cn->baseClasses().end()) { + writeStartTag(DT_cxxClassDerivation); + writeStartTag(DT_cxxClassDerivationAccessSpecifier); + xmlWriter().writeAttribute("value",(*r).accessString()); + writeEndTag(); // </cxxClassDerivationAccessSpecifier> + + // not included: <cxxClassDerivationVirtual> + + writeStartTag(DT_cxxClassBaseClass); + QString attr = fileName((*r).node) + QLatin1Char('#') + (*r).node->guid(); + xmlWriter().writeAttribute("href",attr); + writeCharacters(marker->plainFullName((*r).node)); + writeEndTag(); // </cxxClassBaseClass> + + // not included: <ClassBaseStruct> or <cxxClassBaseUnion> + + writeEndTag(); // </cxxClassDerivation> + + // not included: <cxxStructDerivation> + + ++r; + } + writeEndTag(); // </cxxClassDerivations> + } +} + +/*! + Writes a <cxxXXXAPIItemLocation> element, depending on the + type of the node \a n, which can be a class, function, enum, + typedef, or property. + */ +void DitaXmlGenerator::writeLocation(const Node* n) +{ + DitaTag s1, s2, s3a, s3b; + s1 = DT_cxxClassAPIItemLocation; + s2 = DT_cxxClassDeclarationFile; + s3a = DT_cxxClassDeclarationFileLineStart; + s3b = DT_cxxClassDeclarationFileLineEnd; + if (n->type() == Node::Class || n->type() == Node::Namespace) { + s1 = DT_cxxClassAPIItemLocation; + s2 = DT_cxxClassDeclarationFile; + s3a = DT_cxxClassDeclarationFileLineStart; + s3b = DT_cxxClassDeclarationFileLineEnd; + } + else if (n->type() == Node::Function) { + FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(n)); + if (fn->isMacro()) { + s1 = DT_cxxDefineAPIItemLocation; + s2 = DT_cxxDefineDeclarationFile; + s3a = DT_cxxDefineDeclarationFileLine; + s3b = DT_NONE; + } + else { + s1 = DT_cxxFunctionAPIItemLocation; + s2 = DT_cxxFunctionDeclarationFile; + s3a = DT_cxxFunctionDeclarationFileLine; + s3b = DT_NONE; + } + } + else if (n->type() == Node::Enum) { + s1 = DT_cxxEnumerationAPIItemLocation; + s2 = DT_cxxEnumerationDeclarationFile; + s3a = DT_cxxEnumerationDeclarationFileLineStart; + s3b = DT_cxxEnumerationDeclarationFileLineEnd; + } + else if (n->type() == Node::Typedef) { + s1 = DT_cxxTypedefAPIItemLocation; + s2 = DT_cxxTypedefDeclarationFile; + s3a = DT_cxxTypedefDeclarationFileLine; + s3b = DT_NONE; + } + else if ((n->type() == Node::Property) || + (n->type() == Node::Variable)) { + s1 = DT_cxxVariableAPIItemLocation; + s2 = DT_cxxVariableDeclarationFile; + s3a = DT_cxxVariableDeclarationFileLine; + s3b = DT_NONE; + } + writeStartTag(s1); + writeStartTag(s2); + xmlWriter().writeAttribute("name","filePath"); + xmlWriter().writeAttribute("value",n->location().filePath()); + writeEndTag(); // <s2> + writeStartTag(s3a); + xmlWriter().writeAttribute("name","lineNumber"); + QString lineNr; + xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo())); + writeEndTag(); // </s3a> + if (s3b != DT_NONE) { + writeStartTag(s3b); + xmlWriter().writeAttribute("name","lineNumber"); + QString lineNr; + xmlWriter().writeAttribute("value",lineNr.setNum(n->location().lineNo())); + writeEndTag(); // </s3b> + } + writeEndTag(); // </cxx<s1>ApiItemLocation> +} + +/*! + Write the <cxxFunction> elements. + */ +void DitaXmlGenerator::writeFunctions(const Section& s, + const InnerNode* parent, + CodeMarker* marker, + const QString& attribute) +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Function) { + FunctionNode* fn = const_cast<FunctionNode*>(static_cast<const FunctionNode*>(*m)); + writeStartTag(DT_cxxFunction); + xmlWriter().writeAttribute("id",fn->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + if (fn->metaness() == FunctionNode::Signal) + xmlWriter().writeAttribute("outputclass","signal"); + else if (fn->metaness() == FunctionNode::Slot) + xmlWriter().writeAttribute("outputclass","slot"); + writeCharacters(fn->name()); + writeEndTag(); // </apiName> + generateBrief(fn,marker); + + // not included: <prolog> + + writeStartTag(DT_cxxFunctionDetail); + writeStartTag(DT_cxxFunctionDefinition); + writeStartTag(DT_cxxFunctionAccessSpecifier); + xmlWriter().writeAttribute("value",fn->accessString()); + writeEndTag(); // <cxxFunctionAccessSpecifier> + + // not included: <cxxFunctionStorageClassSpecifierExtern> + + if (fn->isStatic()) { + writeStartTag(DT_cxxFunctionStorageClassSpecifierStatic); + xmlWriter().writeAttribute("name","static"); + xmlWriter().writeAttribute("value","static"); + writeEndTag(); // <cxxFunctionStorageClassSpecifierStatic> + } + + // not included: <cxxFunctionStorageClassSpecifierMutable>, + + if (fn->isConst()) { + writeStartTag(DT_cxxFunctionConst); + xmlWriter().writeAttribute("name","const"); + xmlWriter().writeAttribute("value","const"); + writeEndTag(); // <cxxFunctionConst> + } + + // not included: <cxxFunctionExplicit> + // <cxxFunctionInline + + if (fn->virtualness() != FunctionNode::NonVirtual) { + writeStartTag(DT_cxxFunctionVirtual); + xmlWriter().writeAttribute("name","virtual"); + xmlWriter().writeAttribute("value","virtual"); + writeEndTag(); // <cxxFunctionVirtual> + if (fn->virtualness() == FunctionNode::PureVirtual) { + writeStartTag(DT_cxxFunctionPureVirtual); + xmlWriter().writeAttribute("name","pure virtual"); + xmlWriter().writeAttribute("value","pure virtual"); + writeEndTag(); // <cxxFunctionPureVirtual> + } + } + + if (fn->name() == parent->name()) { + writeStartTag(DT_cxxFunctionConstructor); + xmlWriter().writeAttribute("name","constructor"); + xmlWriter().writeAttribute("value","constructor"); + writeEndTag(); // <cxxFunctionConstructor> + } + else if (fn->name()[0] == QChar('~')) { + writeStartTag(DT_cxxFunctionDestructor); + xmlWriter().writeAttribute("name","destructor"); + xmlWriter().writeAttribute("value","destructor"); + writeEndTag(); // <cxxFunctionDestructor> + } + else { + writeStartTag(DT_cxxFunctionDeclaredType); + QString src = marker->typified(fn->returnType()); + replaceTypesWithLinks(fn,parent,marker,src); + writeEndTag(); // <cxxFunctionDeclaredType> + } + + // not included: <cxxFunctionReturnType> + + QString fq = fullQualification(fn); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxFunctionScopedName); + writeCharacters(fq); + writeEndTag(); // <cxxFunctionScopedName> + } + writeStartTag(DT_cxxFunctionPrototype); + writeCharacters(fn->signature(true)); + writeEndTag(); // <cxxFunctionPrototype> + + QString fnl = fn->signature(false); + int idx = fnl.indexOf(' '); + if (idx < 0) + idx = 0; + else + ++idx; + fnl = fn->parent()->name() + "::" + fnl.mid(idx); + writeStartTag(DT_cxxFunctionNameLookup); + writeCharacters(fnl); + writeEndTag(); // <cxxFunctionNameLookup> + + if (!fn->isInternal() && fn->isReimp() && fn->reimplementedFrom() != 0) { + FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom(); + if (rfn && !rfn->isInternal()) { + writeStartTag(DT_cxxFunctionReimplemented); + xmlWriter().writeAttribute("href",rfn->ditaXmlHref()); + writeCharacters(marker->plainFullName(rfn)); + writeEndTag(); // </cxxFunctionReimplemented> + } + } + writeParameters(fn,parent,marker); + writeLocation(fn); + writeEndTag(); // <cxxFunctionDefinition> + + writeApiDesc(fn, marker, QString()); + // generateAlsoList(inner, marker); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxFunctionDetail> + writeEndTag(); // </cxxFunction> + + if (fn->metaness() == FunctionNode::Ctor || + fn->metaness() == FunctionNode::Dtor || + fn->overloadNumber() != 1) { + } + } + ++m; + } +} + +static const QString typeTag("type"); +static const QChar charLangle = '<'; +static const QChar charAt = '@'; + +/*! + This function replaces class and enum names with <apiRelation> + elements, i.e. links. + */ +void DitaXmlGenerator::replaceTypesWithLinks(const Node* n, + const InnerNode* parent, + CodeMarker* marker, + QString& src) +{ + QStringRef arg; + QStringRef par1; + int srcSize = src.size(); + QString text; + for (int i=0; i<srcSize;) { + if (src.at(i) == charLangle && src.at(i+1) == charAt) { + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); + } + i += 2; + if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { + const Node* tn = marker->resolveTarget(arg.toString(), tree_, parent, n); + if (tn) { + //Do not generate a link from a C++ function to a QML Basic Type (such as int) + if (n->type() == Node::Function && tn->subType() == Node::QmlBasicType) + writeCharacters(arg.toString()); + else + addLink(linkForNode(tn,parent),arg,DT_apiRelation); + } + } + } + else { + text += src.at(i++); + } + } + if (!text.isEmpty()) { + writeCharacters(text); + text.clear(); + } +} + +/*! + This function writes the <cxxFunctionParameters> element. + */ +void DitaXmlGenerator::writeParameters(const FunctionNode* fn, + const InnerNode* parent, + CodeMarker* marker) +{ + const QList<Parameter>& parameters = fn->parameters(); + if (!parameters.isEmpty()) { + writeStartTag(DT_cxxFunctionParameters); + QList<Parameter>::ConstIterator p = parameters.begin(); + while (p != parameters.end()) { + writeStartTag(DT_cxxFunctionParameter); + writeStartTag(DT_cxxFunctionParameterDeclaredType); + QString src = marker->typified((*p).leftType()); + replaceTypesWithLinks(fn,parent,marker,src); + //writeCharacters((*p).leftType()); + if (!(*p).rightType().isEmpty()) + writeCharacters((*p).rightType()); + writeEndTag(); // <cxxFunctionParameterDeclaredType> + writeStartTag(DT_cxxFunctionParameterDeclarationName); + writeCharacters((*p).name()); + writeEndTag(); // <cxxFunctionParameterDeclarationName> + + // not included: <cxxFunctionParameterDefinitionName> + + if (!(*p).defaultValue().isEmpty()) { + writeStartTag(DT_cxxFunctionParameterDefaultValue); + writeCharacters((*p).defaultValue()); + writeEndTag(); // <cxxFunctionParameterDefaultValue> + } + + // not included: <apiDefNote> + + writeEndTag(); // <cxxFunctionParameter> + ++p; + } + writeEndTag(); // <cxxFunctionParameters> + } +} + +/*! + This function writes the enum types. + */ +void DitaXmlGenerator::writeEnumerations(const Section& s, + CodeMarker* marker, + const QString& attribute) +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Enum) { + const EnumNode* en = static_cast<const EnumNode*>(*m); + writeStartTag(DT_cxxEnumeration); + xmlWriter().writeAttribute("id",en->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + writeCharacters(en->name()); + writeEndTag(); // </apiName> + generateBrief(en,marker); + + // not included <prolog> + + writeStartTag(DT_cxxEnumerationDetail); + writeStartTag(DT_cxxEnumerationDefinition); + writeStartTag(DT_cxxEnumerationAccessSpecifier); + xmlWriter().writeAttribute("value",en->accessString()); + writeEndTag(); // <cxxEnumerationAccessSpecifier> + + QString fq = fullQualification(en); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxEnumerationScopedName); + writeCharacters(fq); + writeEndTag(); // <cxxEnumerationScopedName> + } + const QList<EnumItem>& items = en->items(); + if (!items.isEmpty()) { + writeStartTag(DT_cxxEnumerationPrototype); + writeCharacters(en->name()); + xmlWriter().writeCharacters(" = { "); + QList<EnumItem>::ConstIterator i = items.begin(); + while (i != items.end()) { + writeCharacters((*i).name()); + if (!(*i).value().isEmpty()) { + xmlWriter().writeCharacters(" = "); + writeCharacters((*i).value()); + } + ++i; + if (i != items.end()) + xmlWriter().writeCharacters(", "); + } + xmlWriter().writeCharacters(" }"); + writeEndTag(); // <cxxEnumerationPrototype> + } + + writeStartTag(DT_cxxEnumerationNameLookup); + writeCharacters(en->parent()->name() + "::" + en->name()); + writeEndTag(); // <cxxEnumerationNameLookup> + + // not included: <cxxEnumerationReimplemented> + + if (!items.isEmpty()) { + writeStartTag(DT_cxxEnumerators); + QList<EnumItem>::ConstIterator i = items.begin(); + while (i != items.end()) { + writeStartTag(DT_cxxEnumerator); + writeStartTag(DT_apiName); + writeCharacters((*i).name()); + writeEndTag(); // </apiName> + + QString fq = fullQualification(en->parent()); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxEnumeratorScopedName); + writeCharacters(fq + "::" + (*i).name()); + writeEndTag(); // <cxxEnumeratorScopedName> + } + writeStartTag(DT_cxxEnumeratorPrototype); + writeCharacters((*i).name()); + writeEndTag(); // <cxxEnumeratorPrototype> + writeStartTag(DT_cxxEnumeratorNameLookup); + writeCharacters(en->parent()->name() + "::" + (*i).name()); + writeEndTag(); // <cxxEnumeratorNameLookup> + + if (!(*i).value().isEmpty()) { + writeStartTag(DT_cxxEnumeratorInitialiser); + if ((*i).value().toInt(0,16) == 0) + xmlWriter().writeAttribute("value", "0"); + else + xmlWriter().writeAttribute("value", (*i).value()); + writeEndTag(); // <cxxEnumeratorInitialiser> + } + + // not included: <cxxEnumeratorAPIItemLocation> + + if (!(*i).text().isEmpty()) { + writeStartTag(DT_apiDesc); + generateText((*i).text(), en, marker); + writeEndTag(); // </apiDesc> + } + writeEndTag(); // <cxxEnumerator> + ++i; + } + writeEndTag(); // <cxxEnumerators> + } + + writeLocation(en); + writeEndTag(); // <cxxEnumerationDefinition> + + writeApiDesc(en, marker, QString()); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxEnumerationDetail> + + // not included: <related-links> + + writeEndTag(); // </cxxEnumeration> + } + ++m; + } +} + +/*! + This function writes the output for the \typedef commands. + */ +void DitaXmlGenerator::writeTypedefs(const Section& s, + CodeMarker* marker, + const QString& attribute) + +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Typedef) { + const TypedefNode* tn = static_cast<const TypedefNode*>(*m); + writeStartTag(DT_cxxTypedef); + xmlWriter().writeAttribute("id",tn->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + writeCharacters(tn->name()); + writeEndTag(); // </apiName> + generateBrief(tn,marker); + + // not included: <prolog> + + writeStartTag(DT_cxxTypedefDetail); + writeStartTag(DT_cxxTypedefDefinition); + writeStartTag(DT_cxxTypedefAccessSpecifier); + xmlWriter().writeAttribute("value",tn->accessString()); + writeEndTag(); // <cxxTypedefAccessSpecifier> + + // not included: <cxxTypedefDeclaredType> + + QString fq = fullQualification(tn); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxTypedefScopedName); + writeCharacters(fq); + writeEndTag(); // <cxxTypedefScopedName> + } + + // not included: <cxxTypedefPrototype> + + writeStartTag(DT_cxxTypedefNameLookup); + writeCharacters(tn->parent()->name() + "::" + tn->name()); + writeEndTag(); // <cxxTypedefNameLookup> + + // not included: <cxxTypedefReimplemented> + + writeLocation(tn); + writeEndTag(); // <cxxTypedefDefinition> + + writeApiDesc(tn, marker, QString()); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxTypedefDetail> + + // not included: <related-links> + + writeEndTag(); // </cxxTypedef> + } + ++m; + } +} + +/*! + This function writes the output for the \property commands. + This is the Q_PROPERTYs. + */ +void DitaXmlGenerator::writeProperties(const Section& s, + CodeMarker* marker, + const QString& attribute) +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Property) { + const PropertyNode* pn = static_cast<const PropertyNode*>(*m); + writeStartTag(DT_cxxVariable); + xmlWriter().writeAttribute("id",pn->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + writeCharacters(pn->name()); + writeEndTag(); // </apiName> + generateBrief(pn,marker); + + // not included: <prolog> + + writeStartTag(DT_cxxVariableDetail); + writeStartTag(DT_cxxVariableDefinition); + writeStartTag(DT_cxxVariableAccessSpecifier); + xmlWriter().writeAttribute("value",pn->accessString()); + writeEndTag(); // <cxxVariableAccessSpecifier> + + // not included: <cxxVariableStorageClassSpecifierExtern>, + // <cxxVariableStorageClassSpecifierStatic>, + // <cxxVariableStorageClassSpecifierMutable>, + // <cxxVariableConst>, <cxxVariableVolatile> + + if (!pn->qualifiedDataType().isEmpty()) { + writeStartTag(DT_cxxVariableDeclaredType); + writeCharacters(pn->qualifiedDataType()); + writeEndTag(); // <cxxVariableDeclaredType> + } + QString fq = fullQualification(pn); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxVariableScopedName); + writeCharacters(fq); + writeEndTag(); // <cxxVariableScopedName> + } + + writeStartTag(DT_cxxVariablePrototype); + xmlWriter().writeCharacters("Q_PROPERTY("); + writeCharacters(pn->qualifiedDataType()); + xmlWriter().writeCharacters(" "); + writeCharacters(pn->name()); + writePropertyParameter("READ",pn->getters()); + writePropertyParameter("WRITE",pn->setters()); + writePropertyParameter("RESET",pn->resetters()); + writePropertyParameter("NOTIFY",pn->notifiers()); + if (pn->isDesignable() != pn->designableDefault()) { + xmlWriter().writeCharacters(" DESIGNABLE "); + if (!pn->runtimeDesignabilityFunction().isEmpty()) + writeCharacters(pn->runtimeDesignabilityFunction()); + else + xmlWriter().writeCharacters(pn->isDesignable() ? "true" : "false"); + } + if (pn->isScriptable() != pn->scriptableDefault()) { + xmlWriter().writeCharacters(" SCRIPTABLE "); + if (!pn->runtimeScriptabilityFunction().isEmpty()) + writeCharacters(pn->runtimeScriptabilityFunction()); + else + xmlWriter().writeCharacters(pn->isScriptable() ? "true" : "false"); + } + if (pn->isWritable() != pn->writableDefault()) { + xmlWriter().writeCharacters(" STORED "); + xmlWriter().writeCharacters(pn->isStored() ? "true" : "false"); + } + if (pn->isUser() != pn->userDefault()) { + xmlWriter().writeCharacters(" USER "); + xmlWriter().writeCharacters(pn->isUser() ? "true" : "false"); + } + if (pn->isConstant()) + xmlWriter().writeCharacters(" CONSTANT"); + if (pn->isFinal()) + xmlWriter().writeCharacters(" FINAL"); + xmlWriter().writeCharacters(")"); + writeEndTag(); // <cxxVariablePrototype> + + writeStartTag(DT_cxxVariableNameLookup); + writeCharacters(pn->parent()->name() + "::" + pn->name()); + writeEndTag(); // <cxxVariableNameLookup> + + if (pn->overriddenFrom() != 0) { + PropertyNode* opn = (PropertyNode*)pn->overriddenFrom(); + writeStartTag(DT_cxxVariableReimplemented); + xmlWriter().writeAttribute("href",opn->ditaXmlHref()); + writeCharacters(marker->plainFullName(opn)); + writeEndTag(); // </cxxVariableReimplemented> + } + + writeLocation(pn); + writeEndTag(); // <cxxVariableDefinition> + + writeApiDesc(pn, marker, QString()); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxVariableDetail> + + // not included: <related-links> + + writeEndTag(); // </cxxVariable> + } + ++m; + } +} + +/*! + This function outputs the nodes resulting from \variable commands. + */ +void DitaXmlGenerator::writeDataMembers(const Section& s, + CodeMarker* marker, + const QString& attribute) +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Variable) { + const VariableNode* vn = static_cast<const VariableNode*>(*m); + writeStartTag(DT_cxxVariable); + xmlWriter().writeAttribute("id",vn->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + writeCharacters(vn->name()); + writeEndTag(); // </apiName> + generateBrief(vn,marker); + + // not included: <prolog> + + writeStartTag(DT_cxxVariableDetail); + writeStartTag(DT_cxxVariableDefinition); + writeStartTag(DT_cxxVariableAccessSpecifier); + xmlWriter().writeAttribute("value",vn->accessString()); + writeEndTag(); // <cxxVariableAccessSpecifier> + + // not included: <cxxVAriableStorageClassSpecifierExtern> + + if (vn->isStatic()) { + writeStartTag(DT_cxxVariableStorageClassSpecifierStatic); + xmlWriter().writeAttribute("name","static"); + xmlWriter().writeAttribute("value","static"); + writeEndTag(); // <cxxVariableStorageClassSpecifierStatic> + } + + // not included: <cxxVAriableStorageClassSpecifierMutable>, + // <cxxVariableConst>, <cxxVariableVolatile> + + writeStartTag(DT_cxxVariableDeclaredType); + writeCharacters(vn->leftType()); + if (!vn->rightType().isEmpty()) + writeCharacters(vn->rightType()); + writeEndTag(); // <cxxVariableDeclaredType> + + QString fq = fullQualification(vn); + if (!fq.isEmpty()) { + writeStartTag(DT_cxxVariableScopedName); + writeCharacters(fq); + writeEndTag(); // <cxxVariableScopedName> + } + + writeStartTag(DT_cxxVariablePrototype); + writeCharacters(vn->leftType() + QLatin1Char(' ')); + //writeCharacters(vn->parent()->name() + "::" + vn->name()); + writeCharacters(vn->name()); + if (!vn->rightType().isEmpty()) + writeCharacters(vn->rightType()); + writeEndTag(); // <cxxVariablePrototype> + + writeStartTag(DT_cxxVariableNameLookup); + writeCharacters(vn->parent()->name() + "::" + vn->name()); + writeEndTag(); // <cxxVariableNameLookup> + + // not included: <cxxVariableReimplemented> + + writeLocation(vn); + writeEndTag(); // <cxxVariableDefinition> + + writeApiDesc(vn, marker, QString()); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxVariableDetail> + + // not included: <related-links> + + writeEndTag(); // </cxxVariable> + } + ++m; + } +} + +/*! + This function writes a \macro as a <cxxDefine>. + */ +void DitaXmlGenerator::writeMacros(const Section& s, + CodeMarker* marker, + const QString& attribute) +{ + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Function) { + const FunctionNode* fn = static_cast<const FunctionNode*>(*m); + if (fn->isMacro()) { + writeStartTag(DT_cxxDefine); + xmlWriter().writeAttribute("id",fn->guid()); + if (!attribute.isEmpty()) + xmlWriter().writeAttribute("outputclass",attribute); + writeStartTag(DT_apiName); + writeCharacters(fn->name()); + writeEndTag(); // </apiName> + generateBrief(fn,marker); + + // not included: <prolog> + + writeStartTag(DT_cxxDefineDetail); + writeStartTag(DT_cxxDefineDefinition); + writeStartTag(DT_cxxDefineAccessSpecifier); + xmlWriter().writeAttribute("value",fn->accessString()); + writeEndTag(); // <cxxDefineAccessSpecifier> + + writeStartTag(DT_cxxDefinePrototype); + xmlWriter().writeCharacters("#define "); + writeCharacters(fn->name()); + if (fn->metaness() == FunctionNode::MacroWithParams) { + QStringList params = fn->parameterNames(); + if (!params.isEmpty()) { + xmlWriter().writeCharacters("("); + for (int i = 0; i < params.size(); ++i) { + if (params[i].isEmpty()) + xmlWriter().writeCharacters("..."); + else + writeCharacters(params[i]); + if ((i+1) < params.size()) + xmlWriter().writeCharacters(", "); + } + xmlWriter().writeCharacters(")"); + } + } + writeEndTag(); // <cxxDefinePrototype> + + writeStartTag(DT_cxxDefineNameLookup); + writeCharacters(fn->name()); + writeEndTag(); // <cxxDefineNameLookup> + + if (fn->reimplementedFrom() != 0) { + FunctionNode* rfn = (FunctionNode*)fn->reimplementedFrom(); + writeStartTag(DT_cxxDefineReimplemented); + xmlWriter().writeAttribute("href",rfn->ditaXmlHref()); + writeCharacters(marker->plainFullName(rfn)); + writeEndTag(); // </cxxDefineReimplemented> + } + + if (fn->metaness() == FunctionNode::MacroWithParams) { + QStringList params = fn->parameterNames(); + if (!params.isEmpty()) { + writeStartTag(DT_cxxDefineParameters); + for (int i = 0; i < params.size(); ++i) { + writeStartTag(DT_cxxDefineParameter); + writeStartTag(DT_cxxDefineParameterDeclarationName); + writeCharacters(params[i]); + writeEndTag(); // <cxxDefineParameterDeclarationName> + + // not included: <apiDefNote> + + writeEndTag(); // <cxxDefineParameter> + } + writeEndTag(); // <cxxDefineParameters> + } + } + + writeLocation(fn); + writeEndTag(); // <cxxDefineDefinition> + + writeApiDesc(fn, marker, QString()); + + // not included: <example> or <apiImpl> + + writeEndTag(); // </cxxDefineDetail> + + // not included: <related-links> + + writeEndTag(); // </cxxDefine> + } + } + ++m; + } +} + +/*! + This function writes one parameter of a Q_PROPERTY macro. + The property is identified by \a tag ("READ" "WRIE" etc), + and it is found in the 'a nlist. + */ +void DitaXmlGenerator::writePropertyParameter(const QString& tag, const NodeList& nlist) +{ + NodeList::const_iterator n = nlist.begin(); + while (n != nlist.end()) { + xmlWriter().writeCharacters(" "); + writeCharacters(tag); + xmlWriter().writeCharacters(" "); + writeCharacters((*n)->name()); + ++n; + } +} + +/*! + Calls beginSubPage() in the base class to open the file. + Then creates a new XML stream writer using the IO device + from opened file and pushes the XML writer onto a stackj. + Creates the file named \a fileName in the output directory. + Attaches a QTextStream to the created file, which is written + to all over the place using out(). Finally, it sets some + parameters in the XML writer and calls writeStartDocument(). + + It also ensures that a GUID map is created for the output file. + */ +void DitaXmlGenerator::beginSubPage(const InnerNode* node, + const QString& fileName) +{ + PageGenerator::beginSubPage(node,fileName); + (void) lookupGuidMap(fileName); + QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); + xmlWriterStack.push(writer); + writer->setAutoFormatting(true); + writer->setAutoFormattingIndent(4); + writer->writeStartDocument(); + clearSectionNesting(); +} + +/*! + Calls writeEndDocument() and then pops the XML stream writer + off the stack and deletes it. Then it calls endSubPage() in + the base class to close the device. + */ +void DitaXmlGenerator::endSubPage() +{ + if (inSection()) + qDebug() << "Missing </section> in" << outFileName() << sectionNestingLevel; + xmlWriter().writeEndDocument(); + delete xmlWriterStack.pop(); + PageGenerator::endSubPage(); +} + +/*! + Returns a reference to the XML stream writer currently in use. + There is one XML stream writer open for each XML file being + written, and they are kept on a stack. The one on top of the + stack is the one being written to at the moment. + */ +QXmlStreamWriter& DitaXmlGenerator::xmlWriter() +{ + return *xmlWriterStack.top(); +} + +/*! + Writes the \e {<apiDesc>} element for \a node to the current XML + stream using the code \a marker and the \a title. + */ +void DitaXmlGenerator::writeApiDesc(const Node* node, + CodeMarker* marker, + const QString& title) +{ + if (!node->doc().isEmpty()) { + inDetailedDescription = true; + enterApiDesc(QString(),title); + generateBody(node, marker); + generateAlsoList(node, marker); + leaveSection(); + } + inDetailedDescription = false; +} + +/*! + Write the nested class elements. + */ +void DitaXmlGenerator::writeNestedClasses(const Section& s, + const Node* n) +{ + if (s.members.isEmpty()) + return; + writeStartTag(DT_cxxClassNested); + writeStartTag(DT_cxxClassNestedDetail); + + NodeList::ConstIterator m = s.members.begin(); + while (m != s.members.end()) { + if ((*m)->type() == Node::Class) { + writeStartTag(DT_cxxClassNestedClass); + QString link = linkForNode((*m), n); + xmlWriter().writeAttribute("href", link); + QString name = n->name() + "::" + (*m)->name(); + writeCharacters(name); + writeEndTag(); // <cxxClassNestedClass> + } + ++m; + } + writeEndTag(); // <cxxClassNestedDetail> + writeEndTag(); // <cxxClassNested> +} + +/*! + Recursive writing of DITA XML files from the root \a node. + */ +void +DitaXmlGenerator::generateInnerNode(const InnerNode* node) +{ + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == Node::ExternalPage) + return; + if (fakeNode->subType() == Node::Image) + return; + if (fakeNode->subType() == Node::QmlPropertyGroup) + return; + if (fakeNode->subType() == Node::Page) { + if (node->count() > 0) + qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title())); + } + } + + /* + Obtain a code marker for the source file. + */ + CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); + + if (node->parent() != 0) { + beginSubPage(node, fileName(node)); + if (node->type() == Node::Namespace || node->type() == Node::Class) { + generateClassLikeNode(node, marker); + } + else if (node->type() == Node::Fake) { + if (node->subType() == Node::HeaderFile) + generateClassLikeNode(node, marker); + else if (node->subType() == Node::QmlClass) + generateClassLikeNode(node, marker); + else + generateFakeNode(static_cast<const FakeNode*>(node), marker); + } + endSubPage(); + } + + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->isInnerNode() && (*c)->access() != Node::Private) + generateInnerNode((const InnerNode*) *c); + ++c; + } +} + +/*! + Returns true if \a format is "XML" or "HTML" . + */ +bool DitaXmlGenerator::canHandleFormat(const QString& format) +{ + return (format == "HTML") || (format == this->format()); +} + +/*! + If the node multimap \a nmm contains nodes mapped to \a key, + if any of the nodes mapped to \a key has the same href as the + \a node, return true. Otherwise, return false. + */ +bool DitaXmlGenerator::isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node) +{ + QList<Node*> matches = nmm->values(key); + if (!matches.isEmpty()) { + for (int i=0; i<matches.size(); ++i) { + if (matches[i] == node) + return true; + if (fileName(node) == fileName(matches[i])) + return true; + } + } + return false; +} +/*! + Collect all the nodes in the tree according to their type or subtype. + + type: Class + type: Namespace + + subtype: Example + subtype: External page + subtype: Group + subtype: Header file + subtype: Module + subtype: Page + subtype: QML basic type + subtype: QML class + subtype: QML module + */ +void DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent) +{ + //qDebug() << "START"; + const NodeList& children = parent->childNodes(); + if (children.size() == 0) + return; + + bool related; + QString message; + for (int i=0; i<children.size(); ++i) { + Node* child = children[i]; + if (!child || child->isInternal() || child->doc().isEmpty()) + continue; + if (child->relates()) { + related = true; + message = child->relates()->name(); + } + else { + related = false; + message = "has documentation but no \\relates command"; + } + switch (child->type()) { + case Node::Namespace: + //qDebug() << "NODE: Namespace" << "TITLE:" << child->name() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeTypeMaps[Node::Namespace],child->name(),child)) + nodeTypeMaps[Node::Namespace]->insert(child->name(),child); + break; + case Node::Class: + //qDebug() << "NODE: Class" << "TITLE:" << child->name() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeTypeMaps[Node::Class],child->name(),child)) + nodeTypeMaps[Node::Class]->insert(child->name(),child); + break; + case Node::Fake: + //qDebug() << "NODE: Fake"; + switch (child->subType()) { + case Node::Example: + //qDebug() << "FAKE NODE: Example" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::Example],child->title(),child)) + nodeSubtypeMaps[Node::Example]->insert(child->title(),child); + break; + case Node::HeaderFile: + //qDebug() << "FAKE NODE: Header file" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::HeaderFile],child->title(),child)) + nodeSubtypeMaps[Node::HeaderFile]->insert(child->title(),child); + break; + case Node::File: + //qDebug() << "FAKE NODE: File"; + break; + case Node::Image: + //qDebug() << "FAKE NODE: Image"; + break; + case Node::Group: + //qDebug() << "FAKE NODE: Group" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::Group],child->title(),child)) + nodeSubtypeMaps[Node::Group]->insert(child->title(),child); + break; + case Node::Module: + //qDebug() << "FAKE NODE: Module" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::Module],child->title(),child)) + nodeSubtypeMaps[Node::Module]->insert(child->title(),child); + break; + case Node::Page: + //qDebug() << "FAKE NODE: Page" << "PAGE TYPE:" << child->pageTypeString() + // << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(pageTypeMaps[child->pageType()],child->title(),child)) + pageTypeMaps[child->pageType()]->insert(child->title(),child); + break; + case Node::ExternalPage: + //qDebug() << "FAKE NODE: External page" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child)) + nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child); + break; + case Node::QmlClass: + //qDebug() << "FAKE NODE: QML class" << "TITLE:" << child->title() << "FILE:" + // << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::QmlClass],child->title(),child)) + nodeSubtypeMaps[Node::QmlClass]->insert(child->title(),child); + break; + case Node::QmlPropertyGroup: + //qDebug() << "FAKE NODE: QML property group"; + break; + case Node::QmlBasicType: + //qDebug() << "FAKE NODE: QML basic type" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::QmlBasicType],child->title(),child)) + nodeSubtypeMaps[Node::QmlBasicType]->insert(child->title(),child); + break; + case Node::QmlModule: + //qDebug() << "FAKE NODE: QML module" << "TITLE:" << child->title() + // << "FILE:" << fileName(child); + if (!isDuplicate(nodeSubtypeMaps[Node::QmlModule],child->title(),child)) + nodeSubtypeMaps[Node::QmlModule]->insert(child->title(),child); + break; + case Node::Collision: + //qDebug() << "FAKE NODE: Collision"; + break; + default: + break; + } + break; + case Node::Enum: + if (!related) + child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Typedef: + if (!related) + child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Function: + if (!related) { + const FunctionNode* fn = static_cast<const FunctionNode*>(child); + if (fn->isMacro()) + child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message)); + else + child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message)); + } + break; + case Node::Property: + break; + case Node::Variable: + if (!related) + child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Target: + break; + case Node::QmlProperty: + if (!related) + child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message)); + break; + case Node::QmlSignal: + if (!related) + child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message)); + break; + case Node::QmlSignalHandler: + if (!related) + child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message)); + break; + case Node::QmlMethod: + if (!related) + child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message)); + break; + default: + break; + } + } +} + +/*! + Creates the DITA map for the qdoc run. The map is written + to the file \e{qt.ditamap" in the DITA XML output directory. + */ +void DitaXmlGenerator::writeDitaMap(const Tree *tree) +{ + beginSubPage(tree->root(),"qt.ditamap"); + + QString doctype; + doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; + // doctype = "<!DOCTYPE cxxAPIMap PUBLIC \"-//NOKIA//DTD DITA C++ API Map Reference Type v0.6.0//EN\" \"dtd/cxxAPIMap.dtd\">"; + + xmlWriter().writeDTD(doctype); + writeStartTag(DT_map); + //xmlWriter().writeAttribute("id","Qt-DITA-Map"); + //xmlWriter().writeAttribute("title","Qt DITA Map"); + writeStartTag(DT_topicmeta); + writeStartTag(DT_shortdesc); + xmlWriter().writeCharacters("The top level map for the Qt documentation"); + writeEndTag(); // </shortdesc> + writeEndTag(); // </topicmeta> + GuidMaps::iterator i = guidMaps.begin(); + while (i != guidMaps.end()) { + writeStartTag(DT_topicref); + if (i.key() != "qt.ditamap") + xmlWriter().writeAttribute("href",i.key()); + writeEndTag(); // </topicref> + ++i; + } + endSubPage(); + + for (unsigned i=0; i<Node::LastType; ++i) + nodeTypeMaps[i] = new NodeMultiMap; + for (unsigned i=0; i<Node::LastSubtype; ++i) + nodeSubtypeMaps[i] = new NodeMultiMap; + for (unsigned i=0; i<Node::OnBeyondZebra; ++i) + pageTypeMaps[i] = new NodeMultiMap; + collectNodesByTypeAndSubtype(tree->root()); +#if 0 + for (unsigned i=0; i<Node::LastType; ++i) { + if (nodeTypeMaps[i] && nodeTypeMaps[i]->size() > 0) + qDebug() << "NODE TYPE:" << Node::nodeTypeString(i) << nodeTypeMaps[i]->size(); + } + for (unsigned i=1; i<Node::LastSubtype; ++i) { + if (nodeSubtypeMaps[i] && nodeSubtypeMaps[i]->size() > 0) + qDebug() << "NODE SUBTYPE:" << Node::nodeSubtypeString(i) << nodeSubtypeMaps[i]->size(); + } + for (unsigned i=1; i<Node::OnBeyondZebra; ++i) { + if (pageTypeMaps[i] && pageTypeMaps[i]->size() > 0) + qDebug() << "PAGE TYPE:" << Node::pageTypeString(i) << pageTypeMaps[i]->size(); + } +#endif + beginSubPage(tree->root(),"test.ditamap"); + + doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; + xmlWriter().writeDTD(doctype); + writeStartTag(DT_map); + writeStartTag(DT_topicmeta); + writeStartTag(DT_shortdesc); + xmlWriter().writeCharacters("The top level map for the Qt documentation"); + writeEndTag(); // </shortdesc> + writeEndTag(); // </topicmeta> + + writeTopicrefs(pageTypeMaps[Node::OverviewPage], "overviews"); + writeTopicrefs(pageTypeMaps[Node::HowToPage], "howtos"); + writeTopicrefs(pageTypeMaps[Node::TutorialPage], "tutorials"); + writeTopicrefs(pageTypeMaps[Node::FAQPage], "faqs"); + writeTopicrefs(pageTypeMaps[Node::ArticlePage], "articles"); + writeTopicrefs(nodeSubtypeMaps[Node::Example], "examples"); + writeTopicrefs(nodeSubtypeMaps[Node::QmlClass], "QML classes"); + writeTopicrefs(nodeTypeMaps[Node::Class], "C++ classes"); + writeTopicrefs(nodeTypeMaps[Node::Namespace], "C++ namespaces"); + writeTopicrefs(nodeSubtypeMaps[Node::HeaderFile], "header files"); + writeTopicrefs(nodeSubtypeMaps[Node::Module], "modules"); + writeTopicrefs(nodeSubtypeMaps[Node::Group], "groups"); + writeTopicrefs(nodeSubtypeMaps[Node::QmlModule], "QML modules"); + writeTopicrefs(nodeSubtypeMaps[Node::QmlBasicType], "QML basic types"); + + endSubPage(); + + for (unsigned i=0; i<Node::LastType; ++i) + delete nodeTypeMaps[i]; + for (unsigned i=0; i<Node::LastSubtype; ++i) + delete nodeSubtypeMaps[i]; + for (unsigned i=0; i<Node::OnBeyondZebra; ++i) + delete pageTypeMaps[i]; +} + +/*! + Creates the DITA map from the topicrefs in \a node, + which is a DitaMapNode. + */ +void DitaXmlGenerator::writeDitaMap(const DitaMapNode* node) +{ + beginSubPage(node,node->name()); + + QString doctype; + doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; + // doctype = "<!DOCTYPE cxxAPIMap PUBLIC \"-//NOKIA//DTD DITA C++ API Map Reference Type v0.6.0//EN\" \"dtd/cxxAPIMap.dtd\">"; + + xmlWriter().writeDTD(doctype); + writeStartTag(DT_map); + writeStartTag(DT_topicmeta); + writeStartTag(DT_shortdesc); + xmlWriter().writeCharacters(node->title()); + writeEndTag(); // </shortdesc> + writeEndTag(); // </topicmeta> + const DitaRefList map = node->map(); + writeDitaRefs(map); + endSubPage(); +} + +/*! + Write the \a ditarefs to the current output file. + */ +void DitaXmlGenerator::writeDitaRefs(const DitaRefList& ditarefs) +{ + foreach (DitaRef* t, ditarefs) { + if (t->isMapRef()) + writeStartTag(DT_mapref); + else + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",t->navtitle()); + if (t->href().isEmpty()) { + const FakeNode* fn = tree_->findFakeNodeByTitle(t->navtitle()); + if (fn) + xmlWriter().writeAttribute("href",fileName(fn)); + } + else + xmlWriter().writeAttribute("href",t->href()); + if (t->subrefs() && !t->subrefs()->isEmpty()) + writeDitaRefs(*(t->subrefs())); + writeEndTag(); // </topicref> or </mapref> + } +} + +void DitaXmlGenerator::writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle) +{ + if (!nmm || nmm->isEmpty()) + return; + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",navtitle); + NodeMultiMap::iterator i = nmm->begin(); + while (i != nmm->end()) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",i.key()); + xmlWriter().writeAttribute("href",fileName(i.value())); + switch (i.value()->type()) { + case Node::Fake: { + const FakeNode* fn = static_cast<const FakeNode*>(i.value()); + switch (fn->subType()) { + case Node::Group: { + const NodeList& members = fn->groupMembers(); + for (int j=0; j<members.size(); ++j) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",members[j]->name()); + xmlWriter().writeAttribute("href",fileName(members[j])); + writeEndTag(); // </topicref> + } + break; + } + case Node::QmlModule: { + const NodeList& members = fn->qmlModuleMembers(); + for (int j=0; j<members.size(); ++j) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",members[j]->name()); + xmlWriter().writeAttribute("href",fileName(members[j])); + writeEndTag(); // </topicref> + } + break; + } + case Node::Example: { + const ExampleNode* en = static_cast<const ExampleNode*>(fn); + if (!en->imageFileName().isEmpty()) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle","image"); + xmlWriter().writeAttribute("href",en->imageFileName()); + writeEndTag(); // </topicref> + } + const NodeList& files = en->childNodes(); + for (int j=0; j<files.size(); ++j) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("href",files[j]->name()); + writeEndTag(); // </topicref> + } + break; + } + case Node::Module: { + if (moduleNamespaceMap.contains(fn->name())) { + const NodeMap& nodeMap = moduleNamespaceMap[fn->name()]; + foreach (const QString& name, nodeMap.keys()) { + const Node* node = nodeMap[name]; + if (node->status() == Node::Obsolete || + node->isInternal() || + node->access() == Node::Private || + node->doc().isEmpty()) + continue; + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",node->name()); + xmlWriter().writeAttribute("href",fileName(node)); + writeEndTag(); // </topicref> + } + } + if (moduleClassMap.contains(fn->name())) { + const NodeMap& nodeMap = moduleClassMap[fn->name()]; + foreach (const QString& name, nodeMap.keys()) { + const Node* node = nodeMap[name]; + if (node->status() == Node::Obsolete || + node->isInternal() || + node->access() == Node::Private || + node->doc().isEmpty()) + continue; + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",node->name()); + xmlWriter().writeAttribute("href",fileName(node)); + writeEndTag(); // </topicref> + } + } + break; + } + default: + break; + } + break; + } + case Node::Namespace: { + const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value()); + const NodeList& c = nn->childNodes(); + for (int j=0; j<c.size(); ++j) { + if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty()) + continue; + if (c[j]->type() == Node::Class) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",c[j]->name()); + xmlWriter().writeAttribute("href",fileName(c[j])); + writeEndTag(); // </topicref> + } + } + break; + } + case Node::Class: { + const NamespaceNode* nn = static_cast<const NamespaceNode*>(i.value()); + const NodeList& c = nn->childNodes(); + for (int j=0; j<c.size(); ++j) { + if (c[j]->isInternal() || c[j]->access() == Node::Private || c[j]->doc().isEmpty()) + continue; + if (c[j]->type() == Node::Class) { + writeStartTag(DT_topicref); + xmlWriter().writeAttribute("navtitle",c[j]->name()); + xmlWriter().writeAttribute("href",fileName(c[j])); + writeEndTag(); // </topicref> + } + } + break; + } + default: + break; + } + writeEndTag(); // </topicref> + ++i; + } + writeEndTag(); // </topicref> +} + + +/*! + Looks up the tag name for \a t in the map of metadata + values for the current topic in \a inner. If a value + for the tag is found, the element is written with the + found value. Otherwise if \a force is set, an empty + element is written using the tag. + + Returns true or false depending on whether it writes + an element using the tag \a t. + + \note If \a t is found in the metadata map, it is erased. + i.e. Once you call this function for a particular \a t, + you consume \a t. + */ +bool DitaXmlGenerator::writeMetadataElement(const InnerNode* inner, + DitaXmlGenerator::DitaTag t, + bool force) +{ + QString s = getMetadataElement(inner,t); + if (s.isEmpty() && !force) + return false; + writeStartTag(t); + if (!s.isEmpty()) + xmlWriter().writeCharacters(s); + writeEndTag(); + return true; +} + + +/*! + Looks up the tag name for \a t in the map of metadata + values for the current topic in \a inner. If one or more + value sfor the tag are found, the elements are written. + Otherwise nothing is written. + + Returns true or false depending on whether it writes + at least one element using the tag \a t. + + \note If \a t is found in the metadata map, it is erased. + i.e. Once you call this function for a particular \a t, + you consume \a t. + */ +bool DitaXmlGenerator::writeMetadataElements(const InnerNode* inner, + DitaXmlGenerator::DitaTag t) +{ + QStringList s = getMetadataElements(inner,t); + if (s.isEmpty()) + return false; + for (int i=0; i<s.size(); ++i) { + writeStartTag(t); + xmlWriter().writeCharacters(s[i]); + writeEndTag(); + } + return true; +} + +/*! + Looks up the tag name for \a t in the map of metadata + values for the current topic in \a inner. If a value + for the tag is found, the value is returned. + + \note If \a t is found in the metadata map, it is erased. + i.e. Once you call this function for a particular \a t, + you consume \a t. + */ +QString DitaXmlGenerator::getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t) +{ + QString s = Generator::getMetadataElement(inner, ditaTags[t]); + if (s.isEmpty()) + s = metadataDefault(t); + return s; +} + +/*! + Looks up the tag name for \a t in the map of metadata + values for the current topic in \a inner. If values + for the tag are found, they are returned in a string + list. + + \note If \a t is found in the metadata map, all the + pairs having the key \a t are erased. i.e. Once you + all this function for a particular \a t, you consume + \a t. + */ +QStringList DitaXmlGenerator::getMetadataElements(const InnerNode* inner, + DitaXmlGenerator::DitaTag t) +{ + QStringList s = Generator::getMetadataElements(inner,ditaTags[t]); + if (s.isEmpty()) + s.append(metadataDefault(t)); + return s; +} + +/*! + Returns the value of key \a t or an empty string + if \a t is not found in the map. + */ +QString DitaXmlGenerator::metadataDefault(DitaTag t) const +{ + return metadataDefaults.value(ditaTags[t]); +} + +/*! + Writes the <prolog> element for the \a inner node + using the \a marker. The <prolog> element contains + the <metadata> element, plus some others. This + function writes one or more of these elements: + + \list + \o <audience> * + \o <author> * + \o <brand> not used + \o <category> * + \o <compomnent> * + \o <copyrholder> * + \o <copyright> * + \o <created> not used + \o <copyryear> * + \o <critdates> not used + \o <keyword> not used + \o <keywords> not used + \o <metadata> * + \o <othermeta> * + \o <permissions> * + \o <platform> not used + \o <prodinfo> * + \o <prodname> * + \o <prolog> * + \o <publisher> * + \o <resourceid> not used + \o <revised> not used + \o <source> not used + \o <tm> not used + \o <unknown> not used + \o <vrm> * + \o <vrmlist> * + \endlist + + \node * means the tag has been used. + + */ +void +DitaXmlGenerator::writeProlog(const InnerNode* inner) +{ + if (!inner) + return; + writeStartTag(DT_prolog); + writeMetadataElements(inner,DT_author); + writeMetadataElement(inner,DT_publisher); + QString s = getMetadataElement(inner,DT_copyryear); + QString t = getMetadataElement(inner,DT_copyrholder); + writeStartTag(DT_copyright); + writeStartTag(DT_copyryear); + if (!s.isEmpty()) + xmlWriter().writeAttribute("year",s); + writeEndTag(); // </copyryear> + writeStartTag(DT_copyrholder); + if (!s.isEmpty()) + xmlWriter().writeCharacters(t); + writeEndTag(); // </copyrholder> + writeEndTag(); // </copyright> + s = getMetadataElement(inner,DT_permissions); + writeStartTag(DT_permissions); + xmlWriter().writeAttribute("view",s); + writeEndTag(); // </permissions> + writeStartTag(DT_metadata); + QStringList sl = getMetadataElements(inner,DT_audience); + if (!sl.isEmpty()) { + for (int i=0; i<sl.size(); ++i) { + writeStartTag(DT_audience); + xmlWriter().writeAttribute("type",sl[i]); + writeEndTag(); // </audience> + } + } + if (!writeMetadataElement(inner,DT_category,false)) { + writeStartTag(DT_category); + QString category = "Page"; + if (inner->type() == Node::Class) + category = "Class reference"; + else if (inner->type() == Node::Namespace) + category = "Namespace"; + else if (inner->type() == Node::Fake) { + if (inner->subType() == Node::QmlClass) + category = "QML Reference"; + else if (inner->subType() == Node::QmlBasicType) + category = "QML Basic Type"; + else if (inner->subType() == Node::HeaderFile) + category = "Header File"; + else if (inner->subType() == Node::Module) + category = "Module"; + else if (inner->subType() == Node::File) + category = "Example Source File"; + else if (inner->subType() == Node::Example) + category = "Example"; + else if (inner->subType() == Node::Image) + category = "Image"; + else if (inner->subType() == Node::Group) + category = "Group"; + else if (inner->subType() == Node::Page) + category = "Page"; + else if (inner->subType() == Node::ExternalPage) + category = "External Page"; // Is this necessary? + } + xmlWriter().writeCharacters(category); + writeEndTag(); // </category> + } + if (vrm.size() > 0) { + writeStartTag(DT_prodinfo); + if (!writeMetadataElement(inner,DT_prodname,false)) { + writeStartTag(DT_prodname); + xmlWriter().writeCharacters(projectDescription); + writeEndTag(); // </prodname> + } + writeStartTag(DT_vrmlist); + writeStartTag(DT_vrm); + if (vrm.size() > 0) + xmlWriter().writeAttribute("version",vrm[0]); + if (vrm.size() > 1) + xmlWriter().writeAttribute("release",vrm[1]); + if (vrm.size() > 2) + xmlWriter().writeAttribute("modification",vrm[2]); + writeEndTag(); // <vrm> + writeEndTag(); // <vrmlist> + if (!writeMetadataElement(inner,DT_component,false)) { + QString component = inner->moduleName(); + if (!component.isEmpty()) { + writeStartTag(DT_component); + xmlWriter().writeCharacters(component); + writeEndTag(); // </component> + } + } + writeEndTag(); // </prodinfo> + } + const QStringMultiMap& metaTagMap = inner->doc().metaTagMap(); + QMapIterator<QString, QString> i(metaTagMap); + while (i.hasNext()) { + i.next(); + writeStartTag(DT_othermeta); + xmlWriter().writeAttribute("name",i.key()); + xmlWriter().writeAttribute("content",i.value()); + writeEndTag(); // </othermeta> + } + writeEndTag(); // </metadata> + writeEndTag(); // </prolog> +} + +/*! + This function should be called to write the \a href attribute + if the href could be an \e http or \e ftp link. If \a href is + one or the other, a \e scope attribute is also writen, with + value \e external. + */ +void DitaXmlGenerator::writeHrefAttribute(const QString& href) +{ + xmlWriter().writeAttribute("href", href); + if (href.startsWith("http:") || href.startsWith("ftp:") || + href.startsWith("https:") || href.startsWith("mailto:")) + xmlWriter().writeAttribute("scope", "external"); +} + +/*! + Strips the markup tags from \a src, when we are trying to + create an \e{id} attribute. Returns the stripped text. + */ +QString DitaXmlGenerator::stripMarkup(const QString& src) const +{ + QString text; + const QChar charAt = '@'; + const QChar charSlash = '/'; + const QChar charLangle = '<'; + const QChar charRangle = '>'; + + int n = src.size(); + int i = 0; + while (i < n) { + if (src.at(i) == charLangle) { + ++i; + if (src.at(i) == charAt || (src.at(i) == charSlash && src.at(i+1) == charAt)) { + while (i < n && src.at(i) != charRangle) + ++i; + ++i; + } + else { + text += charLangle; + } + } + else + text += src.at(i++); + } + return text; +} + + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h new file mode 100644 index 0000000000..ba96265b48 --- /dev/null +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef DITAXMLGENERATOR_H +#define DITAXMLGENERATOR_H + +#include <qmap.h> +#include <qregexp.h> +#include <QXmlStreamWriter> +#include "codemarker.h" +#include "config.h" +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +typedef QMap<QString, QString> GuidMap; +typedef QMap<QString, GuidMap*> GuidMaps; + +class DitaXmlGenerator : public PageGenerator +{ +public: + enum SinceType { + Namespace, + Class, + MemberFunction, + NamespaceFunction, + GlobalFunction, + Macro, + Enum, + Typedef, + Property, + Variable, + QmlClass, + QmlProperty, + QmlSignal, + QmlSignalHandler, + QmlMethod, + LastSinceType + }; + + enum DitaTag { + DT_NONE, + DT_alt, + DT_apiDesc, + DT_APIMap, + DT_apiName, + DT_apiRelation, + DT_audience, + DT_author, + DT_b, + DT_body, + DT_bodydiv, + DT_brand, + DT_category, + DT_codeblock, + DT_comment, + DT_component, + DT_copyrholder, + DT_copyright, + DT_copyryear, + DT_created, + DT_critdates, + DT_cxxAPIMap, + DT_cxxClass, + DT_cxxClassAbstract, + DT_cxxClassAccessSpecifier, + DT_cxxClassAPIItemLocation, + DT_cxxClassBaseClass, + DT_cxxClassDeclarationFile, + DT_cxxClassDeclarationFileLine, + DT_cxxClassDeclarationFileLineStart, + DT_cxxClassDeclarationFileLineEnd, + DT_cxxClassDefinition, + DT_cxxClassDerivation, + DT_cxxClassDerivationAccessSpecifier, + DT_cxxClassDerivations, + DT_cxxClassDetail, + DT_cxxClassNested, + DT_cxxClassNestedClass, + DT_cxxClassNestedDetail, + DT_cxxDefine, + DT_cxxDefineAccessSpecifier, + DT_cxxDefineAPIItemLocation, + DT_cxxDefineDeclarationFile, + DT_cxxDefineDeclarationFileLine, + DT_cxxDefineDefinition, + DT_cxxDefineDetail, + DT_cxxDefineNameLookup, + DT_cxxDefineParameter, + DT_cxxDefineParameterDeclarationName, + DT_cxxDefineParameters, + DT_cxxDefinePrototype, + DT_cxxDefineReimplemented, + DT_cxxEnumeration, + DT_cxxEnumerationAccessSpecifier, + DT_cxxEnumerationAPIItemLocation, + DT_cxxEnumerationDeclarationFile, + DT_cxxEnumerationDeclarationFileLine, + DT_cxxEnumerationDeclarationFileLineStart, + DT_cxxEnumerationDeclarationFileLineEnd, + DT_cxxEnumerationDefinition, + DT_cxxEnumerationDetail, + DT_cxxEnumerationNameLookup, + DT_cxxEnumerationPrototype, + DT_cxxEnumerationScopedName, + DT_cxxEnumerator, + DT_cxxEnumeratorInitialiser, + DT_cxxEnumeratorNameLookup, + DT_cxxEnumeratorPrototype, + DT_cxxEnumerators, + DT_cxxEnumeratorScopedName, + DT_cxxFunction, + DT_cxxFunctionAccessSpecifier, + DT_cxxFunctionAPIItemLocation, + DT_cxxFunctionConst, + DT_cxxFunctionConstructor, + DT_cxxFunctionDeclarationFile, + DT_cxxFunctionDeclarationFileLine, + DT_cxxFunctionDeclaredType, + DT_cxxFunctionDefinition, + DT_cxxFunctionDestructor, + DT_cxxFunctionDetail, + DT_cxxFunctionNameLookup, + DT_cxxFunctionParameter, + DT_cxxFunctionParameterDeclarationName, + DT_cxxFunctionParameterDeclaredType, + DT_cxxFunctionParameterDefaultValue, + DT_cxxFunctionParameters, + DT_cxxFunctionPrototype, + DT_cxxFunctionPureVirtual, + DT_cxxFunctionReimplemented, + DT_cxxFunctionScopedName, + DT_cxxFunctionStorageClassSpecifierStatic, + DT_cxxFunctionVirtual, + DT_cxxTypedef, + DT_cxxTypedefAccessSpecifier, + DT_cxxTypedefAPIItemLocation, + DT_cxxTypedefDeclarationFile, + DT_cxxTypedefDeclarationFileLine, + DT_cxxTypedefDefinition, + DT_cxxTypedefDetail, + DT_cxxTypedefNameLookup, + DT_cxxTypedefScopedName, + DT_cxxVariable, + DT_cxxVariableAccessSpecifier, + DT_cxxVariableAPIItemLocation, + DT_cxxVariableDeclarationFile, + DT_cxxVariableDeclarationFileLine, + DT_cxxVariableDeclaredType, + DT_cxxVariableDefinition, + DT_cxxVariableDetail, + DT_cxxVariableNameLookup, + DT_cxxVariablePrototype, + DT_cxxVariableReimplemented, + DT_cxxVariableScopedName, + DT_cxxVariableStorageClassSpecifierStatic, + DT_data, + DT_dataabout, + DT_dd, + DT_dl, + DT_dlentry, + DT_dt, + DT_entry, + DT_fig, + DT_i, + DT_image, + DT_keyword, + DT_keywords, + DT_li, + DT_link, + DT_linktext, + DT_lq, + DT_map, + DT_mapref, + DT_metadata, + DT_note, + DT_ol, + DT_othermeta, + DT_p, + DT_parameter, + DT_permissions, + DT_ph, + DT_platform, + DT_pre, + DT_prodinfo, + DT_prodname, + DT_prolog, + DT_publisher, + DT_relatedLinks, + DT_resourceid, + DT_revised, + DT_row, + DT_section, + DT_sectiondiv, + DT_shortdesc, + DT_simpletable, + DT_source, + DT_stentry, + DT_sthead, + DT_strow, + DT_sub, + DT_sup, + DT_table, + DT_tbody, + DT_tgroup, + DT_thead, + DT_title, + DT_tm, + DT_topic, + DT_topicmeta, + DT_topicref, + DT_tt, + DT_u, + DT_ul, + DT_unknown, + DT_vrm, + DT_vrmlist, + DT_xref, + DT_LAST + }; + +public: + DitaXmlGenerator(); + ~DitaXmlGenerator(); + + virtual void initializeGenerator(const Config& config); + virtual void terminateGenerator(); + virtual QString format(); + virtual bool canHandleFormat(const QString& format); + virtual void generateTree(const Tree *tree); + virtual void generateDisambiguationPages() { } + + QString protectEnc(const QString& string); + static QString protect(const QString& string, const QString& encoding = "ISO-8859-1"); + static QString cleanRef(const QString& ref); + static QString sinceTitle(int i) { return sinceTitles[i]; } + +protected: + virtual void startText(const Node* relative, CodeMarker* marker); + virtual int generateAtom(const Atom* atom, + const Node* relative, + CodeMarker* marker); + virtual void generateClassLikeNode(const InnerNode* inner, CodeMarker* marker); + virtual void generateFakeNode(const FakeNode* fake, CodeMarker* marker); + virtual QString fileExtension(const Node* node) const; + virtual QString guidForNode(const Node* node); + virtual QString linkForNode(const Node* node, const Node* relative); + virtual QString refForAtom(Atom* atom, const Node* node); + + void writeXrefListItem(const QString& link, const QString& text); + QString fullQualification(const Node* n); + + void writeCharacters(const QString& text); + void writeDerivations(const ClassNode* cn, CodeMarker* marker); + void writeLocation(const Node* n); + void writeFunctions(const Section& s, + const InnerNode* parent, + CodeMarker* marker, + const QString& attribute = QString()); + void writeNestedClasses(const Section& s, const Node* n); + void replaceTypesWithLinks(const Node* n, + const InnerNode* parent, + CodeMarker* marker, + QString& src); + void writeParameters(const FunctionNode* fn, const InnerNode* parent, CodeMarker* marker); + void writeEnumerations(const Section& s, + CodeMarker* marker, + const QString& attribute = QString()); + void writeTypedefs(const Section& s, + CodeMarker* marker, + const QString& attribute = QString()); + void writeDataMembers(const Section& s, + CodeMarker* marker, + const QString& attribute = QString()); + void writeProperties(const Section& s, + CodeMarker* marker, + const QString& attribute = QString()); + void writeMacros(const Section& s, + CodeMarker* marker, + const QString& attribute = QString()); + void writePropertyParameter(const QString& tag, const NodeList& nlist); + void writeRelatedLinks(const FakeNode* fake, CodeMarker* marker); + void writeLink(const Node* node, const QString& tex, const QString& role); + void writeProlog(const InnerNode* inner); + bool writeMetadataElement(const InnerNode* inner, + DitaXmlGenerator::DitaTag t, + bool force=true); + bool writeMetadataElements(const InnerNode* inner, DitaXmlGenerator::DitaTag t); + void writeHrefAttribute(const QString& href); + QString getMetadataElement(const InnerNode* inner, DitaXmlGenerator::DitaTag t); + QStringList getMetadataElements(const InnerNode* inner, DitaXmlGenerator::DitaTag t); + +private: + enum SubTitleSize { SmallSubTitle, LargeSubTitle }; + + const QPair<QString,QString> anchorForNode(const Node* node); + const Node* findNodeForTarget(const QString& target, + const Node* relative, + CodeMarker* marker, + const Atom* atom = 0); + void generateHeader(const Node* node, + const QString& name, + bool subpage = false); + void generateBrief(const Node* node, CodeMarker* marker); + void generateIncludes(const InnerNode* inner, CodeMarker* marker); + void generateTableOfContents(const Node* node, + CodeMarker* marker, + Doc::Sections sectioningUnit, + int numColumns, + const Node* relative = 0); + void generateTableOfContents(const Node* node, + CodeMarker* marker, + QList<Section>* sections = 0); + void generateLowStatusMembers(const InnerNode* inner, + CodeMarker* marker, + CodeMarker::Status status); + QString generateLowStatusMemberFile(const InnerNode* inner, + CodeMarker* marker, + CodeMarker::Status status); + void generateClassHierarchy(const Node* relative, + CodeMarker* marker, + const NodeMap& classMap); + void generateAnnotatedList(const Node* relative, + CodeMarker* marker, + const NodeMap& nodeMap); + void generateCompactList(const Node* relative, + CodeMarker* marker, + const NodeMap& classMap, + bool includeAlphabet, + QString commonPrefix = QString()); + void generateFunctionIndex(const Node* relative, CodeMarker* marker); + void generateLegaleseList(const Node* relative, CodeMarker* marker); + void generateOverviewList(const Node* relative, CodeMarker* marker); + + void generateQmlSummary(const Section& section, + const Node* relative, + CodeMarker* marker); + void generateQmlItem(const Node* node, + const Node* relative, + CodeMarker* marker, + bool summary); + void generateDetailedQmlMember(const Node* node, + const InnerNode* relative, + CodeMarker* marker); + void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); + void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); + void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); + void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); + + void generateSection(const NodeList& nl, + const Node* relative, + CodeMarker* marker, + CodeMarker::SynopsisStyle style); + QString getMarkedUpSynopsis(const Node* node, + const Node* relative, + CodeMarker* marker, + CodeMarker::SynopsisStyle style); + void generateSectionInheritedList(const Section& section, + const Node* relative, + CodeMarker* marker); + void writeText(const QString& markedCode, + CodeMarker* marker, + const Node* relative); + + void generateFullName(const Node* apparentNode, + const Node* relative, + CodeMarker* marker, + const Node* actualNode = 0); + void generateLink(const Atom* atom, + const Node* relative, + CodeMarker* marker); + void generateStatus(const Node* node, CodeMarker* marker); + + QString registerRef(const QString& ref); + virtual QString fileBase(const Node *node) const; + QString fileName(const Node *node); + void findAllClasses(const InnerNode *node); + void findAllFunctions(const InnerNode *node); + void findAllLegaleseTexts(const InnerNode *node); + void findAllNamespaces(const InnerNode *node); + static int hOffset(const Node *node); + static bool isThreeColumnEnumValueTable(const Atom *atom); + QString getLink(const Atom *atom, + const Node *relative, + CodeMarker *marker, + const Node **node); + QString getDisambiguationLink(const Atom* atom, CodeMarker* marker); + virtual void generateIndex(const QString& fileBase, + const QString& url, + const QString& title); +#ifdef GENERATE_MAC_REFS + void generateMacRef(const Node* node, CodeMarker* marker); +#endif + void beginLink(const QString& link); + void endLink(); + QString writeGuidAttribute(QString text); + void writeGuidAttribute(Node* node); + QString lookupGuid(QString text); + QString lookupGuid(const QString& fileName, const QString& text); + GuidMap* lookupGuidMap(const QString& fileName); + virtual void beginSubPage(const InnerNode* node, const QString& fileName); + virtual void endSubPage(); + virtual void generateInnerNode(const InnerNode* node); + QXmlStreamWriter& xmlWriter(); + void writeApiDesc(const Node* node, CodeMarker* marker, const QString& title); + void addLink(const QString& href, const QStringRef& text, DitaTag t = DT_xref); + void writeDitaMap(const Tree* tree); + void writeDitaMap(const DitaMapNode* node); + void writeStartTag(DitaTag t); + void writeEndTag(DitaTag t=DT_NONE); + DitaTag currentTag(); + void clearSectionNesting() { sectionNestingLevel = 0; } + int enterApiDesc(const QString& outputclass, const QString& title); + int enterSection(const QString& outputclass, const QString& title); + int leaveSection(); + bool inSection() const { return (sectionNestingLevel > 0); } + int currentSectionNestingLevel() const { return sectionNestingLevel; } + QString metadataDefault(DitaTag t) const; + QString stripMarkup(const QString& src) const; + void collectNodesByTypeAndSubtype(const InnerNode* parent); + void writeDitaRefs(const DitaRefList& ditarefs); + void writeTopicrefs(NodeMultiMap* nmm, const QString& navtitle); + bool isDuplicate(NodeMultiMap* nmm, const QString& key, Node* node); + +private: + /* + These flags indicate which elements the generator + is currently outputting. + */ + bool inContents; + bool inDetailedDescription; + bool inLegaleseText; + bool inLink; + bool inObsoleteLink; + bool inSectionHeading; + bool inTableHeader; + bool inTableBody; + + bool noLinks; + bool obsoleteLinks; + bool offlineDocs; + bool threeColumnEnumValueTable; + + int codeIndent; + int numTableRows; + int divNestingLevel; + int sectionNestingLevel; + int tableColumnCount; + + QString link; + QStringList sectionNumber; + QRegExp funcLeftParen; + QString style; + QString postHeader; + QString postPostHeader; + QString footer; + QString address; + bool pleaseGenerateMacRef; + QString project; + QString projectDescription; + QString projectUrl; + QString navigationLinks; + QString version; + QStringList vrm; + QStringList stylesheets; + QStringList customHeadElements; + const Tree* tree_; + QMap<QString, QString> refMap; + QMap<QString, QString> name2guidMap; + GuidMaps guidMaps; + QMap<QString, NodeMap > moduleClassMap; + QMap<QString, NodeMap > moduleNamespaceMap; + NodeMap nonCompatClasses; + NodeMap mainClasses; + NodeMap compatClasses; + NodeMap obsoleteClasses; + NodeMap namespaceIndex; + NodeMap serviceClasses; +#ifdef QDOC_QML + NodeMap qmlClasses; +#endif + QMap<QString, NodeMap > funcIndex; + QMap<Text, const Node*> legaleseTexts; + static int id; + static QString ditaTags[]; + QStack<QXmlStreamWriter*> xmlWriterStack; + QStack<DitaTag> tagStack; + QStringMultiMap metadataDefaults; + QVector<NodeMultiMap*> nodeTypeMaps; + QVector<NodeMultiMap*> nodeSubtypeMaps; + QVector<NodeMultiMap*> pageTypeMaps; +}; + +#define DITAXMLGENERATOR_ADDRESS "address" +#define DITAXMLGENERATOR_FOOTER "footer" +#define DITAXMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me +#define DITAXMLGENERATOR_POSTHEADER "postheader" +#define DITAXMLGENERATOR_POSTPOSTHEADER "postpostheader" +#define DITAXMLGENERATOR_STYLE "style" +#define DITAXMLGENERATOR_STYLESHEETS "stylesheets" +#define DITAXMLGENERATOR_CUSTOMHEADELEMENTS "customheadelements" + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp new file mode 100644 index 0000000000..7572799eba --- /dev/null +++ b/src/tools/qdoc/doc.cpp @@ -0,0 +1,3393 @@ +/**************************************************************************** +** +** 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 "config.h" +#include "doc.h" +#include "codemarker.h" +#include "editdistance.h" +#include "openedlist.h" +#include "quoter.h" +#include "text.h" +#include "tokenizer.h" +#include <qdatetime.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qhash.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <ctype.h> +#include <limits.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString) +Q_GLOBAL_STATIC(TopicList, nullTopicList) +Q_GLOBAL_STATIC(QStringList, null_QStringList) +Q_GLOBAL_STATIC(QList<Text>, null_QList_Text) +//Q_GLOBAL_STATIC(QStringMap, null_QStringMap) +Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap) + +struct Macro +{ + QString defaultDef; + Location defaultDefLocation; + QStringMap otherDefs; + int numParams; +}; + +enum { + CMD_A, + CMD_ABSTRACT, + CMD_ANNOTATEDLIST, + CMD_B, + CMD_BADCODE, + CMD_BASENAME, + CMD_BOLD, + CMD_BRIEF, + CMD_C, + CMD_CAPTION, + CMD_CHAPTER, + CMD_CODE, + CMD_CODELINE, + CMD_DIV, + CMD_DOTS, + CMD_E, + CMD_ELSE, + CMD_ENDABSTRACT, + CMD_ENDCHAPTER, + CMD_ENDCODE, + CMD_ENDDIV, + CMD_ENDFOOTNOTE, + CMD_ENDIF, + CMD_ENDLEGALESE, + CMD_ENDLINK, + CMD_ENDLIST, + CMD_ENDMAPREF, + CMD_ENDOMIT, + CMD_ENDPART, + CMD_ENDQUOTATION, + CMD_ENDRAW, + CMD_ENDSECTION1, + CMD_ENDSECTION2, + CMD_ENDSECTION3, + CMD_ENDSECTION4, + CMD_ENDSIDEBAR, + CMD_ENDTABLE, + CMD_ENDTOPICREF, + CMD_EXPIRE, + CMD_FOOTNOTE, + CMD_GENERATELIST, + CMD_GRANULARITY, + CMD_HEADER, + CMD_I, + CMD_IF, + CMD_IMAGE, + CMD_IMPORTANT, + CMD_INCLUDE, + CMD_INLINEIMAGE, + CMD_INDEX, + CMD_KEYWORD, + CMD_L, + CMD_LEGALESE, + CMD_LI, + CMD_LINK, + CMD_LIST, + CMD_MAPREF, + CMD_META, + CMD_NEWCODE, + CMD_NOTE, + CMD_O, + CMD_OLDCODE, + CMD_OMIT, + CMD_OMITVALUE, + CMD_OVERLOAD, + CMD_PART, + CMD_PRINTLINE, + CMD_PRINTTO, + CMD_PRINTUNTIL, + CMD_QUOTATION, + CMD_QUOTEFILE, + CMD_QUOTEFROMFILE, + CMD_QUOTEFUNCTION, + CMD_RAW, + CMD_ROW, + CMD_SA, + CMD_SECTION1, + CMD_SECTION2, + CMD_SECTION3, + CMD_SECTION4, + CMD_SIDEBAR, + CMD_SINCELIST, + CMD_SKIPLINE, + CMD_SKIPTO, + CMD_SKIPUNTIL, + CMD_SNIPPET, + CMD_SPAN, + CMD_SUB, + CMD_SUP, + CMD_TABLE, + CMD_TABLEOFCONTENTS, + CMD_TARGET, + CMD_TOPICREF, + CMD_TT, + CMD_UNDERLINE, + CMD_UNICODE, + CMD_VALUE, + CMD_WARNING, + CMD_QML, + CMD_ENDQML, + CMD_CPP, + CMD_ENDCPP, + CMD_QMLTEXT, + CMD_ENDQMLTEXT, + CMD_CPPTEXT, + CMD_ENDCPPTEXT, + CMD_JS, + CMD_ENDJS, + NOT_A_CMD +}; + +static struct { + const char *english; + int no; + QString *alias; +} cmds[] = { + { "a", CMD_A, 0 }, + { "abstract", CMD_ABSTRACT, 0 }, + { "annotatedlist", CMD_ANNOTATEDLIST, 0 }, + { "b", CMD_B, 0 }, + { "badcode", CMD_BADCODE, 0 }, + { "basename", CMD_BASENAME, 0 }, // ### don't document for now + { "bold", CMD_BOLD, 0 }, + { "brief", CMD_BRIEF, 0 }, + { "c", CMD_C, 0 }, + { "caption", CMD_CAPTION, 0 }, + { "chapter", CMD_CHAPTER, 0 }, + { "code", CMD_CODE, 0 }, + { "codeline", CMD_CODELINE, 0}, + { "div", CMD_DIV, 0 }, + { "dots", CMD_DOTS, 0 }, + { "e", CMD_E, 0 }, + { "else", CMD_ELSE, 0 }, + { "endabstract", CMD_ENDABSTRACT, 0 }, + { "endchapter", CMD_ENDCHAPTER, 0 }, + { "endcode", CMD_ENDCODE, 0 }, + { "enddiv", CMD_ENDDIV, 0 }, + { "endfootnote", CMD_ENDFOOTNOTE, 0 }, + { "endif", CMD_ENDIF, 0 }, + { "endlegalese", CMD_ENDLEGALESE, 0 }, + { "endlink", CMD_ENDLINK, 0 }, + { "endlist", CMD_ENDLIST, 0 }, + { "endmapref", CMD_ENDMAPREF, 0 }, + { "endomit", CMD_ENDOMIT, 0 }, + { "endpart", CMD_ENDPART, 0 }, + { "endquotation", CMD_ENDQUOTATION, 0 }, + { "endraw", CMD_ENDRAW, 0 }, + { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now + { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now + { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now + { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now + { "endsidebar", CMD_ENDSIDEBAR, 0 }, + { "endtable", CMD_ENDTABLE, 0 }, + { "endtopicref", CMD_ENDTOPICREF, 0 }, + { "expire", CMD_EXPIRE, 0 }, + { "footnote", CMD_FOOTNOTE, 0 }, + { "generatelist", CMD_GENERATELIST, 0 }, + { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now + { "header", CMD_HEADER, 0 }, + { "i", CMD_I, 0 }, + { "if", CMD_IF, 0 }, + { "image", CMD_IMAGE, 0 }, + { "important", CMD_IMPORTANT, 0 }, + { "include", CMD_INCLUDE, 0 }, + { "inlineimage", CMD_INLINEIMAGE, 0 }, + { "index", CMD_INDEX, 0 }, // ### don't document for now + { "keyword", CMD_KEYWORD, 0 }, + { "l", CMD_L, 0 }, + { "legalese", CMD_LEGALESE, 0 }, + { "li", CMD_LI, 0 }, + { "link", CMD_LINK, 0 }, + { "list", CMD_LIST, 0 }, + { "mapref", CMD_MAPREF, 0 }, + { "meta", CMD_META, 0 }, + { "newcode", CMD_NEWCODE, 0 }, + { "note", CMD_NOTE, 0 }, + { "o", CMD_O, 0 }, + { "oldcode", CMD_OLDCODE, 0 }, + { "omit", CMD_OMIT, 0 }, + { "omitvalue", CMD_OMITVALUE, 0 }, + { "overload", CMD_OVERLOAD, 0 }, + { "part", CMD_PART, 0 }, + { "printline", CMD_PRINTLINE, 0 }, + { "printto", CMD_PRINTTO, 0 }, + { "printuntil", CMD_PRINTUNTIL, 0 }, + { "quotation", CMD_QUOTATION, 0 }, + { "quotefile", CMD_QUOTEFILE, 0 }, + { "quotefromfile", CMD_QUOTEFROMFILE, 0 }, + { "quotefunction", CMD_QUOTEFUNCTION, 0 }, + { "raw", CMD_RAW, 0 }, + { "row", CMD_ROW, 0 }, + { "sa", CMD_SA, 0 }, + { "section1", CMD_SECTION1, 0 }, + { "section2", CMD_SECTION2, 0 }, + { "section3", CMD_SECTION3, 0 }, + { "section4", CMD_SECTION4, 0 }, + { "sidebar", CMD_SIDEBAR, 0 }, + { "sincelist", CMD_SINCELIST, 0 }, + { "skipline", CMD_SKIPLINE, 0 }, + { "skipto", CMD_SKIPTO, 0 }, + { "skipuntil", CMD_SKIPUNTIL, 0 }, + { "snippet", CMD_SNIPPET, 0 }, + { "span", CMD_SPAN, 0 }, + { "sub", CMD_SUB, 0 }, + { "sup", CMD_SUP, 0 }, + { "table", CMD_TABLE, 0 }, + { "tableofcontents", CMD_TABLEOFCONTENTS, 0 }, + { "target", CMD_TARGET, 0 }, + { "topicref", CMD_TOPICREF, 0 }, + { "tt", CMD_TT, 0 }, + { "underline", CMD_UNDERLINE, 0 }, + { "unicode", CMD_UNICODE, 0 }, + { "value", CMD_VALUE, 0 }, + { "warning", CMD_WARNING, 0 }, + { "qml", CMD_QML, 0 }, + { "endqml", CMD_ENDQML, 0 }, + { "cpp", CMD_CPP, 0 }, + { "endcpp", CMD_ENDCPP, 0 }, + { "qmltext", CMD_QMLTEXT, 0 }, + { "endqmltext", CMD_ENDQMLTEXT, 0 }, + { "cpptext", CMD_CPPTEXT, 0 }, + { "endcpptext", CMD_ENDCPPTEXT, 0 }, + { "js", CMD_JS, 0 }, + { "endjs", CMD_ENDJS, 0 }, + { 0, 0, 0 } +}; + +typedef QHash<QString, int> QHash_QString_int; +typedef QHash<QString, Macro> QHash_QString_Macro; + +Q_GLOBAL_STATIC(QStringMap, aliasMap) +Q_GLOBAL_STATIC(QHash_QString_int, cmdHash) +Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash) + +class DocPrivateExtra +{ +public: + QString baseName; + Doc::Sections granularity; + Doc::Sections section; // ### + QList<Atom*> tableOfContents; + QList<int> tableOfContentsLevels; + QList<Atom*> keywords; + QList<Atom*> targets; + QStringMultiMap metaMap; + + DocPrivateExtra() + : granularity(Doc::Part) { } +}; + +struct Shared // ### get rid of +{ + Shared() + : count(1) { } + void ref() { ++count; } + bool deref() { return (--count == 0); } + + int count; +}; + +static QString cleanLink(const QString &link) +{ + int colonPos = link.indexOf(':'); + if ((colonPos == -1) || + (!link.startsWith("file:") && !link.startsWith("mailto:"))) + return link; + return link.mid(colonPos + 1).simplified(); +} + +class DocPrivate : public Shared +{ +public: + DocPrivate(const Location& start = Location::null, + const Location& end = Location::null, + const QString& source = ""); + ~DocPrivate(); + + void addAlso(const Text& also); + void constructExtra(); + bool isEnumDocSimplifiable() const; + + // ### move some of this in DocPrivateExtra + Location start_loc; + Location end_loc; + QString src; + Text text; + QSet<QString> params; + QList<Text> alsoList; + QStringList enumItemList; + QStringList omitEnumItemList; + QSet<QString> metacommandsUsed; + QCommandMap metaCommandMap; + bool hasLegalese : 1; + bool hasSectioningUnits : 1; + DocPrivateExtra *extra; + TopicList topics; + DitaRefList ditamap_; +}; + +DocPrivate::DocPrivate(const Location& start, + const Location& end, + const QString& source) + : start_loc(start), + end_loc(end), + src(source), + hasLegalese(false), + hasSectioningUnits(false), + extra(0) +{ + // nothing. +} + +/*! + If the doc is a ditamap, the destructor deletes each element + in the ditamap structure. These were allocated as needed. + */ +DocPrivate::~DocPrivate() +{ + delete extra; + foreach (DitaRef* t, ditamap_) { + delete t; + } +} + +void DocPrivate::addAlso(const Text& also) +{ + alsoList.append(also); +} + +void DocPrivate::constructExtra() +{ + if (extra == 0) + extra = new DocPrivateExtra; +} + +bool DocPrivate::isEnumDocSimplifiable() const +{ + bool justMetColon = false; + int numValueTables = 0; + + const Atom *atom = text.firstAtom(); + while (atom) { + if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) { + justMetColon = atom->string().endsWith(QLatin1Char(':')); + } + else if ((atom->type() == Atom::ListLeft) && + (atom->string() == ATOM_LIST_VALUE)) { + if (justMetColon || numValueTables > 0) + return false; + ++numValueTables; + } + atom = atom->next(); + } + return true; +} + +class DocParser +{ +public: + void parse(const QString &source, + DocPrivate *docPrivate, + const QSet<QString> &metaCommandSet, + const QSet<QString>& possibleTopics); + + static int endCmdFor(int cmd); + static QString cmdName(int cmd); + static QString endCmdName(int cmd); + static QString untabifyEtc(const QString& str); + static int indentLevel(const QString& str); + static QString unindent(int level, const QString& str); + static QString slashed(const QString& str); + + static int tabSize; + static QStringList exampleFiles; + static QStringList exampleDirs; + static QStringList sourceFiles; + static QStringList sourceDirs; + static bool quoting; + +private: + Location& location(); + QString detailsUnknownCommand(const QSet<QString>& metaCommandSet, + const QString& str); + void checkExpiry(const QString& date); + void insertBaseName(const QString &baseName); + void insertTarget(const QString& target, bool keyword); + void include(const QString& fileName, const QString& identifier); + void startFormat(const QString& format, int cmd); + bool openCommand(int cmd); + bool closeCommand(int endCmd); + void startSection(Doc::Sections unit, int cmd); + void endSection(int unit, int endCmd); + void parseAlso(); + void append(Atom::Type type, const QString& string = ""); + void append(Atom::Type type, const QString& p1, const QString& p2); + void appendChar(QChar ch); + void appendWord(const QString &word); + void appendToCode(const QString &code); + void appendToCode(const QString &code, Atom::Type defaultType); + void startNewPara(); + void enterPara(Atom::Type leftType = Atom::ParaLeft, + Atom::Type rightType = Atom::ParaRight, + const QString& string = ""); + void leavePara(); + void leaveValue(); + void leaveValueList(); + void leaveTableRow(); + CodeMarker *quoteFromFile(); + void expandMacro(const QString& name, const QString& def, int numParams); + QString expandMacroToString(const QString &name, const QString &def, int numParams); + Doc::Sections getSectioningUnit(); + QString getArgument(bool verbatim = false); + QString getBracedArgument(bool verbatim); + QString getOptionalArgument(); + QString getRestOfLine(); + QString getMetaCommandArgument(const QString &cmdStr); + QString getUntilEnd(int cmd); + QString getCode(int cmd, CodeMarker *marker); + QString getUnmarkedCode(int cmd); + + bool isBlankLine(); + bool isLeftBraceAhead(); + void skipSpacesOnLine(); + void skipSpacesOrOneEndl(); + void skipAllSpaces(); + void skipToNextPreprocessorCommand(); + + QStack<int> openedInputs; + + QString in; + int pos; + int len; + Location cachedLoc; + int cachedPos; + + DocPrivate* priv; + enum ParagraphState { + OutsideParagraph, + InSingleLineParagraph, + InMultiLineParagraph + }; + ParagraphState paraState; + bool inTableHeader; + bool inTableRow; + bool inTableItem; + bool indexStartedPara; // ### rename + Atom::Type pendingParaLeftType; + Atom::Type pendingParaRightType; + QString pendingParaString; + + int braceDepth; + int minIndent; + Doc::Sections currentSection; + QMap<QString, Location> targetMap; + QMap<int, QString> pendingFormats; + QStack<int> openedCommands; + QStack<OpenedList> openedLists; + Quoter quoter; + QStack<DitaRef*> ditarefs_; +}; + +int DocParser::tabSize; +QStringList DocParser::exampleFiles; +QStringList DocParser::exampleDirs; +QStringList DocParser::sourceFiles; +QStringList DocParser::sourceDirs; +bool DocParser::quoting; + +/*! + Parse the \a source string to build a Text data structure + in \a docPrivate. The Text data structure is a linked list + of Atoms. + + \a metaCommandSet is the set of metacommands that may be + found in \a source. These metacommands are not markup text + commands. They are topic commands and related metacommands. + */ +void DocParser::parse(const QString& source, + DocPrivate *docPrivate, + const QSet<QString>& metaCommandSet, + const QSet<QString>& possibleTopics) +{ + in = source; + pos = 0; + len = in.length(); + cachedLoc = docPrivate->start_loc; + cachedPos = 0; + priv = docPrivate; + priv->text << Atom::Nop; + priv->topics.clear(); + + paraState = OutsideParagraph; + inTableHeader = false; + inTableRow = false; + inTableItem = false; + indexStartedPara = false; + pendingParaLeftType = Atom::Nop; + pendingParaRightType = Atom::Nop; + + braceDepth = 0; + minIndent = INT_MAX; + currentSection = Doc::NoSection; + openedCommands.push(CMD_OMIT); + quoter.reset(); + + CodeMarker *marker = 0; + Atom *currentLinkAtom = 0; + QString p1, p2; + QStack<bool> preprocessorSkipping; + int numPreprocessorSkipping = 0; + + while (pos < len) { + QChar ch = in.at(pos); + + switch (ch.unicode()) { + case '\\': + { + QString cmdStr; + pos++; + while (pos < len) { + ch = in.at(pos); + if (ch.isLetterOrNumber()) { + cmdStr += ch; + pos++; + } + else { + break; + } + } + if (cmdStr.isEmpty()) { + if (pos < len) { + enterPara(); + if (in.at(pos).isSpace()) { + skipAllSpaces(); + appendChar(QLatin1Char(' ')); + } + else { + appendChar(in.at(pos++)); + } + } + } + else { + int cmd = cmdHash()->value(cmdStr,NOT_A_CMD); + switch (cmd) { + case CMD_A: + enterPara(); + p1 = getArgument(); + append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER); + append(Atom::String, p1); + append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER); + priv->params.insert(p1); + break; + case CMD_ABSTRACT: + if (openCommand(cmd)) { + leavePara(); + append(Atom::AbstractLeft); + } + break; + case CMD_BADCODE: + leavePara(); + append(Atom::CodeBad,getCode(CMD_BADCODE, marker)); + break; + case CMD_BASENAME: + leavePara(); + insertBaseName(getArgument()); + break; + case CMD_BOLD: + location().warning(tr("'\\bold' is deprecated. Use '\\b'")); + case CMD_B: + startFormat(ATOM_FORMATTING_BOLD, cmd); + break; + case CMD_BRIEF: + leavePara(); + enterPara(Atom::BriefLeft, Atom::BriefRight); + break; + case CMD_C: + enterPara(); + p1 = untabifyEtc(getArgument(true)); + marker = CodeMarker::markerForCode(p1); + append(Atom::C, marker->markedUpCode(p1, 0, location())); + break; + case CMD_CAPTION: + leavePara(); + enterPara(Atom::CaptionLeft, Atom::CaptionRight); + break; + case CMD_CHAPTER: + startSection(Doc::Chapter, cmd); + break; + case CMD_CODE: + leavePara(); + append(Atom::Code, getCode(CMD_CODE, 0)); + break; + case CMD_QML: + leavePara(); + append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML")))); + break; + case CMD_QMLTEXT: + append(Atom::QmlText); + break; + case CMD_JS: + leavePara(); + append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript")))); + break; + case CMD_DIV: + leavePara(); + p1 = getArgument(true); + append(Atom::DivLeft, p1); + openedCommands.push(cmd); + break; + case CMD_ENDDIV: + leavePara(); + append(Atom::DivRight); + closeCommand(cmd); + break; + case CMD_CODELINE: + { + if (!quoting) { + if (priv->text.lastAtom()->type() == Atom::Code + && priv->text.lastAtom()->string().endsWith("\n\n")) + priv->text.lastAtom()->chopString(); + appendToCode("\n"); + } + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, " "); + } + } + break; + case CMD_DOTS: + { + if (!quoting) { + if (priv->text.lastAtom()->type() == Atom::Code + && priv->text.lastAtom()->string().endsWith("\n\n")) + priv->text.lastAtom()->chopString(); + + QString arg = getOptionalArgument(); + int indent = 4; + if (!arg.isEmpty()) + indent = arg.toInt(); + for (int i = 0; i < indent; ++i) + appendToCode(" "); + appendToCode("...\n"); + } + else { + append(Atom::CodeQuoteCommand, cmdStr); + QString arg = getOptionalArgument(); + if (arg.isEmpty()) + arg = "4"; + append(Atom::CodeQuoteArgument, arg); + } + } + break; + case CMD_ELSE: + if (preprocessorSkipping.size() > 0) { + if (preprocessorSkipping.top()) { + --numPreprocessorSkipping; + } + else { + ++numPreprocessorSkipping; + } + preprocessorSkipping.top() = !preprocessorSkipping.top(); + (void)getRestOfLine(); // ### should ensure that it's empty + if (numPreprocessorSkipping) + skipToNextPreprocessorCommand(); + } + else { + location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE))); + } + break; + case CMD_ENDABSTRACT: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::AbstractRight); + } + break; + case CMD_ENDCHAPTER: + endSection(Doc::Chapter, cmd); + break; + case CMD_ENDCODE: + closeCommand(cmd); + break; + case CMD_ENDQML: + closeCommand(cmd); + break; + case CMD_ENDQMLTEXT: + append(Atom::EndQmlText); + break; + case CMD_ENDJS: + closeCommand(cmd); + break; + case CMD_ENDFOOTNOTE: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::FootnoteRight); + paraState = InMultiLineParagraph; // ### + } + break; + case CMD_ENDIF: + if (preprocessorSkipping.count() > 0) { + if (preprocessorSkipping.pop()) + --numPreprocessorSkipping; + (void)getRestOfLine(); // ### should ensure that it's empty + if (numPreprocessorSkipping) + skipToNextPreprocessorCommand(); + } + else { + location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF))); + } + break; + case CMD_ENDLEGALESE: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::LegaleseRight); + } + break; + case CMD_ENDLINK: + if (closeCommand(cmd)) { + if (priv->text.lastAtom()->type() == Atom::String + && priv->text.lastAtom()->string().endsWith(QLatin1Char(' '))) + priv->text.lastAtom()->chopString(); + append(Atom::FormattingRight, ATOM_FORMATTING_LINK); + } + break; + case CMD_ENDLIST: + if (closeCommand(cmd)) { + leavePara(); + if (openedLists.top().isStarted()) { + append(Atom::ListItemRight, + openedLists.top().styleString()); + append(Atom::ListRight, + openedLists.top().styleString()); + } + openedLists.pop(); + } + break; + case CMD_ENDMAPREF: + case CMD_ENDTOPICREF: + if (closeCommand(cmd)) { + ditarefs_.pop(); // zzz + } + break; + case CMD_ENDOMIT: + closeCommand(cmd); + break; + case CMD_ENDPART: + endSection(Doc::Part, cmd); + break; + case CMD_ENDQUOTATION: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::QuotationRight); + } + break; + case CMD_ENDRAW: + location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW))); + break; + case CMD_ENDSECTION1: + endSection(Doc::Section1, cmd); + break; + case CMD_ENDSECTION2: + endSection(Doc::Section2, cmd); + break; + case CMD_ENDSECTION3: + endSection(Doc::Section3, cmd); + break; + case CMD_ENDSECTION4: + endSection(Doc::Section4, cmd); + break; + case CMD_ENDSIDEBAR: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::SidebarRight); + } + break; + case CMD_ENDTABLE: + if (closeCommand(cmd)) { + leaveTableRow(); + append(Atom::TableRight); + } + break; + case CMD_EXPIRE: + checkExpiry(getArgument()); + break; + case CMD_FOOTNOTE: + if (openCommand(cmd)) { + enterPara(); + append(Atom::FootnoteLeft); + paraState = OutsideParagraph; // ### + } + break; + case CMD_ANNOTATEDLIST: + append(Atom::AnnotatedList, getArgument()); + break; + case CMD_SINCELIST: + append(Atom::SinceList, getRestOfLine().simplified()); + break; + case CMD_GENERATELIST: + append(Atom::GeneratedList, getArgument()); + break; + case CMD_GRANULARITY: + priv->constructExtra(); + priv->extra->granularity = getSectioningUnit(); + break; + case CMD_HEADER: + if (openedCommands.top() == CMD_TABLE) { + leaveTableRow(); + append(Atom::TableHeaderLeft); + inTableHeader = true; + } + else { + if (openedCommands.contains(CMD_TABLE)) { + location().warning(tr("Cannot use '\\%1' within '\\%2'") + .arg(cmdName(CMD_HEADER)) + .arg(cmdName(openedCommands.top()))); + } + else { + location().warning(tr("Cannot use '\\%1' outside of '\\%2'") + .arg(cmdName(CMD_HEADER)) + .arg(cmdName(CMD_TABLE))); + } + } + break; + case CMD_I: + location().warning(tr("'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item")); + case CMD_E: + startFormat(ATOM_FORMATTING_ITALIC, cmd); + break; + case CMD_IF: + preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine())); + if (preprocessorSkipping.top()) + ++numPreprocessorSkipping; + if (numPreprocessorSkipping) + skipToNextPreprocessorCommand(); + break; + case CMD_IMAGE: + leaveValueList(); + append(Atom::Image, getArgument()); + append(Atom::ImageText, getRestOfLine()); + break; + case CMD_IMPORTANT: + leavePara(); + enterPara(Atom::ImportantLeft, Atom::ImportantRight); + break; + case CMD_INCLUDE: + { + QString fileName = getArgument(); + QString identifier = getRestOfLine(); + include(fileName, identifier); + } + break; + case CMD_INLINEIMAGE: + enterPara(); + append(Atom::InlineImage, getArgument()); + append(Atom::ImageText, getRestOfLine()); + append(Atom::String, " "); + break; + case CMD_INDEX: + if (paraState == OutsideParagraph) { + enterPara(); + indexStartedPara = true; + } + else { + const Atom *last = priv->text.lastAtom(); + if (indexStartedPara && + (last->type() != Atom::FormattingRight || + last->string() != ATOM_FORMATTING_INDEX)) + indexStartedPara = false; + } + startFormat(ATOM_FORMATTING_INDEX, cmd); + break; + case CMD_KEYWORD: + insertTarget(getRestOfLine(),true); + break; + case CMD_L: + enterPara(); + if (isLeftBraceAhead()) { + p1 = getArgument(); + append(Atom::Link, p1); + if (isLeftBraceAhead()) { + currentLinkAtom = priv->text.lastAtom(); + startFormat(ATOM_FORMATTING_LINK, cmd); + } + else { + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + append(Atom::String, cleanLink(p1)); + append(Atom::FormattingRight, ATOM_FORMATTING_LINK); + } + } + else { + p1 = getArgument(); + append(Atom::Link, p1); + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + append(Atom::String, cleanLink(p1)); + append(Atom::FormattingRight, ATOM_FORMATTING_LINK); + } + break; + case CMD_LEGALESE: + leavePara(); + if (openCommand(cmd)) + append(Atom::LegaleseLeft); + docPrivate->hasLegalese = true; + break; + case CMD_LINK: + if (openCommand(cmd)) { + enterPara(); + p1 = getArgument(); + append(Atom::Link, p1); + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + skipSpacesOrOneEndl(); + } + break; + case CMD_LIST: + if (openCommand(cmd)) { + leavePara(); + openedLists.push(OpenedList(location(), + getOptionalArgument())); + } + break; + case CMD_TOPICREF: + case CMD_MAPREF: + if (openCommand(cmd)) { + DitaRef* t = 0; + if (cmd == CMD_MAPREF) + t = new MapRef(); + else + t = new TopicRef(); + t->setNavtitle(getArgument(true)); + if (cmd == CMD_MAPREF) + t->setHref(getArgument()); + else + t->setHref(getOptionalArgument()); + if (ditarefs_.isEmpty()) + priv->ditamap_.append(t); + else + ditarefs_.top()->appendSubref(t); + ditarefs_.push(t); + } + break; + case CMD_META: + priv->constructExtra(); + p1 = getArgument(); + priv->extra->metaMap.insert(p1, getArgument()); + break; + case CMD_NEWCODE: + location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE))); + break; + case CMD_NOTE: + leavePara(); + enterPara(Atom::NoteLeft, Atom::NoteRight); + break; + case CMD_O: + location().warning(tr("'\\o' is deprecated. Use '\\li'")); + case CMD_LI: + leavePara(); + if (openedCommands.top() == CMD_LIST) { + if (openedLists.top().isStarted()) { + append(Atom::ListItemRight, + openedLists.top().styleString()); + } + else { + append(Atom::ListLeft, + openedLists.top().styleString()); + } + openedLists.top().next(); + append(Atom::ListItemNumber, + openedLists.top().numberString()); + append(Atom::ListItemLeft, + openedLists.top().styleString()); + enterPara(); + } + else if (openedCommands.top() == CMD_TABLE) { + p1 = "1,1"; + p2 = ""; + if (isLeftBraceAhead()) { + p1 = getArgument(); + if (isLeftBraceAhead()) { + p2 = getArgument(); + } + } + + if (!inTableHeader && !inTableRow) { + location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'") + .arg(cmdName(CMD_HEADER)) + .arg(cmdName(CMD_ROW)) + .arg(cmdName(CMD_LI))); + append(Atom::TableRowLeft); + inTableRow = true; + } + else if (inTableItem) { + append(Atom::TableItemRight); + inTableItem = false; + } + + append(Atom::TableItemLeft, p1, p2); + inTableItem = true; + } + else { + location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'") + .arg(cmdName(cmd)) + .arg(cmdName(CMD_LIST)) + .arg(cmdName(CMD_TABLE))); + } + break; + case CMD_OLDCODE: + leavePara(); + append(Atom::CodeOld, getCode(CMD_OLDCODE, marker)); + append(Atom::CodeNew, getCode(CMD_NEWCODE, marker)); + break; + case CMD_OMIT: + getUntilEnd(cmd); + break; + case CMD_OMITVALUE: + p1 = getArgument(); + if (!priv->enumItemList.contains(p1)) + priv->enumItemList.append(p1); + if (!priv->omitEnumItemList.contains(p1)) + priv->omitEnumItemList.append(p1); + break; + case CMD_PART: + startSection(Doc::Part, cmd); + break; + case CMD_PRINTLINE: + leavePara(); + if (!quoting) + appendToCode(quoter.quoteLine(location(), cmdStr, + getRestOfLine())); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_PRINTTO: + leavePara(); + if (!quoting) + appendToCode(quoter.quoteTo(location(), cmdStr, + getRestOfLine())); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_PRINTUNTIL: + leavePara(); + if (!quoting) + appendToCode(quoter.quoteUntil(location(), cmdStr, + getRestOfLine())); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_QUOTATION: + if (openCommand(cmd)) { + leavePara(); + append(Atom::QuotationLeft); + } + break; + case CMD_QUOTEFILE: + { + leavePara(); + QString fileName = getArgument(); + Doc::quoteFromFile(location(), quoter, fileName); + if (!quoting) { + append(Atom::Code, + quoter.quoteTo(location(), cmdStr, "")); + quoter.reset(); + } + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, fileName); + } + break; + } + case CMD_QUOTEFROMFILE: + leavePara(); + if (!quoting) + quoteFromFile(); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getArgument()); + } + break; + case CMD_QUOTEFUNCTION: + leavePara(); + marker = quoteFromFile(); + p1 = getRestOfLine(); + if (!quoting) { + quoter.quoteTo(location(), cmdStr, + slashed(marker->functionBeginRegExp(p1))); + append(Atom::Code, + quoter.quoteUntil(location(), cmdStr, + slashed(marker->functionEndRegExp(p1)))); + quoter.reset(); + } + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1))); + } + break; + case CMD_RAW: + leavePara(); + p1 = getRestOfLine(); + if (p1.isEmpty()) + location().warning(tr("Missing format name after '\\%1") + .arg(cmdName(CMD_RAW))); + append(Atom::FormatIf, p1); + append(Atom::RawString, untabifyEtc(getUntilEnd(cmd))); + append(Atom::FormatElse); + append(Atom::FormatEndif); + break; + case CMD_ROW: + if (openedCommands.top() == CMD_TABLE) { + p1.clear(); + if (isLeftBraceAhead()) + p1 = getArgument(true); + leaveTableRow(); + append(Atom::TableRowLeft,p1); + inTableRow = true; + } + else { + if (openedCommands.contains(CMD_TABLE)) { + location().warning(tr("Cannot use '\\%1' within '\\%2'") + .arg(cmdName(CMD_ROW)) + .arg(cmdName(openedCommands.top()))); + } + else { + location().warning(tr("Cannot use '\\%1' outside of '\\%2'") + .arg(cmdName(CMD_ROW)) + .arg(cmdName(CMD_TABLE))); + } + } + break; + case CMD_SA: + parseAlso(); + break; + case CMD_SECTION1: + startSection(Doc::Section1, cmd); + break; + case CMD_SECTION2: + startSection(Doc::Section2, cmd); + break; + case CMD_SECTION3: + startSection(Doc::Section3, cmd); + break; + case CMD_SECTION4: + startSection(Doc::Section4, cmd); + break; + case CMD_SIDEBAR: + if (openCommand(cmd)) { + leavePara(); + append(Atom::SidebarLeft); + } + break; + case CMD_SKIPLINE: + leavePara(); + if (!quoting) + quoter.quoteLine(location(), + cmdStr, + getRestOfLine()); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_SKIPTO: + leavePara(); + if (!quoting) + quoter.quoteTo(location(), + cmdStr, + getRestOfLine()); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_SKIPUNTIL: + leavePara(); + if (!quoting) + quoter.quoteUntil(location(), + cmdStr, + getRestOfLine()); + else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, getRestOfLine()); + } + break; + case CMD_SPAN: + p1 = ATOM_FORMATTING_SPAN + getArgument(true); + startFormat(p1, cmd); + break; + case CMD_SNIPPET: + leavePara(); + { + QString snippet = getArgument(); + QString identifier = getRestOfLine(); + if (quoting) { + append(Atom::SnippetCommand, cmdStr); + append(Atom::SnippetLocation, snippet); + append(Atom::SnippetIdentifier, identifier); + } + else { + marker = Doc::quoteFromFile(location(),quoter,snippet); + appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType()); + } + } + break; + case CMD_SUB: + startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd); + break; + case CMD_SUP: + startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd); + break; + case CMD_TABLE: + //p1 = getRestOfLine(); + p1 = getOptionalArgument(); + p2 = getOptionalArgument(); + if (openCommand(cmd)) { + leavePara(); + append(Atom::TableLeft, p1, p2); + inTableHeader = false; + inTableRow = false; + inTableItem = false; + } + break; + case CMD_TABLEOFCONTENTS: + p1 = "1"; + if (isLeftBraceAhead()) + p1 = getArgument(); + p1 += ","; + p1 += QString::number((int)getSectioningUnit()); + append(Atom::TableOfContents, p1); + break; + case CMD_TARGET: + insertTarget(getRestOfLine(),false); + break; + case CMD_TT: + startFormat(ATOM_FORMATTING_TELETYPE, cmd); + break; + case CMD_UNDERLINE: + startFormat(ATOM_FORMATTING_UNDERLINE, cmd); + break; + case CMD_UNICODE: + enterPara(); + p1 = getArgument(); + { + bool ok; + uint unicodeChar = p1.toUInt(&ok, 0); + if (!ok || + (unicodeChar == 0x0000) || + (unicodeChar > 0xFFFE)) { + location().warning(tr("Invalid Unicode character '%1' specified " + "with '%2'") + .arg(p1, cmdName(CMD_UNICODE))); + } + else { + append(Atom::String, QChar(unicodeChar)); + } + } + break; + case CMD_VALUE: + leaveValue(); + if (openedLists.top().style() == OpenedList::Value) { + p1 = getArgument(); + if (!priv->enumItemList.contains(p1)) + priv->enumItemList.append(p1); + + openedLists.top().next(); + append(Atom::ListTagLeft, ATOM_LIST_VALUE); + append(Atom::String, p1); + append(Atom::ListTagRight, ATOM_LIST_VALUE); + append(Atom::ListItemLeft, ATOM_LIST_VALUE); + + skipSpacesOrOneEndl(); + if (isBlankLine()) + append(Atom::Nop); + } + else { + // ### problems + } + break; + case CMD_WARNING: + leavePara(); + enterPara(); + append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + append(Atom::String, "Warning:"); + append(Atom::FormattingRight, ATOM_FORMATTING_BOLD); + append(Atom::String, " "); + break; + case CMD_OVERLOAD: + priv->metacommandsUsed.insert(cmdStr); + p1.clear(); + if (!isBlankLine()) + p1 = getRestOfLine(); + if (!p1.isEmpty()) { + append(Atom::ParaLeft); + append(Atom::String, "This function overloads "); + append(Atom::AutoLink,p1); + append(Atom::String, "."); + append(Atom::ParaRight); + } + else { + append(Atom::ParaLeft); + append(Atom::String,"This is an overloaded function."); + append(Atom::ParaRight); + p1 = getMetaCommandArgument(cmdStr); + } + priv->metaCommandMap[cmdStr].append(p1); + break; + case NOT_A_CMD: + if (metaCommandSet.contains(cmdStr)) { + priv->metacommandsUsed.insert(cmdStr); + QString arg = getMetaCommandArgument(cmdStr); + priv->metaCommandMap[cmdStr].append(arg); + if (possibleTopics.contains(cmdStr)) { + priv->topics.append(Topic(cmdStr,arg)); + } + } + else if (macroHash()->contains(cmdStr)) { + const Macro ¯o = macroHash()->value(cmdStr); + int numPendingFi = 0; + QStringMap::ConstIterator d; + d = macro.otherDefs.begin(); + while (d != macro.otherDefs.end()) { + append(Atom::FormatIf, d.key()); + expandMacro(cmdStr, *d, macro.numParams); + ++d; + + if (d == macro.otherDefs.end()) { + append(Atom::FormatEndif); + } + else { + append(Atom::FormatElse); + numPendingFi++; + } + } + while (numPendingFi-- > 0) + append(Atom::FormatEndif); + + if (!macro.defaultDef.isEmpty()) { + if (!macro.otherDefs.isEmpty()) { + macro.defaultDefLocation.warning( + tr("Macro cannot have both " + "format-specific and qdoc- " + "syntax definitions")); + } + else { + location().push(macro.defaultDefLocation.filePath()); + in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams)); + len = in.length(); + openedInputs.push(pos + macro.defaultDef.length()); + } + } + } + else { + location().warning( + tr("Unknown command '\\%1'").arg(cmdStr), + detailsUnknownCommand(metaCommandSet,cmdStr)); + enterPara(); + append(Atom::UnknownCommand, cmdStr); + } + } + } + } + break; + case '{': + enterPara(); + appendChar('{'); + braceDepth++; + pos++; + break; + case '}': + { + braceDepth--; + pos++; + + QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth); + if (f == pendingFormats.end()) { + enterPara(); + appendChar('}'); + } + else { + append(Atom::FormattingRight, *f); + if (*f == ATOM_FORMATTING_INDEX) { + if (indexStartedPara) + skipAllSpaces(); + } + else if (*f == ATOM_FORMATTING_LINK) { + // hack for C++ to support links like + // \l{QString::}{count()} + if (currentLinkAtom && + currentLinkAtom->string().endsWith("::")) { + QString suffix = Text::subText(currentLinkAtom, + priv->text.lastAtom()).toString(); + currentLinkAtom->appendString(suffix); + } + currentLinkAtom = 0; + } + pendingFormats.erase(f); + } + } + break; + default: + { + bool newWord; + switch (priv->text.lastAtom()->type()) { + case Atom::ParaLeft: + newWord = true; + break; + default: + newWord = false; + } + + if (paraState == OutsideParagraph) { + if (ch.isSpace()) { + ++pos; + newWord = false; + } + else { + enterPara(); + newWord = true; + } + } + else { + if (ch.isSpace()) { + ++pos; + if ((ch == '\n') && + (paraState == InSingleLineParagraph || + isBlankLine())) { + leavePara(); + newWord = false; + } + else { + appendChar(' '); + newWord = true; + } + } + else { + newWord = true; + } + } + + if (newWord) { + int startPos = pos; + int numInternalUppercase = 0; + int numLowercase = 0; + int numStrangeSymbols = 0; + + while (pos < len) { + unsigned char latin1Ch = in.at(pos).toLatin1(); + if (islower(latin1Ch)) { + ++numLowercase; + ++pos; + } + else if (isupper(latin1Ch)) { + if (pos > startPos) + ++numInternalUppercase; + ++pos; + } + else if (isdigit(latin1Ch)) { + if (pos > startPos) { + ++pos; + } + else { + break; + } + } + else if (latin1Ch == '_' || latin1Ch == '@') { + ++numStrangeSymbols; + ++pos; + } + else if (latin1Ch == ':' && pos < len - 1 + && in.at(pos + 1) == QLatin1Char(':')) { + ++numStrangeSymbols; + pos += 2; + } + else if (latin1Ch == '(') { + if (pos > startPos) { + if (pos < len - 1 && + in.at(pos + 1) == QLatin1Char(')')) { + ++numStrangeSymbols; + pos += 2; + break; + } + else { + // ### handle functions with signatures + // and function calls + break; + } + } + else { + break; + } + } + else { + break; + } + } + + if (pos == startPos) { + if (!ch.isSpace()) { + appendChar(ch); + ++pos; + } + } + else { + QString word = in.mid(startPos, pos - startPos); + // is word a C++ symbol or an English word? + if ((numInternalUppercase >= 1 && numLowercase >= 2) + || numStrangeSymbols >= 1) { + append(Atom::AutoLink, word); + } + else { + appendWord(word); + } + } + } + } + } + } + leaveValueList(); + + // for compatibility + if (openedCommands.top() == CMD_LEGALESE) { + append(Atom::LegaleseRight); + openedCommands.pop(); + } + + if (openedCommands.top() != CMD_OMIT) { + location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top()))); + } + else if (preprocessorSkipping.count() > 0) { + location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF))); + } + + if (currentSection > Doc::NoSection) { + append(Atom::SectionRight, QString::number(currentSection)); + currentSection = Doc::NoSection; + } + + if (priv->extra && priv->extra->granularity < priv->extra->section) + priv->extra->granularity = priv->extra->section; + priv->text.stripFirstAtom(); +} + +Location &DocParser::location() +{ + while (!openedInputs.isEmpty() && openedInputs.top() <= pos) { + cachedLoc.pop(); + cachedPos = openedInputs.pop(); + } + while (cachedPos < pos) + cachedLoc.advance(in.at(cachedPos++)); + return cachedLoc; +} + +QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet, + const QString &str) +{ + QSet<QString> commandSet = metaCommandSet; + int i = 0; + while (cmds[i].english != 0) { + commandSet.insert(*cmds[i].alias); + i++; + } + + if (aliasMap()->contains(str)) + return tr("The command '\\%1' was renamed '\\%2' by the configuration" + " file. Use the new name.") + .arg(str).arg((*aliasMap())[str]); + + QString best = nearestName(str, commandSet); + if (best.isEmpty()) + return QString(); + return tr("Maybe you meant '\\%1'?").arg(best); +} + +void DocParser::checkExpiry(const QString& date) +{ + QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))"); + + if (ymd.exactMatch(date)) { + int y = ymd.cap(1).toInt(); + int m = ymd.cap(2).toInt(); + int d = ymd.cap(3).toInt(); + + if (m == 0) + m = 1; + if (d == 0) + d = 1; + QDate expiryDate(y, m, d); + if (expiryDate.isValid()) { + int days = expiryDate.daysTo(QDate::currentDate()); + if (days == 0) { + location().warning(tr("Documentation expires today")); + } + else if (days == 1) { + location().warning(tr("Documentation expired yesterday")); + } + else if (days >= 2) { + location().warning(tr("Documentation expired %1 days ago") + .arg(days)); + } + } + else { + location().warning(tr("Date '%1' invalid").arg(date)); + } + } + else { + location().warning(tr("Date '%1' not in YYYY-MM-DD format") + .arg(date)); + } +} + +void DocParser::insertBaseName(const QString &baseName) +{ + priv->constructExtra(); + if (currentSection == priv->extra->section) { + priv->extra->baseName = baseName; + } + else { + Atom *atom = priv->text.firstAtom(); + Atom *sectionLeft = 0; + + int delta = currentSection - priv->extra->section; + + while (atom != 0) { + if (atom->type() == Atom::SectionLeft && + atom->string().toInt() == delta) + sectionLeft = atom; + atom = atom->next(); + } + if (sectionLeft != 0) + (void) new Atom(sectionLeft, Atom::BaseName, baseName); + } +} + +void DocParser::insertTarget(const QString &target, bool keyword) +{ + if (targetMap.contains(target)) { + location().warning(tr("Duplicate target name '%1'").arg(target)); + targetMap[target].warning(tr("(The previous occurrence is here)")); + } + else { + targetMap.insert(target, location()); + append(Atom::Target, target); + priv->constructExtra(); + if (keyword) + priv->extra->keywords.append(priv->text.lastAtom()); + else + priv->extra->targets.append(priv->text.lastAtom()); + } +} + +void DocParser::include(const QString& fileName, const QString& identifier) +{ + if (location().depth() > 16) + location().fatal(tr("Too many nested '\\%1's") + .arg(cmdName(CMD_INCLUDE))); + + QString userFriendlyFilePath; + // ### use current directory? + QString filePath = Config::findFile(location(), + sourceFiles, + sourceDirs, + fileName, + userFriendlyFilePath); + if (filePath.isEmpty()) { + location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName)); + } + else { + QFile inFile(filePath); + if (!inFile.open(QFile::ReadOnly)) { + location().warning(tr("Cannot open qdoc include file '%1'") + .arg(userFriendlyFilePath)); + } + else { + location().push(userFriendlyFilePath); + + QTextStream inStream(&inFile); + QString includedStuff = inStream.readAll(); + inFile.close(); + + if (identifier.isEmpty()) { + in.insert(pos, includedStuff); + len = in.length(); + openedInputs.push(pos + includedStuff.length()); + } + else { + QStringList lineBuffer = includedStuff.split(QLatin1Char('\n')); + int i = 0; + int startLine = -1; + while (i < lineBuffer.size()) { + if (lineBuffer[i].startsWith("//!")) { + if (lineBuffer[i].contains(identifier)) { + startLine = i+1; + break; + } + } + ++i; + } + if (startLine < 0) { + location().warning(tr("Cannot find '%1' in '%2'") + .arg(identifier) + .arg(userFriendlyFilePath)); + return; + + } + QString result; + i = startLine; + do { + if (lineBuffer[i].startsWith("//!")) { + if (i<lineBuffer.size()) { + if (lineBuffer[i].contains(identifier)) { + break; + } + } + } + else + result += lineBuffer[i] + QLatin1Char('\n'); + ++i; + } while (i < lineBuffer.size()); + if (result.isEmpty()) { + location().warning(tr("Empty qdoc snippet '%1' in '%2'") + .arg(identifier) + .arg(userFriendlyFilePath)); + } + else { + in.insert(pos, result); + len = in.length(); + openedInputs.push(pos + result.length()); + } + } + } + } +} + +void DocParser::startFormat(const QString& format, int cmd) +{ + enterPara(); + + QMap<int, QString>::ConstIterator f = pendingFormats.begin(); + while (f != pendingFormats.end()) { + if (*f == format) { + location().warning(tr("Cannot nest '\\%1' commands") + .arg(cmdName(cmd))); + return; + } + ++f; + } + + append(Atom::FormattingLeft, format); + + if (isLeftBraceAhead()) { + skipSpacesOrOneEndl(); + pendingFormats.insert(braceDepth, format); + ++braceDepth; + ++pos; + } + else { + append(Atom::String, getArgument()); + append(Atom::FormattingRight, format); + if (format == ATOM_FORMATTING_INDEX && indexStartedPara) { + skipAllSpaces(); + indexStartedPara = false; + } + } +} + +bool DocParser::openCommand(int cmd) +{ + int outer = openedCommands.top(); + bool ok = true; + + if (cmd != CMD_LINK) { + if (outer == CMD_LIST) { + ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST); + } + else if (outer == CMD_ABSTRACT) { + ok = (cmd == CMD_LIST || + cmd == CMD_QUOTATION || + cmd == CMD_TABLE); + } + else if (outer == CMD_SIDEBAR) { + ok = (cmd == CMD_LIST || + cmd == CMD_QUOTATION || + cmd == CMD_SIDEBAR); + } + else if (outer == CMD_QUOTATION) { + ok = (cmd == CMD_LIST); + } + else if (outer == CMD_TABLE) { + ok = (cmd == CMD_LIST || + cmd == CMD_FOOTNOTE || + cmd == CMD_QUOTATION); + } + else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) { + ok = false; + } + else if (outer == CMD_TOPICREF) + ok = (cmd == CMD_TOPICREF || cmd == CMD_MAPREF); + else if (outer == CMD_MAPREF) + ok = false; + } + + if (ok) { + openedCommands.push(cmd); + } + else { + location().warning(tr("Can't use '\\%1' in '\\%2'").arg(cmdName(cmd)).arg(cmdName(outer))); + } + return ok; +} + +bool DocParser::closeCommand(int endCmd) +{ + if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) { + openedCommands.pop(); + return true; + } + else { + bool contains = false; + QStack<int> opened2 = openedCommands; + while (opened2.size() > 1) { + if (endCmdFor(opened2.top()) == endCmd) { + contains = true; + break; + } + opened2.pop(); + } + + if (contains) { + while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) { + location().warning(tr("Missing '\\%1' before '\\%2'") + .arg(endCmdName(openedCommands.top())) + .arg(cmdName(endCmd))); + openedCommands.pop(); + } + } + else { + location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd))); + } + return false; + } +} + +void DocParser::startSection(Doc::Sections unit, int cmd) +{ + leaveValueList(); + + if (currentSection == Doc::NoSection) { + currentSection = (Doc::Sections) (unit); + priv->constructExtra(); + priv->extra->section = currentSection; + } + else + endSection(unit,cmd); + + append(Atom::SectionLeft, QString::number(unit)); + priv->constructExtra(); + priv->extra->tableOfContents.append(priv->text.lastAtom()); + priv->extra->tableOfContentsLevels.append(unit); + enterPara(Atom::SectionHeadingLeft, + Atom::SectionHeadingRight, + QString::number(unit)); + currentSection = unit; + +} + +void DocParser::endSection(int , int) // (int unit, int endCmd) +{ + leavePara(); + append(Atom::SectionRight, QString::number(currentSection)); + currentSection = (Doc::NoSection); +} + +void DocParser::parseAlso() +{ + leavePara(); + skipSpacesOnLine(); + while (pos < len && in[pos] != '\n') { + QString target; + QString str; + + if (in[pos] == '{') { + target = getArgument(); + skipSpacesOnLine(); + if (in[pos] == '{') { + str = getArgument(); + + // hack for C++ to support links like \l{QString::}{count()} + if (target.endsWith("::")) + target += str; + } + else { + str = target; + } +#ifdef QDOC2_COMPAT + } + else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") { + pos += 6; + target = getArgument(); + int endPos = in.indexOf("\\endlink", pos); + if (endPos != -1) { + str = in.mid(pos, endPos - pos).trimmed(); + pos = endPos + 8; + } +#endif + } + else { + target = getArgument(); + str = cleanLink(target); + } + + Text also; + also << Atom(Atom::Link, target) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << str + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + priv->addAlso(also); + + skipSpacesOnLine(); + if (pos < len && in[pos] == ',') { + pos++; + skipSpacesOrOneEndl(); + } + else if (in[pos] != '\n') { + location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA))); + } + } +} + +//static bool debug = false; +#if 0 +if (type == Atom::DivLeft) +debug = true; +if (debug) +qDebug() << type << string; +if (type == Atom::DivRight) +debug = false; +#endif + +void DocParser::append(Atom::Type type, const QString &string) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); + if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) + priv->text.lastAtom()->chopString(); + priv->text << Atom(type, string); +} + +void DocParser::append(Atom::Type type, const QString& p1, const QString& p2) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); + if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) + priv->text.lastAtom()->chopString(); + priv->text << Atom(type, p1, p2); +} + +void DocParser::appendChar(QChar ch) +{ + if (priv->text.lastAtom()->type() != Atom::String) + append(Atom::String); + Atom *atom = priv->text.lastAtom(); + if (ch == QLatin1Char(' ')) { + if (!atom->string().endsWith(QLatin1Char(' '))) + atom->appendChar(QLatin1Char(' ')); + } + else + atom->appendChar(ch); +} + +void DocParser::appendWord(const QString &word) +{ + if (priv->text.lastAtom()->type() != Atom::String) { + append(Atom::String, word); + } + else + priv->text.lastAtom()->appendString(word); +} + +void DocParser::appendToCode(const QString& markedCode) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); +#ifdef QDOC_QML + if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) + append(Atom::Qml); +#else + if (lastType != Atom::Code) + append(Atom::Code); +#endif + priv->text.lastAtom()->appendString(markedCode); +} + +void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); + if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) + append(defaultType, markedCode); + else + priv->text.lastAtom()->appendString(markedCode); +} + +void DocParser::startNewPara() +{ + leavePara(); + enterPara(); +} + +void DocParser::enterPara(Atom::Type leftType, + Atom::Type rightType, + const QString& string) +{ + if (paraState == OutsideParagraph) { + + if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) && + (priv->text.lastAtom()->type() != Atom::DivLeft)) { + leaveValueList(); + } + + append(leftType, string); + indexStartedPara = false; + pendingParaLeftType = leftType; + pendingParaRightType = rightType; + pendingParaString = string; + if (leftType == Atom::SectionHeadingLeft) { + paraState = InSingleLineParagraph; + } + else { + paraState = InMultiLineParagraph; + } + skipSpacesOrOneEndl(); + } +} + +void DocParser::leavePara() +{ + if (paraState != OutsideParagraph) { + if (!pendingFormats.isEmpty()) { + location().warning(tr("Missing '}'")); + pendingFormats.clear(); + } + + if (priv->text.lastAtom()->type() == pendingParaLeftType) { + priv->text.stripLastAtom(); + } + else { + if (priv->text.lastAtom()->type() == Atom::String && + priv->text.lastAtom()->string().endsWith(QLatin1Char(' '))) { + priv->text.lastAtom()->chopString(); + } + append(pendingParaRightType, pendingParaString); + } + paraState = OutsideParagraph; + indexStartedPara = false; + pendingParaRightType = Atom::Nop; + pendingParaString = ""; + } +} + +void DocParser::leaveValue() +{ + leavePara(); + if (openedLists.isEmpty()) { + openedLists.push(OpenedList(OpenedList::Value)); + append(Atom::ListLeft, ATOM_LIST_VALUE); + } + else { + if (priv->text.lastAtom()->type() == Atom::Nop) + priv->text.stripLastAtom(); + append(Atom::ListItemRight, ATOM_LIST_VALUE); + } +} + +void DocParser::leaveValueList() +{ + leavePara(); + if (!openedLists.isEmpty() && + (openedLists.top().style() == OpenedList::Value)) { + if (priv->text.lastAtom()->type() == Atom::Nop) + priv->text.stripLastAtom(); + append(Atom::ListItemRight, ATOM_LIST_VALUE); + append(Atom::ListRight, ATOM_LIST_VALUE); + openedLists.pop(); + } +} + +void DocParser::leaveTableRow() +{ + if (inTableItem) { + leavePara(); + append(Atom::TableItemRight); + inTableItem = false; + } + if (inTableHeader) { + append(Atom::TableHeaderRight); + inTableHeader = false; + } + if (inTableRow) { + append(Atom::TableRowRight); + inTableRow = false; + } +} + +CodeMarker *DocParser::quoteFromFile() +{ + return Doc::quoteFromFile(location(), quoter, getArgument()); +} + +void DocParser::expandMacro(const QString &name, + const QString &def, + int numParams) +{ + if (numParams == 0) { + append(Atom::RawString, def); + } + else { + QStringList args; + QString rawString; + + for (int i = 0; i < numParams; i++) { + if (numParams == 1 || isLeftBraceAhead()) { + args << getArgument(true); + } + else { + location().warning(tr("Macro '\\%1' invoked with too few" + " arguments (expected %2, got %3)") + .arg(name).arg(numParams).arg(i)); + break; + } + } + + int j = 0; + while (j < def.size()) { + int paramNo; + if (((paramNo = def[j].unicode()) >= 1) && + (paramNo <= numParams)) { + if (!rawString.isEmpty()) { + append(Atom::RawString, rawString); + rawString = ""; + } + append(Atom::String, args[paramNo - 1]); + j += 1; + } + else { + rawString += def[j++]; + } + } + if (!rawString.isEmpty()) + append(Atom::RawString, rawString); + } +} + +QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams) +{ + if (numParams == 0) { + return def; + } + else { + QStringList args; + QString rawString; + + for (int i = 0; i < numParams; i++) { + if (numParams == 1 || isLeftBraceAhead()) { + args << getArgument(true); + } + else { + location().warning(tr("Macro '\\%1' invoked with too few" + " arguments (expected %2, got %3)") + .arg(name).arg(numParams).arg(i)); + break; + } + } + + int j = 0; + while (j < def.size()) { + int paramNo; + if (((paramNo = def[j].unicode()) >= 1) && + (paramNo <= numParams)) { + rawString += args[paramNo - 1]; + j += 1; + } + else { + rawString += def[j++]; + } + } + return rawString; + } +} + +Doc::Sections DocParser::getSectioningUnit() +{ + QString name = getOptionalArgument(); + + if (name == "part") { + return Doc::Part; + } + else if (name == "chapter") { + return Doc::Chapter; + } + else if (name == "section1") { + return Doc::Section1; + } + else if (name == "section2") { + return Doc::Section2; + } + else if (name == "section3") { + return Doc::Section3; + } + else if (name == "section4") { + return Doc::Section4; + } + else if (name.isEmpty()) { + return Doc::NoSection; + } + else { + location().warning(tr("Invalid section '%1'").arg(name)); + return Doc::NoSection; + } +} + +/*! + Gets an argument that is enclosed in braces and returns it + without the enclosing braces. On entry, the current character + is the left brace. On exit, the current character is the one + that comes afterr the right brace. + + If \a verbatim is true, extra whitespace is retained in the + returned string. Otherwise, extr whitespace is removed. + */ +QString DocParser::getBracedArgument(bool verbatim) +{ + QString arg; + int delimDepth = 0; + if (pos < (int) in.length() && in[pos] == '{') { + pos++; + while (pos < (int) in.length() && delimDepth >= 0) { + switch (in[pos].unicode()) { + case '{': + delimDepth++; + arg += QLatin1Char('{'); + pos++; + break; + case '}': + delimDepth--; + if (delimDepth >= 0) + arg += QLatin1Char('}'); + pos++; + break; + case '\\': + if (verbatim) { + arg += in[pos]; + pos++; + } + else { + pos++; + if (pos < (int) in.length()) { + if (in[pos].isLetterOrNumber()) + break; + arg += in[pos]; + if (in[pos].isSpace()) { + skipAllSpaces(); + } + else { + pos++; + } + } + } + break; + default: + arg += in[pos]; + pos++; + } + } + if (delimDepth > 0) + location().warning(tr("Missing '}'")); + } + return arg; +} + +/*! + Typically, an argument ends at the next white-space. However, + braces can be used to group words: + + {a few words} + + Also, opening and closing parentheses have to match. Thus, + + printf("%d\n", x) + + is an argument too, although it contains spaces. Finally, + trailing punctuation is not included in an argument, nor is 's. +*/ +QString DocParser::getArgument(bool verbatim) +{ + skipSpacesOrOneEndl(); + + int delimDepth = 0; + int startPos = pos; + QString arg = getBracedArgument(verbatim); + if (arg.isEmpty()) { + while ((pos < in.length()) && + ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) { + switch (in[pos].unicode()) { + case '(': + case '[': + case '{': + delimDepth++; + arg += in[pos]; + pos++; + break; + case ')': + case ']': + case '}': + delimDepth--; + if (pos == startPos || delimDepth >= 0) { + arg += in[pos]; + pos++; + } + break; + case '\\': + if (verbatim) { + arg += in[pos]; + pos++; + } + else { + pos++; + if (pos < (int) in.length()) { + if (in[pos].isLetterOrNumber()) + break; + arg += in[pos]; + if (in[pos].isSpace()) { + skipAllSpaces(); + } + else { + pos++; + } + } + } + break; + default: + arg += in[pos]; + pos++; + } + } + if ((arg.length() > 1) && + (QString(".,:;!?").indexOf(in[pos - 1]) != -1) && + !arg.endsWith("...")) { + arg.truncate(arg.length() - 1); + pos--; + } + if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") { + arg.truncate(arg.length() - 2); + pos -= 2; + } + } + return arg.simplified(); +} + +QString DocParser::getOptionalArgument() +{ + skipSpacesOrOneEndl(); + if (pos + 1 < (int) in.length() && in[pos] == '\\' && + in[pos + 1].isLetterOrNumber()) { + return ""; + } + else { + return getArgument(); + } +} + +QString DocParser::getRestOfLine() +{ + QString t; + + skipSpacesOnLine(); + + bool trailingSlash = false; + + do { + int begin = pos; + + while (pos < in.size() && in[pos] != '\n') { + if (in[pos] == '\\' && !trailingSlash) { + trailingSlash = true; + ++pos; + while ((pos < in.size()) && + in[pos].isSpace() && + (in[pos] != '\n')) + ++pos; + } + else { + trailingSlash = false; + ++pos; + } + } + + if (!t.isEmpty()) + t += " "; + t += in.mid(begin, pos - begin).simplified(); + + if (trailingSlash) { + t.chop(1); + t = t.simplified(); + } + if (pos < in.size()) + ++pos; + } while (pos < in.size() && trailingSlash); + + return t; +} + +/*! + The metacommand argument is normally the remaining text to + the right of the metacommand itself. The extra blanks are + stripped and the argument string is returned. + */ +QString DocParser::getMetaCommandArgument(const QString &cmdStr) +{ + skipSpacesOnLine(); + + int begin = pos; + int parenDepth = 0; + + while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) { + if (in.at(pos) == '(') + ++parenDepth; + else if (in.at(pos) == ')') + --parenDepth; + + ++pos; + } + if (pos == in.size() && parenDepth > 0) { + pos = begin; + location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr)); + } + + QString t = in.mid(begin, pos - begin).simplified(); + skipSpacesOnLine(); + return t; +} + +QString DocParser::getUntilEnd(int cmd) +{ + int endCmd = endCmdFor(cmd); + QRegExp rx("\\\\" + cmdName(endCmd) + "\\b"); + QString t; + int end = rx.indexIn(in, pos); + + if (end == -1) { + location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd))); + pos = in.length(); + } + else { + t = in.mid(pos, end - pos); + pos = end + rx.matchedLength(); + } + return t; +} + +QString DocParser::getCode(int cmd, CodeMarker *marker) +{ + QString code = untabifyEtc(getUntilEnd(cmd)); + int indent = indentLevel(code); + if (indent < minIndent) + minIndent = indent; + code = unindent(minIndent, code); + if (!marker) + marker = CodeMarker::markerForCode(code); + return marker->markedUpCode(code, 0, location()); +} + +/*! + Was used only for generating doxygen output. + */ +QString DocParser::getUnmarkedCode(int cmd) +{ + QString code = getUntilEnd(cmd); + return code; +} + +bool DocParser::isBlankLine() +{ + int i = pos; + + while (i < len && in[i].isSpace()) { + if (in[i] == '\n') + return true; + i++; + } + return false; +} + +bool DocParser::isLeftBraceAhead() +{ + int numEndl = 0; + int i = pos; + + while (i < len && in[i].isSpace() && numEndl < 2) { + // ### bug with '\\' + if (in[i] == '\n') + numEndl++; + i++; + } + return numEndl < 2 && i < len && in[i] == '{'; +} + +/*! + Skips to the next non-space character or EOL. + */ +void DocParser::skipSpacesOnLine() +{ + while ((pos < in.length()) && + in[pos].isSpace() && + (in[pos].unicode() != '\n')) + ++pos; +} + +/*! + Skips spaces and on EOL. + */ +void DocParser::skipSpacesOrOneEndl() +{ + int firstEndl = -1; + while (pos < (int) in.length() && in[pos].isSpace()) { + QChar ch = in[pos]; + if (ch == '\n') { + if (firstEndl == -1) { + firstEndl = pos; + } + else { + pos = firstEndl; + break; + } + } + pos++; + } +} + +void DocParser::skipAllSpaces() +{ + while (pos < len && in[pos].isSpace()) + pos++; +} + +void DocParser::skipToNextPreprocessorCommand() +{ + QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + QLatin1Char('|') + + cmdName(CMD_ELSE) + QLatin1Char('|') + + cmdName(CMD_ENDIF) + ")\\b"); + int end = rx.indexIn(in, pos + 1); // ### + 1 necessary? + + if (end == -1) + pos = in.length(); + else + pos = end; +} + +int DocParser::endCmdFor(int cmd) +{ + switch (cmd) { + case CMD_ABSTRACT: + return CMD_ENDABSTRACT; + case CMD_BADCODE: + return CMD_ENDCODE; + case CMD_CHAPTER: + return CMD_ENDCHAPTER; + case CMD_CODE: + return CMD_ENDCODE; + case CMD_DIV: + return CMD_ENDDIV; + case CMD_QML: + return CMD_ENDQML; + case CMD_QMLTEXT: + return CMD_ENDQMLTEXT; + case CMD_JS: + return CMD_ENDJS; + case CMD_FOOTNOTE: + return CMD_ENDFOOTNOTE; + case CMD_LEGALESE: + return CMD_ENDLEGALESE; + case CMD_LINK: + return CMD_ENDLINK; + case CMD_LIST: + return CMD_ENDLIST; + case CMD_NEWCODE: + return CMD_ENDCODE; + case CMD_OLDCODE: + return CMD_NEWCODE; + case CMD_OMIT: + return CMD_ENDOMIT; + case CMD_PART: + return CMD_ENDPART; + case CMD_QUOTATION: + return CMD_ENDQUOTATION; + case CMD_RAW: + return CMD_ENDRAW; + case CMD_SECTION1: + return CMD_ENDSECTION1; + case CMD_SECTION2: + return CMD_ENDSECTION2; + case CMD_SECTION3: + return CMD_ENDSECTION3; + case CMD_SECTION4: + return CMD_ENDSECTION4; + case CMD_SIDEBAR: + return CMD_ENDSIDEBAR; + case CMD_TABLE: + return CMD_ENDTABLE; + case CMD_TOPICREF: + return CMD_ENDTOPICREF; + case CMD_MAPREF: + return CMD_ENDMAPREF; + default: + return cmd; + } +} + +QString DocParser::cmdName(int cmd) +{ + return *cmds[cmd].alias; +} + +QString DocParser::endCmdName(int cmd) +{ + return cmdName(endCmdFor(cmd)); +} + +QString DocParser::untabifyEtc(const QString& str) +{ + QString result; + result.reserve(str.length()); + int column = 0; + + for (int i = 0; i < str.length(); i++) { + const QChar c = str.at(i); + if (c == QLatin1Char('\r')) + continue; + if (c == QLatin1Char('\t')) { + result += " " + (column % tabSize); + column = ((column / tabSize) + 1) * tabSize; + continue; + } + if (c == QLatin1Char('\n')) { + while (result.endsWith(QLatin1Char(' '))) + result.chop(1); + result += c; + column = 0; + continue; + } + result += c; + column++; + } + + while (result.endsWith("\n\n")) + result.truncate(result.length() - 1); + while (result.startsWith(QLatin1Char('\n'))) + result = result.mid(1); + + return result; +} + +int DocParser::indentLevel(const QString& str) +{ + int minIndent = INT_MAX; + int column = 0; + + for (int i = 0; i < (int) str.length(); i++) { + if (str[i] == '\n') { + column = 0; + } + else { + if (str[i] != ' ' && column < minIndent) + minIndent = column; + column++; + } + } + return minIndent; +} + +QString DocParser::unindent(int level, const QString& str) +{ + if (level == 0) + return str; + + QString t; + int column = 0; + + for (int i = 0; i < (int) str.length(); i++) { + if (str[i] == QLatin1Char('\n')) { + t += '\n'; + column = 0; + } + else { + if (column >= level) + t += str[i]; + column++; + } + } + return t; +} + +QString DocParser::slashed(const QString& str) +{ + QString result = str; + result.replace(QLatin1Char('/'), "\\/"); + return QLatin1Char('/') + result + QLatin1Char('/'); +} + +#define COMMAND_BRIEF Doc::alias("brief") +#define COMMAND_QMLBRIEF Doc::alias("qmlbrief") + +Doc::Doc(const Location& start_loc, + const Location& end_loc, + const QString& source, + const QSet<QString>& metaCommandSet) +{ + priv = new DocPrivate(start_loc,end_loc,source); + DocParser parser; + parser.parse(source,priv,metaCommandSet,QSet<QString>()); +} + +/*! + Parse the qdoc comment \a source. Build up a list of all the topic + commands found including their arguments. This constructor is used + when there can be more than one topic command in theqdoc comment. + Normally, there is only one topic command in a qdoc comment, but in + QML documentation, there is the case where the qdoc \e{qmlproperty} + command can appear multiple times in a qdoc comment. + */ +Doc::Doc(const Location& start_loc, + const Location& end_loc, + const QString& source, + const QSet<QString>& metaCommandSet, + const QSet<QString>& topics) +{ + priv = new DocPrivate(start_loc,end_loc,source); + DocParser parser; + parser.parse(source,priv,metaCommandSet,topics); +} + +Doc::Doc(const Doc& doc) + : priv(0) +{ + operator=(doc); +} + +Doc::~Doc() +{ + if (priv && priv->deref()) + delete priv; +} + +Doc &Doc::operator=(const Doc& doc) +{ + if (doc.priv) + doc.priv->ref(); + if (priv && priv->deref()) + delete priv; + priv = doc.priv; + return *this; +} + +void Doc::renameParameters(const QStringList &oldNames, + const QStringList &newNames) +{ + if (priv && oldNames != newNames) { + detach(); + + priv->params = newNames.toSet(); + + Atom *atom = priv->text.firstAtom(); + while (atom) { + if (atom->type() == Atom::FormattingLeft + && atom->string() == ATOM_FORMATTING_PARAMETER) { + atom = atom->next(); + if (!atom) + return; + int index = oldNames.indexOf(atom->string()); + if (index != -1 && index < newNames.count()) + atom->setString(newNames.at(index)); + } + atom = atom->next(); + } + } +} + +void Doc::simplifyEnumDoc() +{ + if (priv) { + if (priv->isEnumDocSimplifiable()) { + detach(); + + Text newText; + + Atom *atom = priv->text.firstAtom(); + while (atom) { + if ((atom->type() == Atom::ListLeft) && + (atom->string() == ATOM_LIST_VALUE)) { + while (atom && ((atom->type() != Atom::ListRight) || + (atom->string() != ATOM_LIST_VALUE))) + atom = atom->next(); + if (atom) + atom = atom->next(); + } + else { + newText << *atom; + atom = atom->next(); + } + } + priv->text = newText; + } + } +} + +void Doc::setBody(const Text &text) +{ + detach(); + priv->text = text; +} + +/*! + Returns the starting location of a qdoc comment. + */ +const Location &Doc::location() const +{ + static const Location dummy; + return priv == 0 ? dummy : priv->start_loc; +} + +const QString &Doc::source() const +{ + static QString null; + return priv == 0 ? null : priv->src; +} + +bool Doc::isEmpty() const +{ + return priv == 0 || priv->src.isEmpty(); +} + +const Text& Doc::body() const +{ + static const Text dummy; + return priv == 0 ? dummy : priv->text; +} + +Text Doc::briefText(bool inclusive) const +{ + return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive); +} + +Text Doc::trimmedBriefText(const QString &className) const +{ + QString classNameOnly = className; + if (className.contains("::")) + classNameOnly = className.split("::").last(); + + Text originalText = briefText(); + Text resultText; + const Atom *atom = originalText.firstAtom(); + if (atom) { + QString briefStr; + QString whats; + bool standardWording = true; + + /* + This code is really ugly. The entire \brief business + should be rethought. + */ + while (atom) { + if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) { + briefStr += atom->string(); + } + atom = atom->next(); + } + + QStringList w = briefStr.split(QLatin1Char(' ')); + if (!w.isEmpty() && w.first() == "Returns") { + } + else { + if (!w.isEmpty() && w.first() == "The") + w.removeFirst(); + else { + location().warning( + tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')") + .arg(COMMAND_BRIEF).arg(className)); + standardWording = false; + } + + if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly)) + w.removeFirst(); + else { + location().warning( + tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')") + .arg(COMMAND_BRIEF).arg(className).arg(className)); + standardWording = false; + } + + if (!w.isEmpty() && ((w.first() == "class") || + (w.first() == "function") || + (w.first() == "macro") || + (w.first() == "widget") || + (w.first() == "namespace") || + (w.first() == "header"))) + w.removeFirst(); + else { + location().warning( + tr("Nonstandard wording in '\\%1' text for '%2' (" + "expected 'class', 'function', 'macro', 'widget', " + "'namespace' or 'header')") + .arg(COMMAND_BRIEF).arg(className)); + standardWording = false; + } + + if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides")) + w.removeFirst(); + + if (!w.isEmpty() && (w.first() == "a" || w.first() == "an")) + w.removeFirst(); + } + + whats = w.join(" "); + + if (whats.endsWith(QLatin1Char('.'))) + whats.truncate(whats.length() - 1); + + if (whats.isEmpty()) { + location().warning( + tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)") + .arg(COMMAND_BRIEF).arg(className)); + standardWording = false; + } + else + whats[0] = whats[0].toUpper(); + + // ### move this once \brief is abolished for properties + if (standardWording) + resultText << whats; + } + return resultText; +} + +Text Doc::legaleseText() const +{ + if (priv == 0 || !priv->hasLegalese) + return Text(); + else + return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight); +} + +const QString& Doc::baseName() const +{ + static QString null; + if (priv == 0 || priv->extra == 0) { + return null; + } + else { + return priv->extra->baseName; + } +} + +Doc::Sections Doc::granularity() const +{ + if (priv == 0 || priv->extra == 0) { + return DocPrivateExtra().granularity; + } + else { + return priv->extra->granularity; + } +} + +const QSet<QString> &Doc::parameterNames() const +{ + return priv == 0 ? *null_Set_QString() : priv->params; +} + +const QStringList &Doc::enumItemNames() const +{ + return priv == 0 ? *null_QStringList() : priv->enumItemList; +} + +const QStringList &Doc::omitEnumItemNames() const +{ + return priv == 0 ? *null_QStringList() : priv->omitEnumItemList; +} + +const QSet<QString> &Doc::metaCommandsUsed() const +{ + return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed; +} + +/*! + Returns a reference to the list of topic commands used in the + current qdoc comment. Normally there is only one, but there + can be multiple \e{qmlproperty} commands, for example. + */ +const TopicList& Doc::topicsUsed() const +{ + return priv == 0 ? *nullTopicList() : priv->topics; +} + +QStringList Doc::metaCommandArgs(const QString& metacommand) const +{ + return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand); +} + +const QList<Text> &Doc::alsoList() const +{ + return priv == 0 ? *null_QList_Text() : priv->alsoList; +} + +bool Doc::hasTableOfContents() const +{ + return priv && priv->extra && !priv->extra->tableOfContents.isEmpty(); +} + +bool Doc::hasKeywords() const +{ + return priv && priv->extra && !priv->extra->keywords.isEmpty(); +} + +bool Doc::hasTargets() const +{ + return priv && priv->extra && !priv->extra->targets.isEmpty(); +} + +const QList<Atom *> &Doc::tableOfContents() const +{ + priv->constructExtra(); + return priv->extra->tableOfContents; +} + +const QList<int> &Doc::tableOfContentsLevels() const +{ + priv->constructExtra(); + return priv->extra->tableOfContentsLevels; +} + +const QList<Atom *> &Doc::keywords() const +{ + priv->constructExtra(); + return priv->extra->keywords; +} + +const QList<Atom *> &Doc::targets() const +{ + priv->constructExtra(); + return priv->extra->targets; +} + +const QStringMultiMap &Doc::metaTagMap() const +{ + return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap(); +} + +void Doc::initialize(const Config& config) +{ + DocParser::tabSize = config.getInt(CONFIG_TABSIZE); + DocParser::exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES); + DocParser::exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS); + DocParser::sourceFiles = config.getCleanPathList(CONFIG_SOURCES); + DocParser::sourceDirs = config.getCleanPathList(CONFIG_SOURCEDIRS); + DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION); + + QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY); + + QStringMap reverseAliasMap; + + QSet<QString> commands = config.subVars(CONFIG_ALIAS); + QSet<QString>::ConstIterator c = commands.begin(); + while (c != commands.end()) { + QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c); + if (reverseAliasMap.contains(alias)) { + config.lastLocation().warning(tr("Command name '\\%1' cannot stand" + " for both '\\%2' and '\\%3'") + .arg(alias) + .arg(reverseAliasMap[alias]) + .arg(*c)); + } + else { + reverseAliasMap.insert(alias, *c); + } + aliasMap()->insert(*c, alias); + ++c; + } + + int i = 0; + while (cmds[i].english) { + cmds[i].alias = new QString(alias(cmds[i].english)); + cmdHash()->insert(*cmds[i].alias, cmds[i].no); + + if (cmds[i].no != i) + Location::internalError(tr("command %1 missing").arg(i)); + i++; + } + + QSet<QString> macroNames = config.subVars(CONFIG_MACRO); + QSet<QString>::ConstIterator n = macroNames.begin(); + while (n != macroNames.end()) { + QString macroDotName = CONFIG_MACRO + Config::dot + *n; + Macro macro; + macro.numParams = -1; + macro.defaultDef = config.getString(macroDotName); + if (!macro.defaultDef.isEmpty()) { + macro.defaultDefLocation = config.lastLocation(); + macro.numParams = Config::numParams(macro.defaultDef); + } + bool silent = false; + + QSet<QString> formats = config.subVars(macroDotName); + QSet<QString>::ConstIterator f = formats.begin(); + while (f != formats.end()) { + QString def = config.getString(macroDotName + Config::dot + *f); + if (!def.isEmpty()) { + macro.otherDefs.insert(*f, def); + int m = Config::numParams(def); + if (macro.numParams == -1) { + macro.numParams = m; + } + else if (macro.numParams != m) { + if (!silent) { + QString other = tr("default"); + if (macro.defaultDef.isEmpty()) + other = macro.otherDefs.begin().key(); + config.lastLocation().warning(tr("Macro '\\%1' takes" + " inconsistent number" + " of arguments (%2" + " %3, %4 %5)") + .arg(*n) + .arg(*f) + .arg(m) + .arg(other) + .arg(macro.numParams)); + silent = true; + } + if (macro.numParams < m) + macro.numParams = m; + } + } + ++f; + } + + if (macro.numParams != -1) + macroHash()->insert(*n, macro); + ++n; + } +} + +void Doc::terminate() +{ + DocParser::exampleFiles.clear(); + DocParser::exampleDirs.clear(); + DocParser::sourceFiles.clear(); + DocParser::sourceDirs.clear(); + aliasMap()->clear(); + cmdHash()->clear(); + macroHash()->clear(); + + int i = 0; + while (cmds[i].english) { + delete cmds[i].alias; + cmds[i].alias = 0; + ++i; + } +} + +QString Doc::alias(const QString &english) +{ + return aliasMap()->value(english, english); +} + +/*! + Trims the deadwood out of \a str. i.e., this function + cleans up \a str. + */ +void Doc::trimCStyleComment(Location& location, QString& str) +{ + QString cleaned; + Location m = location; + bool metAsterColumn = true; + int asterColumn = location.columnNo() + 1; + int i; + + for (i = 0; i < (int) str.length(); i++) { + if (m.columnNo() == asterColumn) { + if (str[i] != '*') + break; + cleaned += ' '; + metAsterColumn = true; + } + else { + if (str[i] == '\n') { + if (!metAsterColumn) + break; + metAsterColumn = false; + } + cleaned += str[i]; + } + m.advance(str[i]); + } + if (cleaned.length() == str.length()) + str = cleaned; + + for (int i = 0; i < 3; i++) + location.advance(str[i]); + str = str.mid(3, str.length() - 5); +} + +CodeMarker *Doc::quoteFromFile(const Location &location, + Quoter "er, + const QString &fileName) +{ + quoter.reset(); + + QString code; + + QString userFriendlyFilePath; + QString filePath = Config::findFile(location, + DocParser::exampleFiles, + DocParser::exampleDirs, + fileName, userFriendlyFilePath); + if (filePath.isEmpty()) { + location.warning(tr("Cannot find file to quote from: '%1'").arg(fileName)); + } + else { + QFile inFile(filePath); + if (!inFile.open(QFile::ReadOnly)) { + location.warning(tr("Cannot open file to quote from: '%1'").arg(userFriendlyFilePath)); + } + else { + QTextStream inStream(&inFile); + code = DocParser::untabifyEtc(inStream.readAll()); + } + } + + QString dirPath = QFileInfo(filePath).path(); + CodeMarker *marker = CodeMarker::markerForFileName(fileName); + quoter.quoteFromFile(userFriendlyFilePath, + code, + marker->markedUpCode(code, 0, location)); + return marker; +} + +QString Doc::canonicalTitle(const QString &title) +{ + // The code below is equivalent to the following chunk, but _much_ + // faster (accounts for ~10% of total running time) + // + // QRegExp attributeExpr("[^A-Za-z0-9]+"); + // QString result = title.toLower(); + // result.replace(attributeExpr, " "); + // result = result.simplified(); + // result.replace(QLatin1Char(' '), QLatin1Char('-')); + + QString result; + result.reserve(title.size()); + + bool dashAppended = false; + bool begun = false; + int lastAlnum = 0; + for (int i = 0; i != title.size(); ++i) { + uint c = title.at(i).unicode(); + if (c >= 'A' && c <= 'Z') + c -= 'A' - 'a'; + bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); + if (alnum) { + result += QLatin1Char(c); + begun = true; + dashAppended = false; + lastAlnum = result.size(); + } + else if (!dashAppended) { + if (begun) + result += QLatin1Char('-'); + dashAppended = true; + } + } + result.truncate(lastAlnum); + return result; +} + +void Doc::detach() +{ + if (!priv) { + priv = new DocPrivate; + return; + } + if (priv->count == 1) + return; + + --priv->count; + + DocPrivate *newPriv = new DocPrivate(*priv); + newPriv->count = 1; + if (priv->extra) + newPriv->extra = new DocPrivateExtra(*priv->extra); + + priv = newPriv; +} + +/*! + The destructor deletes all the sub-TopicRefs. + */ +TopicRef::~TopicRef() +{ + foreach (DitaRef* t, subrefs_) { + delete t; + } +} + +/*! + Returns a reference to the structure that will be used + for generating a DITA mao. + */ +const DitaRefList& Doc::ditamap() const { return priv->ditamap_; } + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/doc.h b/src/tools/qdoc/doc.h new file mode 100644 index 0000000000..d3fd2dbb4d --- /dev/null +++ b/src/tools/qdoc/doc.h @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + doc.h +*/ + +#ifndef DOC_H +#define DOC_H + +#include <QSet> +#include <QString> +#include <QMap> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Atom; +class CodeMarker; +class Config; +class DocPrivate; +class Quoter; +class Text; +class FakeNode; +class DitaRef; + +typedef QMap<QString, QStringList> QCommandMap; +typedef QMap<QString, QString> QStringMap; +typedef QMultiMap<QString, QString> QStringMultiMap; + +struct Topic +{ + QString topic; + QString args; + Topic(QString& t, QString a) : topic(t), args(a) { } +}; +typedef QList<Topic> TopicList; + +typedef QList<DitaRef*> DitaRefList; + +class DitaRef +{ +public: + DitaRef() { } + virtual ~DitaRef() { } + + const QString& navtitle() const { return navtitle_; } + const QString& href() const { return href_; } + void setNavtitle(const QString& t) { navtitle_ = t; } + void setHref(const QString& t) { href_ = t; } + virtual bool isMapRef() const = 0; + virtual const DitaRefList* subrefs() const { return 0; } + virtual void appendSubref(DitaRef* ) { } + +private: + QString navtitle_; + QString href_; +}; + +class TopicRef : public DitaRef +{ +public: + TopicRef() { } + ~TopicRef(); + + virtual bool isMapRef() const { return false; } + virtual const DitaRefList* subrefs() const { return &subrefs_; } + virtual void appendSubref(DitaRef* t) { subrefs_.append(t); } + +private: + DitaRefList subrefs_; +}; + +class MapRef : public DitaRef +{ +public: + MapRef() { } + ~MapRef() { } + + virtual bool isMapRef() const { return true; } +}; + +class Doc +{ +public: + // the order is important + enum Sections { + NoSection = -2, + Part = -1, + Chapter = 1, + Section1 = 1, + Section2 = 2, + Section3 = 3, + Section4 = 4 + }; + + Doc() : priv(0) {} + Doc(const Location &start_loc, + const Location &end_loc, + const QString &source, + const QSet<QString> &metaCommandSet); + Doc(const Location& start_loc, + const Location& end_loc, + const QString& source, + const QSet<QString>& metaCommandSet, + const QSet<QString>& topics); + Doc(const Doc &doc); + ~Doc(); + + Doc& operator=( const Doc& doc ); + + void renameParameters(const QStringList &oldNames, + const QStringList &newNames); + void simplifyEnumDoc(); + void setBody(const Text &body); + const DitaRefList& ditamap() const; + + const Location &location() const; + bool isEmpty() const; + const QString& source() const; + const Text& body() const; + Text briefText(bool inclusive = false) const; + Text trimmedBriefText(const QString &className) const; + Text legaleseText() const; + const QString& baseName() const; + Sections granularity() const; + const QSet<QString> ¶meterNames() const; + const QStringList &enumItemNames() const; + const QStringList &omitEnumItemNames() const; + const QSet<QString> &metaCommandsUsed() const; + const TopicList& topicsUsed() const; + QStringList metaCommandArgs( const QString& metaCommand ) const; + const QList<Text> &alsoList() const; + bool hasTableOfContents() const; + bool hasKeywords() const; + bool hasTargets() const; + const QList<Atom *> &tableOfContents() const; + const QList<int> &tableOfContentsLevels() const; + const QList<Atom *> &keywords() const; + const QList<Atom *> &targets() const; + const QStringMultiMap &metaTagMap() const; + + static void initialize( const Config &config ); + static void terminate(); + static QString alias( const QString &english ); + static void trimCStyleComment( Location& location, QString& str ); + static CodeMarker *quoteFromFile(const Location &location, + Quoter "er, + const QString &fileName); + static QString canonicalTitle(const QString &title); + +private: + void detach(); + DocPrivate *priv; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/doc/config/compat.qdocconf b/src/tools/qdoc/doc/config/compat.qdocconf new file mode 100644 index 0000000000..0b59629ec3 --- /dev/null +++ b/src/tools/qdoc/doc/config/compat.qdocconf @@ -0,0 +1,31 @@ +alias.i = e +alias.include = input + +macro.0 = "\\\\0" +macro.b = "\\\\b" +macro.n = "\\\\n" +macro.r = "\\\\r" +macro.i = "\\o" +macro.i11 = "\\o{1,1}" +macro.i12 = "\\o{1,2}" +macro.i13 = "\\o{1,3}" +macro.i14 = "\\o{1,4}" +macro.i15 = "\\o{1,5}" +macro.i16 = "\\o{1,6}" +macro.i17 = "\\o{1,7}" +macro.i18 = "\\o{1,8}" +macro.i19 = "\\o{1,9}" +macro.i21 = "\\o{2,1}" +macro.i31 = "\\o{3,1}" +macro.i41 = "\\o{4,1}" +macro.i51 = "\\o{5,1}" +macro.i61 = "\\o{6,1}" +macro.i71 = "\\o{7,1}" +macro.i81 = "\\o{8,1}" +macro.i91 = "\\o{9,1}" +macro.img = "\\image" +macro.endquote = "\\endquotation" +macro.relatesto = "\\relates" + +spurious = "Missing comma in .*" \ + "Missing pattern .*" diff --git a/src/tools/qdoc/doc/config/config.pro b/src/tools/qdoc/doc/config/config.pro new file mode 100644 index 0000000000..ee7aea8a04 --- /dev/null +++ b/src/tools/qdoc/doc/config/config.pro @@ -0,0 +1,13 @@ +SOURCES = config.pro \ + compat.qdocconf \ + qdoc-online.qdocconf \ + qt-defines.qdocconf \ + qt-html-templates.qdocconf \ + qdoc-project.qdocconf \ + qt-html-default-styles.qdocconf \ + qdoc.qdocconf \ + qt-html-online-styles.qdocconf \ + macros.qdocconf \ + qt-cpp-ignore.qdocconf \ + qt-html-templates-online.qdocconf + diff --git a/src/tools/qdoc/doc/config/images/arrow_down.png b/src/tools/qdoc/doc/config/images/arrow_down.png Binary files differnew file mode 100644 index 0000000000..9d01e97f6a --- /dev/null +++ b/src/tools/qdoc/doc/config/images/arrow_down.png diff --git a/src/tools/qdoc/doc/config/images/bg_l.png b/src/tools/qdoc/doc/config/images/bg_l.png Binary files differnew file mode 100755 index 0000000000..90b1da10b9 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bg_l.png diff --git a/src/tools/qdoc/doc/config/images/bg_l_blank.png b/src/tools/qdoc/doc/config/images/bg_l_blank.png Binary files differnew file mode 100755 index 0000000000..5a9673d81b --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bg_l_blank.png diff --git a/src/tools/qdoc/doc/config/images/bg_ll_blank.png b/src/tools/qdoc/doc/config/images/bg_ll_blank.png Binary files differnew file mode 100644 index 0000000000..95a1c45e04 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bg_ll_blank.png diff --git a/src/tools/qdoc/doc/config/images/bg_r.png b/src/tools/qdoc/doc/config/images/bg_r.png Binary files differnew file mode 100755 index 0000000000..f0fb121dea --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bg_r.png diff --git a/src/tools/qdoc/doc/config/images/bg_ul_blank.png b/src/tools/qdoc/doc/config/images/bg_ul_blank.png Binary files differnew file mode 100644 index 0000000000..70512614cc --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bg_ul_blank.png diff --git a/src/tools/qdoc/doc/config/images/box_bg.png b/src/tools/qdoc/doc/config/images/box_bg.png Binary files differnew file mode 100755 index 0000000000..3322f923f8 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/box_bg.png diff --git a/src/tools/qdoc/doc/config/images/breadcrumb.png b/src/tools/qdoc/doc/config/images/breadcrumb.png Binary files differnew file mode 100755 index 0000000000..0ded5514d2 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/breadcrumb.png diff --git a/src/tools/qdoc/doc/config/images/bullet_dn.png b/src/tools/qdoc/doc/config/images/bullet_dn.png Binary files differnew file mode 100644 index 0000000000..f7762472e2 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bullet_dn.png diff --git a/src/tools/qdoc/doc/config/images/bullet_gt.png b/src/tools/qdoc/doc/config/images/bullet_gt.png Binary files differnew file mode 100755 index 0000000000..7561b4edc4 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bullet_gt.png diff --git a/src/tools/qdoc/doc/config/images/bullet_sq.png b/src/tools/qdoc/doc/config/images/bullet_sq.png Binary files differnew file mode 100755 index 0000000000..a84845e3c7 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bullet_sq.png diff --git a/src/tools/qdoc/doc/config/images/bullet_up.png b/src/tools/qdoc/doc/config/images/bullet_up.png Binary files differnew file mode 100644 index 0000000000..7de2f06954 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/bullet_up.png diff --git a/src/tools/qdoc/doc/config/images/feedbackground.png b/src/tools/qdoc/doc/config/images/feedbackground.png Binary files differnew file mode 100755 index 0000000000..3a38d995d7 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/feedbackground.png diff --git a/src/tools/qdoc/doc/config/images/header_bg.png b/src/tools/qdoc/doc/config/images/header_bg.png Binary files differnew file mode 100644 index 0000000000..a436aa61ef --- /dev/null +++ b/src/tools/qdoc/doc/config/images/header_bg.png diff --git a/src/tools/qdoc/doc/config/images/horBar.png b/src/tools/qdoc/doc/config/images/horBar.png Binary files differnew file mode 100755 index 0000000000..100fe91c6c --- /dev/null +++ b/src/tools/qdoc/doc/config/images/horBar.png diff --git a/src/tools/qdoc/doc/config/images/page.png b/src/tools/qdoc/doc/config/images/page.png Binary files differnew file mode 100644 index 0000000000..1db151bd31 --- /dev/null +++ b/src/tools/qdoc/doc/config/images/page.png diff --git a/src/tools/qdoc/doc/config/images/page_bg.png b/src/tools/qdoc/doc/config/images/page_bg.png Binary files differnew file mode 100755 index 0000000000..9b3bd999df --- /dev/null +++ b/src/tools/qdoc/doc/config/images/page_bg.png diff --git a/src/tools/qdoc/doc/config/images/spinner.gif b/src/tools/qdoc/doc/config/images/spinner.gif Binary files differnew file mode 100644 index 0000000000..1ed786f2ec --- /dev/null +++ b/src/tools/qdoc/doc/config/images/spinner.gif diff --git a/src/tools/qdoc/doc/config/images/sprites-combined.png b/src/tools/qdoc/doc/config/images/sprites-combined.png Binary files differnew file mode 100755 index 0000000000..3a48b21f6b --- /dev/null +++ b/src/tools/qdoc/doc/config/images/sprites-combined.png diff --git a/src/tools/qdoc/doc/config/macros.qdocconf b/src/tools/qdoc/doc/config/macros.qdocconf new file mode 100644 index 0000000000..2dbc9c1f91 --- /dev/null +++ b/src/tools/qdoc/doc/config/macros.qdocconf @@ -0,0 +1,40 @@ +macro.aacute.HTML = "á" +macro.Aring.HTML = "Å" +macro.aring.HTML = "å" +macro.Auml.HTML = "Ä" +macro.author = "\\bold{Author:}" +macro.br.HTML = "<br />" +macro.BR.HTML = "<br />" +macro.copyright.HTML = "©" +macro.eacute.HTML = "é" +macro.gui = "\\bold" +macro.hr.HTML = "<hr />" +macro.iacute.HTML = "í" +macro.key = "\\bold" +macro.menu = "\\bold" +macro.note = "\\bold{Note:}" +macro.oslash.HTML = "ø" +macro.ouml.HTML = "ö" +macro.QA = "\\e{Qt Assistant}" +macro.QD = "\\e{Qt Designer}" +macro.QL = "\\e{Qt Linguist}" +macro.QQV = "\\e{Qt QML Viewer}" +macro.param = "\\e" +macro.raisedaster.HTML = "<sup>*</sup>" +macro.rarrow.HTML = "→" +macro.reg.HTML = "<sup>®</sup>" +macro.return = "Returns" +macro.starslash = "\\c{*/}" +macro.begincomment = "\\c{/*}" +macro.endcomment = "\\c{*/}" +macro.uuml.HTML = "ü" +macro.mdash.HTML = "—" +macro.pi.HTML = "Π" +macro.beginqdoc.HTML = "/*!" +macro.endqdoc.HTML = "*/" + +macro.beginfloatleft.HTML = "<div style=\"float: left; margin-right: 2em\">" +macro.beginfloatright.HTML = "<div style=\"float: right; margin-left: 2em\">" +macro.endfloat.HTML = "</div>" +macro.clearfloat.HTML = "<br style=\"clear: both\" />" +macro.emptyspan.HTML = "<span></span>" diff --git a/src/tools/qdoc/doc/config/qdoc-online.qdocconf b/src/tools/qdoc/doc/config/qdoc-online.qdocconf new file mode 100644 index 0000000000..7fd8ed59f4 --- /dev/null +++ b/src/tools/qdoc/doc/config/qdoc-online.qdocconf @@ -0,0 +1,2 @@ +include(qdoc-project.qdocconf) +include(qt-html-templates-online.qdocconf) diff --git a/src/tools/qdoc/doc/config/qdoc-project.qdocconf b/src/tools/qdoc/doc/config/qdoc-project.qdocconf new file mode 100644 index 0000000000..d85058cd6a --- /dev/null +++ b/src/tools/qdoc/doc/config/qdoc-project.qdocconf @@ -0,0 +1,48 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-defines.qdocconf) + +indexes = $$MODULE_BUILD_TREE/doc/html/qt.index + +sourceencoding = UTF-8 +outputencoding = UTF-8 +naturallanguage = en_US + +project = QDoc +description = QDoc3 Manual +url = http://doc.qt.nokia.com/qdoc + +sources.fileextensions = "*.cpp *.qdoc *.mm *.qml" +headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx" +examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp *.qml *.qdoc *.qdocconf *.qdocinc" +examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng" + +sourcedirs = .. + +exampledirs = .. \ + ../examples \ + ../../../../examples \ + config + +imagedirs = ../../../doc/src/templates/images \ + images + +outputdir = $$MODULE_BUILD_TREE/tools/qdoc3/doc/html +tagfile = $$MODULE_BUILD_TREE/tools/qdoc3/doc/html/qdoc.tags + +qhp.projects = QDoc + +qhp.QDoc.file = qdoc.qhp +qhp.QDoc.namespace = com.trolltech.qdoc +qhp.QDoc.virtualFolder = qdoc +qhp.QDoc.indexTitle = QDoc Manual - Table of Contents +qhp.QDoc.indexRoot = + +qhp.QDoc.filterAttributes = qdoc qtrefdoc +qhp.QDoc.customFilters.QDoc.name = QDoc +qhp.QDoc.customFilters.QDoc.filterAttributes = qdoc +qhp.QDoc.subprojects = overviews +qhp.QDoc.subprojects.overviews.title = Overviews +qhp.QDoc.subprojects.overviews.indexTitle = All Overviews and HOWTOs +qhp.QDoc.subprojects.overviews.selectors = fake:page,group,module diff --git a/src/tools/qdoc/doc/config/qdoc.qdocconf b/src/tools/qdoc/doc/config/qdoc.qdocconf new file mode 100644 index 0000000000..c238abef88 --- /dev/null +++ b/src/tools/qdoc/doc/config/qdoc.qdocconf @@ -0,0 +1,2 @@ +include(qdoc-project.qdocconf) +include(qt-html-templates.qdocconf) diff --git a/src/tools/qdoc/doc/config/qt-cpp-ignore.qdocconf b/src/tools/qdoc/doc/config/qt-cpp-ignore.qdocconf new file mode 100644 index 0000000000..21efcf9940 --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-cpp-ignore.qdocconf @@ -0,0 +1,98 @@ +Cpp.ignoretokens = QAXFACTORY_EXPORT \ + QDESIGNER_COMPONENTS_LIBRARY \ + QDESIGNER_EXTENSION_LIBRARY \ + QDESIGNER_SDK_LIBRARY \ + QDESIGNER_SHARED_LIBRARY \ + QDESIGNER_UILIB_LIBRARY \ + QM_EXPORT_CANVAS \ + QM_EXPORT_DNS \ + QM_EXPORT_DOM \ + QM_EXPORT_FTP \ + QM_EXPORT_HTTP \ + QM_EXPORT_ICONVIEW \ + QM_EXPORT_NETWORK \ + QM_EXPORT_OPENGL \ + QM_EXPORT_OPENVG \ + QM_EXPORT_SQL \ + QM_EXPORT_TABLE \ + QM_EXPORT_WORKSPACE \ + QM_EXPORT_XML \ + QT_ASCII_CAST_WARN \ + QT_ASCII_CAST_WARN_CONSTRUCTOR \ + QT_BEGIN_HEADER \ + QT_DESIGNER_STATIC \ + QT_END_HEADER \ + QT_FASTCALL \ + QT_WIDGET_PLUGIN_EXPORT \ + Q_COMPAT_EXPORT \ + Q_CORE_EXPORT \ + Q_CORE_EXPORT_INLINE \ + Q_EXPLICIT \ + Q_EXPORT \ + Q_EXPORT_CODECS_CN \ + Q_EXPORT_CODECS_JP \ + Q_EXPORT_CODECS_KR \ + Q_EXPORT_PLUGIN \ + Q_GFX_INLINE \ + Q_AUTOTEST_EXPORT \ + Q_GUI_EXPORT \ + Q_GUI_EXPORT_INLINE \ + Q_GUI_EXPORT_STYLE_CDE \ + Q_GUI_EXPORT_STYLE_COMPACT \ + Q_GUI_EXPORT_STYLE_MAC \ + Q_GUI_EXPORT_STYLE_MOTIF \ + Q_GUI_EXPORT_STYLE_MOTIFPLUS \ + Q_GUI_EXPORT_STYLE_PLATINUM \ + Q_GUI_EXPORT_STYLE_POCKETPC \ + Q_GUI_EXPORT_STYLE_SGI \ + Q_GUI_EXPORT_STYLE_WINDOWS \ + Q_GUI_EXPORT_STYLE_WINDOWSXP \ + QHELP_EXPORT \ + Q_INLINE_TEMPLATE \ + Q_INTERNAL_WIN_NO_THROW \ + Q_LOCATION_EXPORT \ + Q_NETWORK_EXPORT \ + Q_OPENGL_EXPORT \ + Q_OPENVG_EXPORT \ + Q_OUTOFLINE_TEMPLATE \ + Q_SQL_EXPORT \ + Q_SVG_EXPORT \ + Q_SCRIPT_EXPORT \ + Q_SCRIPTTOOLS_EXPORT \ + Q_TESTLIB_EXPORT \ + Q_XML_EXPORT \ + Q_XMLSTREAM_EXPORT \ + Q_XMLPATTERNS_EXPORT \ + QDBUS_EXPORT \ + Q_DBUS_EXPORT \ + QT_BEGIN_NAMESPACE \ + QT_BEGIN_INCLUDE_NAMESPACE \ + QT_END_NAMESPACE \ + QT_END_INCLUDE_NAMESPACE \ + PHONON_EXPORT \ + Q_DECLARATIVE_EXPORT \ + Q_GADGET \ + QWEBKIT_EXPORT \ + Q_INVOKABLE +Cpp.ignoredirectives = Q_DECLARE_HANDLE \ + Q_DECLARE_INTERFACE \ + Q_DECLARE_METATYPE \ + Q_DECLARE_OPERATORS_FOR_FLAGS \ + Q_DECLARE_PRIVATE \ + Q_DECLARE_PUBLIC \ + Q_DECLARE_SHARED \ + Q_DECLARE_TR_FUNCTIONS \ + Q_DECLARE_TYPEINFO \ + Q_DISABLE_COPY \ + QT_FORWARD_DECLARE_CLASS \ + Q_DUMMY_COMPARISON_OPERATOR \ + Q_ENUMS \ + Q_FLAGS \ + Q_INTERFACES \ + __attribute__ \ + K_DECLARE_PRIVATE \ + PHONON_OBJECT \ + PHONON_HEIR \ + Q_PRIVATE_PROPERTY \ + Q_DECLARE_PRIVATE_D \ + Q_CLASSINFO diff --git a/src/tools/qdoc/doc/config/qt-defines.qdocconf b/src/tools/qdoc/doc/config/qt-defines.qdocconf new file mode 100644 index 0000000000..50a355f04c --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-defines.qdocconf @@ -0,0 +1,17 @@ +defines = Q_QDOC \ + QT_.*_SUPPORT \ + QT_.*_LIB \ + QT_COMPAT \ + QT_KEYPAD_NAVIGATION \ + QT_NO_EGL \ + QT3_SUPPORT \ + Q_WS_.* \ + Q_OS_.* \ + Q_BYTE_ORDER \ + QT_DEPRECATED \ + Q_NO_USING_KEYWORD \ + __cplusplus + +versionsym = QT_VERSION_STR + +codeindent = 1 diff --git a/src/tools/qdoc/doc/config/qt-html-default-styles.qdocconf b/src/tools/qdoc/doc/config/qt-html-default-styles.qdocconf new file mode 100644 index 0000000000..b2e39d02f5 --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-html-default-styles.qdocconf @@ -0,0 +1,32 @@ +# Define the location of the templates to use. Style sheets and scripts are +# specified relative to the template directory and will be copied into +# subdirectories of the output directory. + +HTML.templatedir = . + +HTML.stylesheets = style/offline.css + +HTML.scripts = + +# Files not referenced in any qdoc file (last four needed by qtdemo) +# See also qhp.Qt.extraFiles +extraimages.HTML = qt-logo.png \ + arrow_down.png \ + breadcrumb.png \ + bullet_gt.png \ + bullet_dn.png \ + bullet_sq.png \ + bullet_up.png \ + horBar.png \ + sprites-combined.png + +# Include the style sheets and scripts used. + +HTML.headerstyles = \ + " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/offline.css\" />\n" + +HTML.headerscripts = + +HTML.endheader = \ + "</head>\n" \ + "<body>\n" diff --git a/src/tools/qdoc/doc/config/qt-html-online-styles.qdocconf b/src/tools/qdoc/doc/config/qt-html-online-styles.qdocconf new file mode 100644 index 0000000000..4ffd6ca65f --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-html-online-styles.qdocconf @@ -0,0 +1,72 @@ +# Define the location of the templates to use. Style sheets and scripts are +# specified relative to the template directory and will be copied into +# subdirectories of the output directory. + +HTML.templatedir = . + +HTML.stylesheets = style/narrow.css \ + style/style.css \ + style/style_ie6.css \ + style/style_ie7.css \ + style/style_ie8.css \ + style/superfish.css + +# Adding jquery and functions - providing online tools and search features +HTML.scripts = scripts/functions.js \ + scripts/narrow.js \ + scripts/superfish.js \ + scripts/jquery.js + + +# Files not referenced in any qdoc file. +# See also qhp.Qt.extraFiles +extraimages.HTML = qt-logo.png \ + bg_l.png \ + bg_l_blank.png \ + bg_ll_blank.png \ + bg_ul_blank.png \ + header_bg.png \ + bg_r.png \ + box_bg.png \ + breadcrumb.png \ + bullet_gt.png \ + bullet_dn.png \ + bullet_sq.png \ + bullet_up.png \ + arrow_down.png \ + feedbackground.png \ + horBar.png \ + page.png \ + page_bg.png \ + sprites-combined.png \ + spinner.gif + +# Include the style sheets and scripts used. + +HTML.headerstyles = \ + " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/style.css\" />\n" \ + " <script src=\"scripts/jquery.js\" type=\"text/javascript\"></script>\n" \ + " <script src=\"scripts/functions.js\" type=\"text/javascript\"></script>\n" \ + " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/superfish.css\" />\n" \ + " <link rel=\"stylesheet\" type=\"text/css\" href=\"style/narrow.css\" />\n" \ + " <!--[if IE]>\n" \ + "<meta name=\"MSSmartTagsPreventParsing\" content=\"true\">\n" \ + "<meta http-equiv=\"imagetoolbar\" content=\"no\">\n" \ + "<![endif]-->\n" \ + "<!--[if lt IE 7]>\n" \ + "<link rel=\"stylesheet\" type=\"text/css\" href=\"style/style_ie6.css\">\n" \ + "<![endif]-->\n" \ + "<!--[if IE 7]>\n" \ + "<link rel=\"stylesheet\" type=\"text/css\" href=\"style/style_ie7.css\">\n" \ + "<![endif]-->\n" \ + "<!--[if IE 8]>\n" \ + "<link rel=\"stylesheet\" type=\"text/css\" href=\"style/style_ie8.css\">\n" \ + "<![endif]-->\n\n" + +HTML.headerscripts = \ + "<script src=\"scripts/superfish.js\" type=\"text/javascript\"></script>\n" \ + "<script src=\"scripts/narrow.js\" type=\"text/javascript\"></script>\n\n" + +HTML.endheader = \ + "</head>\n" \ + "<body class=\"\" onload=\"CheckEmptyAndLoadList();\">\n" diff --git a/src/tools/qdoc/doc/config/qt-html-templates-online.qdocconf b/src/tools/qdoc/doc/config/qt-html-templates-online.qdocconf new file mode 100644 index 0000000000..af186f0568 --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-html-templates-online.qdocconf @@ -0,0 +1,115 @@ +include(qt-html-online-styles.qdocconf) + +HTML.postheader = \ + " <div class=\"header\" id=\"qtdocheader\">\n" \ + " <div class=\"content\"> \n" \ + " <div id=\"nav-logo\">\n" \ + " <a href=\"index.html\">Home</a></div>\n" \ + " <a href=\"index.html\" class=\"qtref\"><span>QDoc Reference Documentation</span></a>\n" \ + " <div id=\"narrowsearch\"></div>\n" \ + " <div id=\"nav-topright\">\n" \ + " <ul>\n" \ + " <li class=\"nav-topright-home\"><a href=\"http://qt.nokia.com/\">Qt HOME</a></li>\n" \ + " <li class=\"nav-topright-dev\"><a href=\"http://developer.qt.nokia.com/\">DEV</a></li>\n" \ + " <li class=\"nav-topright-labs\"><a href=\"http://labs.qt.nokia.com/blogs/\">LABS</a></li>\n" \ + " <li class=\"nav-topright-doc nav-topright-doc-active\"><a href=\"http://doc.qt.nokia.com/\">\n" \ + " DOC</a></li>\n" \ + " <li class=\"nav-topright-blog\"><a href=\"http://blog.qt.nokia.com/\">BLOG</a></li>\n" \ + " </ul>\n" \ + " </div>\n" \ + " <div id=\"shortCut\">\n" \ + " <ul>\n" \ + " <li class=\"shortCut-topleft-inactive\"><span><a href=\"index.html\">Qt 4.7</a></span></li>\n" \ + " <li class=\"shortCut-topleft-active\"><a href=\"http://doc.qt.nokia.com\">ALL VERSIONS" \ + " </a></li>\n" \ + " </ul>\n" \ + " </div>\n" \ + " </div>\n" \ + " </div>\n" \ + " <div class=\"wrapper\">\n" \ + " <div class=\"hd\">\n" \ + " <span></span>\n" \ + " </div>\n" \ + " <div class=\"bd group\">\n" \ + " <div class=\"wrap\">\n" \ + " <div class=\"toolbar\">\n" \ + " <div class=\"breadcrumb toolblock\">\n" \ + " <ul>\n" \ + " <li class=\"first\"><a href=\"index.html\">Home</a></li>\n" \ + " <!-- Breadcrumbs go here -->\n" + +HTML.postpostheader = \ + " </ul>\n" \ + " </div>\n" \ + " <div class=\"toolbuttons toolblock\">\n" \ + " <ul>\n" \ + " <li id=\"smallA\" class=\"t_button\">A</li>\n" \ + " <li id=\"medA\" class=\"t_button active\">A</li>\n" \ + " <li id=\"bigA\" class=\"t_button\">A</li>\n" \ + " <li id=\"print\" class=\"t_button\"><a href=\"javascript:this.print();\">\n" \ + " <span>Print</span></a></li>\n" \ + " </ul>\n" \ + " </div>\n" \ + " </div>\n" \ + " <div class=\"content mainContent\">\n" + +HTML.footer = \ + " </div>\n" \ + " </div>\n" \ + " </div> \n" \ + " <div class=\"ft\">\n" \ + " <span></span>\n" \ + " </div>\n" \ + " </div> \n" \ + " <div class=\"footer\">\n" \ + " <p>\n" \ + " <acronym title=\"Copyright\">©</acronym> 2012 Nokia Corporation and/or its\n" \ + " subsidiaries. Nokia, Qt and their respective logos are trademarks of Nokia Corporation \n" \ + " in Finland and/or other countries worldwide.</p>\n" \ + " <p>\n" \ + " All other trademarks are property of their respective owners. <a title=\"Privacy Policy\"\n" \ + " href=\"http://qt.nokia.com/about/privacy-policy\">Privacy Policy</a></p>\n" \ + " <br />\n" \ + " <p>\n" \ + " Licensees holding valid Qt Commercial licenses may use this document in accordance with the" \ + " Qt Commercial License Agreement provided with the Software or, alternatively, in accordance" \ + " with the terms contained in a written agreement between you and Nokia.</p>\n" \ + " <p>\n" \ + " Alternatively, this document may be used under the terms of the <a href=\"http://www.gnu.org/licenses/fdl.html\">GNU\n" \ + " Free Documentation License version 1.3</a>\n" \ + " as published by the Free Software Foundation.</p>\n" \ + " </div>\n" + + +# Files not referenced in any qdoc file. +# See also extraimages.HTML +qhp.QDoc.extraFiles = index.html \ + images/bg_l.png \ + images/bg_l_blank.png \ + images/bg_ll_blank.png \ + images/bg_ul_blank.png \ + images/header_bg.png \ + images/bg_r.png \ + images/box_bg.png \ + images/breadcrumb.png \ + images/bullet_gt.png \ + images/bullet_dn.png \ + images/bullet_sq.png \ + images/bullet_up.png \ + images/arrow_down.png \ + images/feedbackground.png \ + images/horBar.png \ + images/page.png \ + images/page_bg.png \ + images/sprites-combined.png \ + images/spinner.gif \ + scripts/functions.js \ + scripts/jquery.js \ + scripts/narrow.js \ + scripts/superfish.js \ + style/narrow.css \ + style/superfish.css \ + style/style_ie6.css \ + style/style_ie7.css \ + style/style_ie8.css \ + style/style.css diff --git a/src/tools/qdoc/doc/config/qt-html-templates.qdocconf b/src/tools/qdoc/doc/config/qt-html-templates.qdocconf new file mode 100644 index 0000000000..2e15140945 --- /dev/null +++ b/src/tools/qdoc/doc/config/qt-html-templates.qdocconf @@ -0,0 +1,54 @@ +include(qt-html-default-styles.qdocconf) + +HTML.postheader = \ + "<div class=\"header\" id=\"qtdocheader\">\n" \ + " <div class=\"content\"> \n" \ + " <a href=\"index.html\" class=\"qtref\"><span>QDoc Reference Documentation</span></a>\n" \ + " </div>\n" \ + " <div class=\"breadcrumb toolblock\">\n" \ + " <ul>\n" \ + " <li class=\"first\"><a href=\"index.html\">Home</a></li>\n" \ + " <!-- Breadcrumbs go here -->\n" + +HTML.postpostheader = \ + " </ul>\n" \ + " </div>\n" \ + "</div>\n" \ + "<div class=\"content mainContent\">\n" + +HTML.footer = \ + " <div class=\"ft\">\n" \ + " <span></span>\n" \ + " </div>\n" \ + "</div> \n" \ + "<div class=\"footer\">\n" \ + " <p>\n" \ + " <acronym title=\"Copyright\">©</acronym> 2012 Nokia Corporation and/or its\n" \ + " subsidiaries. Nokia, Qt and their respective logos are trademarks of Nokia Corporation \n" \ + " in Finland and/or other countries worldwide.</p>\n" \ + " <p>\n" \ + " All other trademarks are property of their respective owners. <a title=\"Privacy Policy\"\n" \ + " href=\"http://qt.nokia.com/about/privacy-policy\">Privacy Policy</a></p>\n" \ + " <br />\n" \ + " <p>\n" \ + " Licensees holding valid Qt Commercial licenses may use this document in accordance with the" \ + " Qt Commercial License Agreement provided with the Software or, alternatively, in accordance" \ + " with the terms contained in a written agreement between you and Nokia.</p>\n" \ + " <p>\n" \ + " Alternatively, this document may be used under the terms of the <a href=\"http://www.gnu.org/licenses/fdl.html\">GNU\n" \ + " Free Documentation License version 1.3</a>\n" \ + " as published by the Free Software Foundation.</p>\n" \ + "</div>\n" \ + +# Files not referenced in any qdoc file. +# See also extraimages.HTML +qhp.QDoc.extraFiles = index.html \ + images/arrow_down.png \ + images/breadcrumb.png \ + images/bullet_gt.png \ + images/bullet_dn.png \ + images/bullet_sq.png \ + images/bullet_up.png \ + images/horBar.png \ + images/sprites-combined.png \ + style/offline.css diff --git a/src/tools/qdoc/doc/config/style/offline.css b/src/tools/qdoc/doc/config/style/offline.css new file mode 100644 index 0000000000..3689ee8d40 --- /dev/null +++ b/src/tools/qdoc/doc/config/style/offline.css @@ -0,0 +1,673 @@ +@media screen +{ + +/* basic elements */ + html + { + color: #000000; + background: #FFFFFF; + } + table + { + border-collapse: collapse; + border-spacing: 0; + } + fieldset, img + { + border: 0; + max-width:100%; + } + address, caption, cite, code, dfn, em, strong, th, var, optgroup + { + font-style: inherit; + font-weight: inherit; + } + del, ins + { + text-decoration: none; + } + li + { + list-style: none; + } + ol li + { + list-style: decimal; + } + caption, th + { + text-align: left; + } + h1, h2, h3, h4, h5, h6 + { + font-size: 100%; + } + q:before, q:after + { + content: ''; + } + abbr, acronym + { + border: 0; + font-variant: normal; + } + sup, sub + { + vertical-align: baseline; + } + tt, .qmlreadonly span, .qmldefault span + { + word-spacing:0.5em; + } + legend + { + color: #000000; + } + strong + { + font-weight: bold; + } + em + { + font-style: italic; + } + + body + { + margin-left: 0.5em; + margin-right: 0.5em; + } + a + { + color: #00732F; + text-decoration: none; + } + hr + { + background-color: #E6E6E6; + border: 1px solid #E6E6E6; + height: 1px; + width: 100%; + text-align: left; + margin: 1.5em 0 1.5em 0; + } + + pre + { + border: 1px solid #DDDDDD; + -moz-border-radius: 0.7em 0.7em 0.7em 0.7em; + -webkit-border-radius: 0.7em 0.7em 0.7em 0.7em; + border-radius: 0.7em 0.7em 0.7em 0.7em; + margin: 0 1.5em 1em 1em; + padding: 1em 1em 1em 1em; + overflow-x: auto; + } + table, pre + { + -moz-border-radius: 0.7em 0.7em 0.7em 0.7em; + -webkit-border-radius: 0.7em 0.7em 0.7em 0.7em; + border-radius: 0.7em 0.7em 0.7em 0.7em; + background-color: #F6F6F6; + border: 1px solid #E6E6E6; + border-collapse: separate; + margin-bottom: 2.5em; + } + pre { + font-size: 90%; + display: block; + overflow:hidden; + } + thead + { + margin-top: 0.5em; + font-weight: bold + } + th + { + padding: 0.5em 1.5em 0.5em 1.5em; + background-color: #E1E1E1; + border-left: 1px solid #E6E6E6; + } + td + { + padding: 0.25em 1.5em 0.25em 2em; + } + + td.rightAlign + { + padding: 0.25em 0.5em 0.25em 1em; + } + table tr.odd + { + border-left: 1px solid #E6E6E6; + background-color: #F6F6F6; + color: #66666E; + } + table tr.even + { + border-left: 1px solid #E6E6E6; + background-color: #ffffff; + color: #66666E; + } + + div.float-left + { + float: left; margin-right: 2em + } + div.float-right + { + float: right; margin-left: 2em + } + + span.comment + { + color: #008B00; + font-style: italic + } + span.string, span.char + { + color: #000084; + } + span.number + { + color: #a46200; + } + span.operator + { + color: #202020; + } + span.keyword + { + color: #840000; + } + span.name + { + color: black + } + span.type + { + font-weight: bold + } + span.type a:visited + { + color: #0F5300; + } + span.preprocessor + { + color: #404040 + } +/* end basic elements */ + +/* font style elements */ + .heading + { + font-weight: bold; + font-size: 125%; + } + .subtitle + { + font-size: 110% + } + .small-subtitle + { + font-size: 100% + } + .red + { + color:red; + } +/* end font style elements */ + +/* global settings*/ + .header, .footer + { + display: block; + clear: both; + overflow: hidden; + } +/* end global settings*/ + +/* header elements */ + .header .qtref + { + color: #00732F; + font-weight: bold; + font-size: 130%; + } + + .header .content + { + margin-bottom: 0.5em + } + + .naviNextPrevious + { + display: none + } + .header .breadcrumb + { + font-size: 90%; + padding: 0.5em 0 0.5em 1em; + margin: 0; + background-color: #fafafa; + height: 1.35em; + border-bottom: 1px solid #d1d1d1; + } + + .header .breadcrumb ul + { + margin: 0; + padding: 0; + } + + .header .content + { + word-wrap: break-word; + } + + .header .breadcrumb ul li + { + float: left; + background: url(../images/breadcrumb.png) no-repeat 0 3px; + padding-left: 1.5em; + margin-left: 1.5em; + } + + .header .breadcrumb ul li.last + { + font-weight: normal; + } + + .header .breadcrumb ul li a + { + color: #00732F; + } + + .header .breadcrumb ul li.first + { + background-image: none; + padding-left: 0; + margin-left: 0; + } + + .header .content ol li { + background: none; + margin-bottom: 1.0em; + margin-left: 1.2em; + padding-left: 0 + } + + .header .content li + { + background: url(../images/bullet_sq.png) no-repeat 0 5px; + margin-bottom: 1em; + padding-left: 1.2em; + } + +/* end header elements */ + +/* content elements */ + .content h1 + { + font-weight: bold; + font-size: 150% + } + + .content h2 + { + font-weight: bold; + font-size: 135%; + width: 100%; + } + .content h3 + { + font-weight: bold; + font-size: 120%; + width: 100%; + } + .content table p + { + margin: 0 + } + .content ul + { + padding-left: 2.5em; + } + .content li + { + padding-top: 0.25em; + padding-bottom: 0.25em; + } + .content ul img { + vertical-align: middle; + } + + .content a:visited + { + color: #4c0033; + text-decoration: none; + } + + .content a:visited:hover + { + color: #4c0033; + text-decoration: underline; + } + + a:hover + { + color: #4c0033; + text-decoration: underline; + } + descr p a + { + text-decoration: underline; + } + + .descr p a:visited + { + text-decoration: underline; + } + + .alphaChar{ + width:95%; + background-color:#F6F6F6; + border:1px solid #E6E6E6; + -moz-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + font-size:12pt; + padding-left:10px; + margin-top:10px; + margin-bottom:10px; + } + .flowList{ + /*vertical-align:top;*/ + /*margin:20px auto;*/ + + column-count:3; + -webkit-column-count:3; + -moz-column-count:3; +/* + column-width:100%; + -webkit-column-width:200px; + -col-column-width:200px; +*/ + column-gap:41px; + -webkit-column-gap:41px; + -moz-column-gap:41px; + + column-rule: 1px dashed #ccc; + -webkit-column-rule: 1px dashed #ccc; + -moz-column-rule: 1px dashed #ccc; + } + + .flowList dl{ + } + .flowList dd{ + /*display:inline-block;*/ + margin-left:10px; + min-width:250px; + line-height: 1.5; + min-width:100%; + min-height:15px; + } + + .flowList dd a{ + } + + .content .flowList p{ + padding:0px; + } + + .content .alignedsummary + { + margin: 15px; + } + + + .qmltype + { + text-align: center; + font-size: 120%; + } + .qmlreadonly + { + padding-left: 5px; + float: right; + color: #254117; + } + + .qmldefault + { + padding-left: 5px; + float: right; + color: red; + } + + .qmldoc + { + } + + .generic .alphaChar{ + margin-top:5px; + } + + .generic .odd .alphaChar{ + background-color: #F6F6F6; + } + + .generic .even .alphaChar{ + background-color: #FFFFFF; + } + + .memItemRight{ + padding: 0.25em 1.5em 0.25em 0; + } + .highlightedCode + { + margin: 1.0em; + } + .annotated td { + padding: 0.25em 0.5em 0.25em 0.5em; + } + + .header .content .toc ul + { + padding-left: 0px; + } + + .content .toc h3 { + border-bottom: 0px; + margin-top: 0px; + } + + .content .toc h3 a:hover { + color: #00732F; + text-decoration: none; + } + + .content .toc .level2 + { + margin-left: 1.5em; + } + + .content .toc .level3 + { + margin-left: 3.0em; + } + + .content ul li + { + background: url(../images/bullet_sq.png) no-repeat 0 0.7em; + padding-left: 1em + } + + .content .toc li + { + background: url(../images/bullet_dn.png) no-repeat 0 5px; + padding-left: 1em + } + + .relpage + { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + border: 1px solid #DDDDDD; + padding: 25px 25px; + clear: both; + } + .relpage ul + { + float: none; + padding: 1.5em; + } + + h3.fn, span.fn + { + -moz-border-radius:7px 7px 7px 7px; + -webkit-border-radius:7px 7px 7px 7px; + border-radius:7px 7px 7px 7px; + background-color: #F6F6F6; + border-width: 1px; + border-style: solid; + border-color: #E6E6E6; + font-weight: bold; + word-spacing:3px; + padding:3px 5px; + } + + .functionIndex { + font-size:12pt; + word-spacing:10px; + margin-bottom:10px; + background-color: #F6F6F6; + border-width: 1px; + border-style: solid; + border-color: #E6E6E6; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + width:100%; + } + + .centerAlign + { + text-align:center; + } + + .rightAlign + { + text-align:right; + } + + .leftAlign + { + text-align:left; + } + + .topAlign{ + vertical-align:top + } + + .functionIndex a{ + display:inline-block; + } + +/* end content elements */ +/* footer elements */ + + .footer + { + color: #393735; + font-size: 0.75em; + text-align: center; + padding-top: 1.5em; + padding-bottom: 1em; + background-color: #E6E7E8; + margin: 0; + } + .footer p + { + margin: 0.25em + } + .small + { + font-size: 0.5em; + } +/* end footer elements */ + + .item { + float: left; + position: relative; + width: 100%; + overflow: hidden; + } + + + .item .primary { + margin-right: 220px; + position: relative; + } + + .item hr { + margin-left: -220px; + } + + .item .secondary { + float: right; + width: 200px; + position: relative; + } + + .item .cols { + clear: both; + display: block; + } + + .item .cols .col { + float: left; + margin-left: 1.5%; + } + + .item .cols .col.first { + margin-left: 0; + } + + .item .cols.two .col { + width: 45%; + } + + .item .box { + margin: 0 0 10px 0; + } + + .item .box h3 { + margin: 0 0 10px 0; + } + + .cols.unclear { + clear:none; + } +} + +/* end of screen media */ + +/* start of print media */ + +@media print +{ + input, textarea, .header, .footer, .toolbar, .feedback, .wrapper .hd, .wrapper .bd .sidebar, .wrapper .ft, #feedbackBox, #blurpage, .toc, .breadcrumb, .toolbar, .floatingResult + { + display: none; + background: none; + } + .content + { + background: none; + display: block; + width: 100%; margin: 0; float: none; + } +} +/* end of print media */ diff --git a/src/tools/qdoc/doc/corefeatures.qdoc b/src/tools/qdoc/doc/corefeatures.qdoc new file mode 100644 index 0000000000..6d31cacda2 --- /dev/null +++ b/src/tools/qdoc/doc/corefeatures.qdoc @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +/*! + \page corefeatures.html + \title Core Features + + \input examples/signalandslots.qdocinc + \input examples/objectmodel.qdocinc + \input examples/layoutmanagement.qdocinc +*/ diff --git a/src/tools/qdoc/doc/examples/componentset/ProgressBar.qml b/src/tools/qdoc/doc/examples/componentset/ProgressBar.qml new file mode 100644 index 0000000000..fc6d6a644f --- /dev/null +++ b/src/tools/qdoc/doc/examples/componentset/ProgressBar.qml @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +/*! + \qmlclass ProgressBar + \inqmlmodule UIComponents 1.0 + \brief A component that shows the progress of an event + + A ProgressBar shows the linear progress of an event as its \l value. + The range is specified using the \l {minimum} and the \l{maximum} values. + + The ProgressBar component is part of the \l {UI Components} module. + + This documentation is part of the \l{componentset}{UIComponents} example. +*/ +Item { + id: progressbar + + /*! + The minumum value of the ProgressBar range. + The \l value must not be less than this value. + */ + property int minimum: 0 + + /*! + The maximum value of the ProgressBar range. + The \l value must not be more than this value. + */ + property int maximum: 100 + + /*! + The value of the progress. + */ + property int value: 0 + + /*! + \qmlproperty color ProgressBar::color + The color of the ProgressBar's gradient. Must bind to a color type. + + \omit + The "\qmlproperty <type> <property name>" is needed because + property alias need to have their types manually entered. + + QDoc will not publish the documentation within omit and endomit. + \endomit + + \sa secondcolor + */ + property alias color: gradient1.color + + /*! + \qmlproperty color ProgressBar::secondcolor + The second color of the ProgressBar's gradient. + Must bind to a color type. + + \omit + The "\qmlproperty <type> <property name>" is needed because + property alias need to have their types manually entered. + + QDoc will not publish the documentation within omit and endomit. + \endomit + + \sa color + */ + property alias secondColor: gradient2.color + + width: 250; height: 23 + clip: true + + Rectangle { + id: highlight + + /*! + An internal documentation comment. The widthDest property is not + a public API and therefore will not be exposed. + */ + property int widthDest: ((progressbar.width * (value - minimum)) / (maximum - minimum) - 6) + + width: highlight.widthDest + Behavior on width { SmoothedAnimation { velocity: 1200 } } + + anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: 3 } + radius: 1 + gradient: Gradient { + GradientStop { id: gradient1; position: 0.0 } + GradientStop { id: gradient2; position: 1.0 } + } + + } + Text { + anchors { right: highlight.right; rightMargin: 6; verticalCenter: parent.verticalCenter } + color: "white" + font.bold: true + text: Math.floor((value - minimum) / (maximum - minimum) * 100) + '%' + } +} diff --git a/src/tools/qdoc/doc/examples/componentset/Switch.qml b/src/tools/qdoc/doc/examples/componentset/Switch.qml new file mode 100644 index 0000000000..31153d6252 --- /dev/null +++ b/src/tools/qdoc/doc/examples/componentset/Switch.qml @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +/*! + \qmlclass ToggleSwitch + \inqmlmodule UIComponents 1.0 + \brief A component that can be turned on or off + + A toggle switch has two states: an \c on and an \c off state. The \c off + state is when the \l on property is set to \c false. + + The ToggleSwitch component is part of the \l {UI Components} module. + + This documentation is part of the \l{componentset}{UIComponents} example. + +*/ +Item { + id: toggleswitch + width: background.width; height: background.height + + /*! + Indicates the state of the switch. If \c false, then the switch is in + the \c off state. + + \omit + The \qmlproperty <type> <propertyname> is not necessary as QDoc + will associate this property to the ToggleSwitch + + QDoc will not publish the documentation within omit and endomit. + \endomit + */ + property bool on: false + + + /*! + A method to toggle the switch. If the switch is \c on, the toggling it + will turn it \c off. Toggling a switch in the \c off position will + turn it \c on. + */ + function toggle() { + if (toggleswitch.state == "on") + toggleswitch.state = "off"; + else + toggleswitch.state = "on"; + } + + + /*! + \internal + + An internal function to synchronize the switch's internals. This + function is not for public access. The \internal command will + prevent QDoc from publishing this comment in the public API. + */ + function releaseSwitch() { + if (knob.x == 1) { + if (toggleswitch.state == "off") return; + } + if (knob.x == 78) { + if (toggleswitch.state == "on") return; + } + toggle(); + } + + Rectangle { + id: background + width: 130; height: 48 + radius: 48 + color: "lightsteelblue" + MouseArea { anchors.fill: parent; onClicked: toggle() } + } + + Rectangle { + id: knob + width: 48; height: 48 + radius: width + color: "lightblue" + + MouseArea { + anchors.fill: parent + drag.target: knob; drag.axis: Drag.XAxis; drag.minimumX: 1; drag.maximumX: 78 + onClicked: toggle() + onReleased: releaseSwitch() + } + } + + states: [ + State { + name: "on" + PropertyChanges { target: knob; x: 78 } + PropertyChanges { target: toggleswitch; on: true } + }, + State { + name: "off" + PropertyChanges { target: knob; x: 1 } + PropertyChanges { target: toggleswitch; on: false } + } + ] + + transitions: Transition { + NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad; duration: 200 } + } +} diff --git a/src/tools/qdoc/doc/examples/componentset/TabWidget.qml b/src/tools/qdoc/doc/examples/componentset/TabWidget.qml new file mode 100644 index 0000000000..eb64979fb2 --- /dev/null +++ b/src/tools/qdoc/doc/examples/componentset/TabWidget.qml @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 1.0 + +/*! + \qmlclass TabWidget + \inqmlmodule UIComponents 1.0 + \brief A widget that places its children as tabs + + A TabWidget places its children as tabs in a view. Selecting + a tab involves selecting the tab at the top. + + The TabWidget component is part of the \l {UI Components} module. + + This documentation is part of the \l{componentset}{UIComponents} example. + + \section1 Adding Tabs + + To add a tab, declare the tab as a child of the TabWidget. + + \code + TabWidget { + id: tabwidget + + Rectangle { + id: tab1 + color: "red" + //... omitted + } + Rectangle { + id: tab2 + color: "blue" + //... omitted + } + + } + \endcode + +*/ +Item { + id: tabWidget + + /*! + \internal + + Setting the default property to stack.children means any child items + of the TabWidget are actually added to the 'stack' item's children. + + See the \l{"Property Binding in QML"} + documentation for details on default properties. + + This is an implementation detail, not meant for public knowledge. Putting + the \internal command at the beginning will cause QDoc to not publish this + documentation in the public API page. + + Normally, a property alias needs to have a + "\qmlproperty <type> <propertyname>" to assign the alias a type. + + */ + default property alias content: stack.children + + + /*! + The currently active tab in the TabWidget. + */ + property int current: 0 + + /*! + A sample \c{read-only} property. + A contrived property to demonstrate QDoc's ability to detect + read-only properties. + + The signature is: + \code + readonly property int sampleReadOnlyProperty: 0 + \endcode + + Note that the property must be initialized to a value. + + */ + readonly property int sampleReadOnlyProperty: 0 + + /*! + \internal + + This handler is an implementation + detail. The \c{\internal} command will prevent QDoc from publishing this + documentation on the public API. + */ + onCurrentChanged: setOpacities() + Component.onCompleted: setOpacities() + + /*! + \internal + + An internal function to set the opacity. + The \internal command will prevent QDoc from publishing this + documentation on the public API. + */ + function setOpacities() { + for (var i = 0; i < stack.children.length; ++i) { + stack.children[i].opacity = (i == current ? 1 : 0) + } + } + + Row { + id: header + + Repeater { + model: stack.children.length + delegate: Rectangle { + width: tabWidget.width / stack.children.length; height: 36 + + Rectangle { + width: parent.width; height: 1 + anchors { bottom: parent.bottom; bottomMargin: 1 } + color: "#acb2c2" + } + BorderImage { + anchors { fill: parent; leftMargin: 2; topMargin: 5; rightMargin: 1 } + border { left: 7; right: 7 } + source: "tab.png" + visible: tabWidget.current == index + } + Text { + horizontalAlignment: Qt.AlignHCenter; verticalAlignment: Qt.AlignVCenter + anchors.fill: parent + text: stack.children[index].title + elide: Text.ElideRight + font.bold: tabWidget.current == index + } + MouseArea { + anchors.fill: parent + onClicked: tabWidget.current = index + } + } + } + } + + Item { + id: stack + width: tabWidget.width + anchors.top: header.bottom; anchors.bottom: tabWidget.bottom + } +} diff --git a/src/tools/qdoc/doc/examples/componentset/componentset.pro b/src/tools/qdoc/doc/examples/componentset/componentset.pro new file mode 100644 index 0000000000..5b44737c2d --- /dev/null +++ b/src/tools/qdoc/doc/examples/componentset/componentset.pro @@ -0,0 +1,5 @@ +SOURCES = componentset.pro \ + ProgressBar.qml \ + Switch.qml \ + TabWidget.qml \ + uicomponents.qdoc diff --git a/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc b/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc new file mode 100644 index 0000000000..10c23c7c0f --- /dev/null +++ b/src/tools/qdoc/doc/examples/componentset/uicomponents.qdoc @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +/*! + \qmlmodule UIComponents 1.0 + \title UI Components + \brief Basic set of QML Components + + This is a listing of a list of QML components. These files are available + for general import and they are based off the \l{Qt Quick Code Samples}. + + This module is part of the \l{componentset}{UIComponents} example. +*/ diff --git a/src/tools/qdoc/doc/examples/examples.qdoc b/src/tools/qdoc/doc/examples/examples.qdoc new file mode 100644 index 0000000000..69ea11afe6 --- /dev/null +++ b/src/tools/qdoc/doc/examples/examples.qdoc @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +/*! + \example componentset + \title QML Documentation Example + + This example demonstrates one of the ways to document QML components. + + In particular, there are sample components that are documented with QDoc + commands comments. There are documentation comments for the QML components + and their public interfaces. The components are grouped into a module, the + \l {UI Components} module. + + The \l{componentset/uicomponents.qdoc}{uicomponents.qdoc} file generates + the overview page for the \l {UI Components} module page. + + The generated documentation is available in the \l {UI Components} module. + + \section1 QML Class + + The components use the \l{qmlclass-command}{\\qmlclass} to document the + component. In addition, they have the \l{inmodule-command}{\\inmodule} + command in order for QDoc to associate them to the \c UIComponents module. + + QDoc uses the \l{brief-command}{\\brief} command to place a basic + description when listing the component. + + \section1 Properties, Signals, Handlers, and Methods + + The components have their properties, signals, handlers, and methods + defined in their respective QML files. QDoc associates the properties and + methods to the components, therefore, you only need to place the + documentation above the property, method, or signal. + + To document the type of a \e {property alias}, you must use the + \l{qmlproperty-command}{\\qmlproperty} command to specify the data type. + + \code + \qmlproperty int anAliasedProperty + An aliased property of type int. + \endcode + + \section2 Internal Documentation + + You may declare that a documentation is for internal use by placing the + \l{internal-command}{\\internal} command after the beginning QDoc comment + \begincomment. QDoc will prevent the internal documentation from appearing + in the public API. + + If you wish to omit certain parts of the documentation, you may use the + \l{omit-command}{\\omit} and \l{omit-command}{\\endomit} command. + + \section1 Components with C++ Implementation + + This example only demonstrates the documentation for components in QML + files, but the regular \l{qml-documentation}{QML commands} may be placed + inside C++ classes to define the public API of the component. + +*/ diff --git a/src/tools/qdoc/doc/examples/layoutmanagement.qdocinc b/src/tools/qdoc/doc/examples/layoutmanagement.qdocinc new file mode 100644 index 0000000000..01f8acf363 --- /dev/null +++ b/src/tools/qdoc/doc/examples/layoutmanagement.qdocinc @@ -0,0 +1,13 @@ +\section1 Layout Classes + +The Qt layout system provides a simple and powerful way of specifying +the layout of child widgets. + +By specifying the logical layout once, you get the following benefits: + +\list + \o Positioning of child widgets. + \o Sensible default sizes for windows. + \o Sensible minimum sizes for windows. + \o ... +\endlist diff --git a/src/tools/qdoc/doc/examples/main.cpp b/src/tools/qdoc/doc/examples/main.cpp new file mode 100644 index 0000000000..b721c7fcf4 --- /dev/null +++ b/src/tools/qdoc/doc/examples/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 <QApplication> +#include <QPushButton> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QPushButton hello("Hello world!"); + hello.resize(100, 30); + + hello.show(); + return app.exec(); +} diff --git a/src/tools/qdoc/doc/examples/minimum.qdocconf b/src/tools/qdoc/doc/examples/minimum.qdocconf new file mode 100644 index 0000000000..1dcff501c0 --- /dev/null +++ b/src/tools/qdoc/doc/examples/minimum.qdocconf @@ -0,0 +1,42 @@ +# QDoc is a tool that constantly evolves to suit our needs, +# and there are some compatibility issues between old and new +# practices. For that reason, any QDoc configuration file needs to +# include compat.qdocconf. + +#include(compat.qdocconf) + + +# The outputdir variable specifies the directory +# where QDoc will put the generated documentation. + +outputdir = html + + +# The headerdirs variable specifies the directories +# containing the header files associated +# with the .cpp source files used in the documentation. + +headerdirs = . + + +# The sourcedirs variable specifies the +# directories containing the .cpp or .qdoc +# files used in the documentation. + +#sourcedirs = . + + +# The exampledirs variable specifies the directories containing +# the source code of the example files. + +exampledirs = . + + +# The imagedirs variable specifies the +# directories containing the images used in the documentation. + +imagedirs = ./images + + + + diff --git a/src/tools/qdoc/doc/examples/objectmodel.qdocinc b/src/tools/qdoc/doc/examples/objectmodel.qdocinc new file mode 100644 index 0000000000..02b5991c4d --- /dev/null +++ b/src/tools/qdoc/doc/examples/objectmodel.qdocinc @@ -0,0 +1,11 @@ +\section1 Qt Object Model + +The standard C++ object model provides very efficient runtime support +for the object paradigm. But its static nature is inflexibile in +certain problem domains. Graphical user interface programming is a +domain that requires both runtime efficiency and a high level of +flexibility. Qt provides this, by combining the speed of C++ with the +flexibility of the Qt Object Model. + +... + diff --git a/src/tools/qdoc/doc/examples/samples.qdocinc b/src/tools/qdoc/doc/examples/samples.qdocinc new file mode 100644 index 0000000000..58213210f2 --- /dev/null +++ b/src/tools/qdoc/doc/examples/samples.qdocinc @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +//! [qvector3d-class] +/*! + \class QVector3D + \brief The QVector3D class represents a vector or vertex in 3D space. + \since 4.6 + \ingroup painting-3D + + Vectors are one of the main building blocks of 3D representation and + drawing. They consist of three coordinates, traditionally called + x, y, and z. + + The QVector3D class can also be used to represent vertices in 3D space. + We therefore do not need to provide a separate vertex class. + + \bold{Note:} By design values in the QVector3D instance are stored as \c float. + This means that on platforms where the \c qreal arguments to QVector3D + functions are represented by \c double values, it is possible to + lose precision. + + \sa QVector2D, QVector4D, QQuaternion +*/ +//! [qvector3d-class] + +//! [qvector3d-function] +/*! + \fn QVector3D::QVector3D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and a + z coordinate of 0. +*/ +//! [qvector3d-function] + +//! [sample-page] +/*! + \page generic-guide.html + \title Generic QDoc Guide + \nextpage Creating QDoc Configuration Files + There are three essential materials for generating documentation with qdoc: + + \list + \o \c qdoc binary + \o \c qdocconf configuration files + \o \c Documentation in \c C++, \c QML, and \c .qdoc files + \endlist +*/ +//! [sample-page] + +//! [sample-faq] +/*! + \page altruism-faq.html faq + \title Altruism Frequently Asked Questions + + \brief All the questions about altruism, answered. + + ... +*/ +//! [sample-faq] + +//! [sample-example] +/*! + \title UI Components: Tab Widget Example + \example declarative/ui-components/tabwidget + + This example shows how to create a tab widget. It also demonstrates how + \l {Property aliases}{property aliases} and + \l {Introduction to the QML Language#Default Properties}{default properties} can be used to collect and + assemble the child items declared within an \l Item. + + \image qml-tabwidget-example.png +*/ +//! [sample-example] + +//! [sample-overview] +/*! + \page overview-qt-technology.html overview + \title Overview of a Qt Technology + + \brief provides a technology never seen before. + +*/ +//! [sample-overview] + diff --git a/src/tools/qdoc/doc/examples/signalandslots.qdocinc b/src/tools/qdoc/doc/examples/signalandslots.qdocinc new file mode 100644 index 0000000000..e14ede1441 --- /dev/null +++ b/src/tools/qdoc/doc/examples/signalandslots.qdocinc @@ -0,0 +1,9 @@ +\section1 Signals and Slots + +Signals and slots are used for communication between objects. The signals and +slots mechanism is a central feature of Qt and probably the part that differs +most from the features provided by other frameworks. + +\section2 Introduction + +In GUI programming, when we ... diff --git a/src/tools/qdoc/doc/files/compat.qdocconf b/src/tools/qdoc/doc/files/compat.qdocconf new file mode 100644 index 0000000000..3e7ea6c891 --- /dev/null +++ b/src/tools/qdoc/doc/files/compat.qdocconf @@ -0,0 +1,12 @@ +alias.include = input + +macro.0 = "\\\\0" +macro.b = "\\\\b" +macro.n = "\\\\n" +macro.r = "\\\\r" +macro.img = "\\image" +macro.endquote = "\\endquotation" +macro.relatesto = "\\relates" + +spurious = "Missing comma in .*" \ + "Missing pattern .*" diff --git a/src/tools/qdoc/doc/files/qt.qdocconf b/src/tools/qdoc/doc/files/qt.qdocconf new file mode 100644 index 0000000000..377f0f14c1 --- /dev/null +++ b/src/tools/qdoc/doc/files/qt.qdocconf @@ -0,0 +1,115 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qt +versionsym = +version = %VERSION% +description = Qt Reference Documentation +url = http://qt.nokia.com/doc/4.7 + +edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \ + QtXmlPatterns QtTest +edition.Desktop.modules = QtCore QtDBus QtGui QtNetwork QtOpenGL QtScript QtScriptTools QtSql QtSvg \ + QtWebKit QtXml QtXmlPatterns Qt3Support QtHelp \ + QtDesigner QtAssistant QAxContainer Phonon \ + QAxServer QtUiTools QtTest QtDBus +edition.DesktopLight.modules = QtCore QtDBus QtGui Qt3SupportLight QtTest +edition.DesktopLight.groups = -graphicsview-api + +qhp.projects = Qt + +qhp.Qt.file = qt.qhp +qhp.Qt.namespace = com.trolltech.qt.474 +qhp.Qt.virtualFolder = qdoc +qhp.Qt.indexTitle = Qt Reference Documentation +qhp.Qt.indexRoot = + +# Files not referenced in any qdoc file (last four are needed by qtdemo) +# See also extraimages.HTML +qhp.Qt.extraFiles = classic.css \ + images/qt-logo.png \ + images/taskmenuextension-example.png \ + images/coloreditorfactoryimage.png \ + images/dynamiclayouts-example.png \ + images/stylesheet-coffee-plastique.png + +qhp.Qt.filterAttributes = qt 4.7.4 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.7.4 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.4 +qhp.Qt.subprojects = classes overviews examples +qhp.Qt.subprojects.classes.title = Classes +qhp.Qt.subprojects.classes.indexTitle = Qt's Classes +qhp.Qt.subprojects.classes.selectors = class fake:headerfile +qhp.Qt.subprojects.classes.sortPages = true +qhp.Qt.subprojects.overviews.title = Overviews +qhp.Qt.subprojects.overviews.indexTitle = All Overviews and HOWTOs +qhp.Qt.subprojects.overviews.selectors = fake:page,group,module +qhp.Qt.subprojects.examples.title = Tutorials and Examples +qhp.Qt.subprojects.examples.indexTitle = Qt Examples +qhp.Qt.subprojects.examples.selectors = fake:example + +language = Cpp + +headerdirs = $QTDIR/src \ + $QTDIR/extensions/activeqt \ + $QTDIR/tools/assistant/lib \ + $QTDIR/tools/assistant/compat/lib \ + $QTDIR/tools/designer/src/uitools \ + $QTDIR/tools/designer/src/lib/extension \ + $QTDIR/tools/designer/src/lib/sdk \ + $QTDIR/tools/designer/src/lib/uilib \ + $QTDIR/tools/qtestlib/src \ + $QTDIR/tools/qdbus/src +sourcedirs = $QTDIR/src \ + $QTDIR/doc/src \ + $QTDIR/extensions/activeqt \ + $QTDIR/tools/assistant/lib \ + $QTDIR/tools/assistant/compat/lib \ + $QTDIR/tools/designer/src/uitools \ + $QTDIR/tools/designer/src/lib/extension \ + $QTDIR/tools/designer/src/lib/sdk \ + $QTDIR/tools/designer/src/lib/uilib \ + $QTDIR/tools/qtestlib/src \ + $QTDIR/tools/qdbus + +excludedirs = $QTDIR/src/3rdparty/clucene \ + $QTDIR/src/3rdparty/des \ + $QTDIR/src/3rdparty/freetype \ + $QTDIR/src/3rdparty/harfbuzz \ + $QTDIR/src/3rdparty/kdebase \ + $QTDIR/src/3rdparty/libjpeg \ + $QTDIR/src/3rdparty/libmng \ + $QTDIR/src/3rdparty/libpng \ + $QTDIR/src/3rdparty/libtiff \ + $QTDIR/src/3rdparty/md4 \ + $QTDIR/src/3rdparty/md5 \ + $QTDIR/src/3rdparty/patches \ + $QTDIR/src/3rdparty/sha1 \ + $QTDIR/src/3rdparty/sqlite \ + $QTDIR/src/3rdparty/webkit/JavaScriptCore \ + $QTDIR/src/3rdparty/webkit/WebCore \ + $QTDIR/src/3rdparty/wintab \ + $QTDIR/src/3rdparty/zlib \ + $QTDIR/doc/src/snippets \ + $QTDIR/src/3rdparty/phonon/gstreamer \ + $QTDIR/src/3rdparty/phonon/ds9 \ + $QTDIR/src/3rdparty/phonon/qt7 \ + $QTDIR/src/3rdparty/phonon/waveout + +sources.fileextensions = "*.cpp *.qdoc *.mm" +examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp" + +exampledirs = $QTDIR/doc/src \ + $QTDIR/examples \ + $QTDIR/examples/tutorials \ + $QTDIR \ + $QTDIR/qmake/examples \ + $QTDIR/src/3rdparty/webkit/WebKit/qt/docs +imagedirs = $QTDIR/doc/src/images \ + $QTDIR/examples +outputdir = $QTDIR/doc/html +tagfile = $QTDIR/doc/html/qt.tags +base = file:$QTDIR/doc/html diff --git a/src/tools/qdoc/doc/images/happy.gif b/src/tools/qdoc/doc/images/happy.gif Binary files differnew file mode 100644 index 0000000000..a4597f6fa8 --- /dev/null +++ b/src/tools/qdoc/doc/images/happy.gif diff --git a/src/tools/qdoc/doc/images/happyguy.jpg b/src/tools/qdoc/doc/images/happyguy.jpg Binary files differnew file mode 100644 index 0000000000..e8604793c2 --- /dev/null +++ b/src/tools/qdoc/doc/images/happyguy.jpg diff --git a/src/tools/qdoc/doc/images/qt-logo.png b/src/tools/qdoc/doc/images/qt-logo.png Binary files differnew file mode 100644 index 0000000000..14ddf2a028 --- /dev/null +++ b/src/tools/qdoc/doc/images/qt-logo.png diff --git a/src/tools/qdoc/doc/images/training.jpg b/src/tools/qdoc/doc/images/training.jpg Binary files differnew file mode 100644 index 0000000000..c2ce5c3b21 --- /dev/null +++ b/src/tools/qdoc/doc/images/training.jpg diff --git a/src/tools/qdoc/doc/qdoc-guide.qdoc b/src/tools/qdoc/doc/qdoc-guide.qdoc new file mode 100644 index 0000000000..704e03b7c7 --- /dev/null +++ b/src/tools/qdoc/doc/qdoc-guide.qdoc @@ -0,0 +1,671 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ +/*! + \page qdoc-guide.html + \title Getting Started with QDoc + \nextpage Creating QDoc Configuration Files + + Qt uses QDoc to generate its documentation set into HTML and DITA XML + formats. QDoc uses a set of configuration files to generate documentation + from QDoc comments. The comments have types called + \l{writing-topic-commands}{topics} that determine whether a comment is a + class documentation or a property documentation. A comment may also have + \l{writing-markup}{mark up} to enhance the layout and formatting of the + final output. + + There are three essential materials for generating documentation with qdoc: + \list + \o \c QDoc binary + \o \c qdocconf configuration files + \o \c Documentation in \c C++, \c QML, and \c .qdoc files + \endlist + + This section intends to cover the basic necessities for creating a + documentation set. Additionally, the guide presents special considerations + and options to documenting non-C++ API documentation as well as QML + documentation. Finally, the guide will provide a sample project + documentation and a QML component documentation. + + For specific QDoc information, consult the + \l{Table of Contents}{QDoc Manual}. + \section1 Chapters + + \list 1 + \o \l{Creating QDoc Configuration Files} + \o \l{Writing Documentation} + \o \l{Categories of Documentation} + \o \l{Configuration File Example} + \o \l{QML Documentation Example} + \endlist + +*/ + +/*! + \page qdoc-guide-conf.html + \title Creating QDoc Configuration Files + \previouspage Getting Started with QDoc + \nextpage Writing Documentation + To generate documentation, QDoc uses configuration files, with the + \c qdocconf extension, to store configuration settings. + + The \l{The QDoc Configuration File} article covers the various configuration + variables in greater detail. + + \section1 QDoc Configuration Files + QDoc's configuration settings can reside in a single \e qdocconf file, but + can also be in other qdocconf files. The \c {include(<filepath>)} command + allows configuration files to include other configuration files. + + QDoc has two outputs, HTML documentation and documentation in DITA XML + format. The main distinction between the two outputs is that HTML + documentation needs to have its HTML styling information in the + configuration files. DITA XML documentation does not, and a separate process + can style the documentation in DITA at a later time. DITA XML is therefore + more flexible in allowing different styles to apply to the same information. + + To run qdoc, the project configuration file is supplied as an argument. + \code + qdoc3 project.qdocconf + \endcode + + The project configuration contains information that qdoc uses to create the + documentation. + + \section2 Project Information + + QDoc uses the \c project information to generate the documentation. + \code + project = QDoc Project + description = Sample QDoc project + \endcode + + \target qdoc-input-output-dir + \section2 Input and Output Directories + + Specifying the path to the source directories allow QDoc to find sources and + generate documentation. + + \code + sourcedirs = <path to source code> + exampledirs = <path to examples directory> + imagedirs = <path to image directory> + + sources.fileextensions = "*.cpp *.qdoc *.mm *.qml" + headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx" + examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp *.qml" + examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng" + \endcode + + QDoc will process headers and sources from the ones specified in the + \c fileextensions variable. + + Likewise, QDoc needs the path to the output directory. The \c outputformats + variable determines the type of documentation. These variables should be + in separate configuration files to modularize the documentation build. + \code + outputdir = $SAMPLE_PROJECT/doc/html + outputformats = HTML + \endcode + + QDoc can resolve the paths relative to the qdocconf file as well as + environment variables. + + \note During each QDoc run, the output directory is deleted. + \section2 Extra Files + + QDoc will output generated documentation into the directory specified in + the \l{Input and Output Directories}{output} directory. It is also possible + to specify extra files that QDoc should export. + + \code + extraimages.HTML = extraImage.png \ + extraImage2.png + \endcode + + The \c extraImage.png and the \c extraImage2.png files will be copied to the + HTML output directory. + + \section2 Qt Help Framework Configuration + + QDoc will also export a \l{Qt Help Project} file, in a \c qhp file. + The qhp file is then used by the \c qhelpgenerator to package the + documentation into a \c qch file. Qt Creator and Qt Assistant reads the qch + file to display the documentation. + + The \l {Creating Help Project Files} article covers the configuration + options. + + \section2 HTML Configuration + + QDoc has an HTML generator that will export a set of documentation into + HTML files using various configuration settings. QDoc will place the + generated documentation into the directory specified by the \c outputdir + variable. + + \code + outputformats = HTML + outputdir = <path to output directory> + \endcode + + QDoc needs to know where the styles and templates for generating HTML + are located. Typically, the templates directory contains a \c scripts, + \c images, and a \c style directory, containing scripts and CSS files. + + \code + HTML.templatedir = <path to templates> + \endcode + + The main configuration variables are: + \code + HTML.postheader + HTML.postpostheader + HTML.postheader + HTML.footer + + HTML.headerstyles + HTML.stylesheets = style.css \ + style1.css + + HTML.scripts = script.js + \endcode + + The \c{HTML.headerstyles} variable inserts the style information into the + HTML file and the \c{HTML.stylesheets} specifies which files QDoc should + copy into the output directory. As well, QDoc will embed the string + in the \c postheader, \c footer, and related variables into each HTML file. + + The \l {HTML Specific Configuration Variables} article outlines the usage + of each variable. + + \section2 DITA XML Configuration + + DITA XML output is enabled using the \c outputformats variable. Unlike HTML + documentation, QDoc does not need HTML style templates for generating + documentation in DITA XML format. + + \code + outputformats = DITAXML + outputdir + \endcode + + \section2 Qt Index Reference + Documentation projects can link to Qt APIs and other articles by specifying + the path to the \c qt.index file. When qdoc generates the Qt Reference + Documentation, it will also generate an index file, containing the URLs to + the articles. Other projects can use the links in the index file so that + they can link to other articles and API documentation within Qt. + + \code + indexes = $QT_INSTALL_DOCS/html/qt.index $OTHER_PROJECT/html/qt.index + \endcode + It is possible to specify multiple index files from several projects. + + \section1 Macros and Other Configurations + + Macros for substituting HTML characters exist and are helpful for generating + specific HTML-valid characters. + + \code + macro.pi.HTML = "Π" + \endcode + The snippet code will replace any instances of \c{\\pi} with \c Π in the + HTML file, which will appear as the Greek \pi symbol when viewed in + browsers. + + There is quite a long list of macros for inserting text and + \l{alias-variable}{aliases} available. + + \section2 QML Additions + + QDoc is able to parse QML files for QDoc comments. QDoc will parse files + with the QML extension, \c{.qml}, if the extension type is included in the + \l{Input and Output Directories}{fileextensions} variable. + + Also, the generated HTML files can have a prefix, specified in the QDoc + configuration file. + \code + outputprefixes = QML + outputprefixes.QML = qml-components- + \endcode + The outputprefixes will, for example, prefix QML components HTML filenames. + \code + files: + qml-components-button.html + qml-components-scrollbar.html + \endcode + +*/ + +/*! + \page qdoc-guide-writing.html + \title Writing Documentation + \previouspage Creating QDoc Configuration Files + \nextpage Categories of Documentation + + \section1 QDoc Comments + Documentation is contained within qdoc \e comments, delimited by + \beginqdoc and \endqdoc comments. Note that these are valid comments + in C++, QML, and JavaScript. + + QDoc will parse C++ and QML files to look for qdoc comments. To explicitly + omit a certain file type, omit it from the + \l{Input and Output Directories}{configuration} file. + + \section1 QDoc Commands + + QDoc uses \e commands to retrieve information about the documentation. \c + Topic commands determine the type of documentation element, the \c context + commands provide hints and information about a topic, and \c markup commands + provide information on how QDoc should format a piece of documentation. + + \target writing-topic-commands + \section2 QDoc Topics + Each qdoc comment must have a \e topic type. A topic distinguishes it from + other topics. To specify a topic type, use one of the several + \l{Topic Commands}{topic commands}. + + QDoc will collect similar topics and create a page for each one. For + example, all the enumerations, properties, functions, and class description + of a particular C++ class will reside in one page. A generic page is + specified using the \l{page-command}{\\page} command and the filename is the + argument. + + Example of topic commands: + \list + \o \l{enum-command}{\\enum} - for enumeration documentation + \o \l{class-command}{\\class} - for C++ class documentation + \o \l{qmlclass-command}{\\qmlclass} - for QML component documentation + \o \l{page-command}{\\page} - for creating a page. + \endlist + + The \l{page-command}{\\page} command is for creating articles that are not + part of source documentation. The command can also accept two arguments: the + file name of the article and the documentation type. The possible types are: + \list + \o \c howto + \o \c overview + \o \c tutorial + \o \c faq + \o \c article - \e default when there is no type + \endlist + + \snippet examples/samples.qdocinc sample-faq + + The \l{Topic Commands} page has information on all of the available topic + commands. + + \target writing-context + \section2 Topic Contexts + + Context commands give QDoc a hint about the \e context of the topic. For + example, if a C++ function is obsolete, then it should be marked obsolete + with the \l{obsolete-command}{\\obsolete} command. Likewise, + \l{nextpage-command}{page navigation} and \l{title-command}{page title} + give extra page information to QDoc. + + QDoc will create additional links or pages for these contexts. For example, + a group is created using the \l{group-command}{\\group} command and the + members have the \l{ingroup-command}{\\ingroup} command. The group name is + supplied as an argument. + + The \l{Context Commands} page has a listing of all the available context + commands. + + \target writing-markup + \section2 Documentation Markup + + QDoc can provide \e markup to content similar to other markup or + documentation tools. QDoc can mark a section of text in \bold{bold} when + the text is marked up with the \l{bold-command}{\\bold} command. + + \code + \bold{This} text will be in \bold{bold}. + \endcode + + The \l{Markup Commands} page has a full listing of the available markup + commands. + + \section1 Anatomy of Documentation + + Essentially, for QDoc to create a page, there must be some essential + ingredients present. + + \list + \o Assign a topic to a QDoc comment - A comment could be a page, a + property documentation, a class documentation, or any of the available + \l{Topic Commands}{topic commands}. + + \o Give the topic a context - QDoc can associate certain topics to other + pages such as associating obsolete functions when the documentation is + marked with \l{obsolete-command}{\\obsolete}. + + \o Mark sections of the document with + \l{Markup Commands}{markup commands} - QDoc can create layouts and + format the documentation for the documentation. + \endlist + + In Qt, the \l{QVector3D} class was documented with the following QDoc + comment: + \snippet examples/samples.qdocinc qvector3d-class + + It has a constructor, \l{QVector3D::QVector3D()}, which was documented with + the following QDoc comment: + \snippet examples/samples.qdocinc qvector3d-function + + The different comments may reside in different files and QDoc will collect + them depending on their topic and their context. The resulting documentation + from the snippets are generated into the \l{QVector3D} class documentation. + + Note that if the documentation immediately precedes the function or class + in the source code, then it does not need to have a topic. QDoc will assume + that the documentation above the code is the documentation for that code. + + An article is created using \l{page-command}{\\page} command. The first + argument is the HTML file that QDoc will create. The topic is supplemented + with context commands, the \l{title-command}{\\title} and + \l{nextpage-command}{\\nextpage} commands. There are several other + QDoc commands such as the \l{list-command}{\\list} command. + \snippet examples/samples.qdocinc sample-page + + The section on \l{QDoc Topics}{topic commands} gives an overview on several + other topic types. + + +*/ + +/*! + \page qdoc-categories.html + \title Categories of Documentation + \previouspage Writing Documentation + \nextpage Configuration File Example + \brief Describes the different types such as How-To's, Tutorials, Overviews, + Examples, and Class Documentation. + + There are several types of predefined documentation \e categories or + \e types: + \list + \o How-To's + \o Tutorial + \o Overview + \o Article + \o FAQ (Frequently Asked Questions) + \o C++ API Documentation + \o QML Component Documentation + \o Code Example + \endlist + + QDoc has the ability to format a page depending on the type. Further, + stylesheets can provide additional control on the display of each category. + + \section1 API Documentation + QDoc excels in the creation of API documentation given a set of source code + and documentation in QDoc comments. Specifically, QDoc is aware of Qt's + architecture and can validate the existence of Qt C++ class, function, or + property documentation. QDoc gives warnings and errors if it cannot + associate a documentation with a code entity or if a code entity does not + have documentation. + + In general, every Qt code entity such as properties, classes, methods, + signals, and enumerations have a corresponding + \l{qdoc-topics}{topic command}. QDoc will associate the documentation to the + source using C++ naming rules. + + QDoc will parse the header files (typically \c .h files) to build a tree of + the class structures. Then QDoc will parse the source files and + documentation files to attach documentation to the class structure. + Afterwards, QDoc will generate a page for the class. + + \note QDoc uses the header files to inform itself about the class and will + not properly process QDoc comments in header files. + + \keyword qml-documentation + \section2 Documenting QML Components + + In the world of \l{Qt Quick}{QML}, there are additional entities we need to + document such as QML signals, attached properties, and QML methods. + Internally, they use Qt technologies, however, QML API documentation + requires different layout and naming conventions from the Qt C++ API + documentation. + + A list of QML related QDoc commands: + \list + \o \l{qmlattachedproperty-command}{\\qmlattachedproperty} + \o \l{qmlattachedsignal-command}{\\qmlattachedsignal} + \o \l{qmlbasictype-command}{\\qmlbasictype} + \o \l{qmlclass-command}{\\qmlclass} - creates a QML component documentation + \o \l{qmlmethod-command}{\\qmlmethod} + \o \l{qmlproperty-command}{\\qmlproperty} + \o \l{qmlsignal-command}{\\qmlsignal} + \o \l{inherits-command}{\\inherits} + \o \l{qmlmodule-command}{\\qmlmodule} + \o \l{inqmlmodule-command}{\\inqmlmodule} + + \endlist + + \note Remember to enable QML parsing by including the \c{*.qml} filetype in + the \l{qdoc-input-output-dir}{fileextension} variable. + + Essentially, to create an API documentation of a QML component, create + a QDoc comment with a \l{qmlclass-command}{\\qmlclass} command. Similar to + C++ API documentation, QML API are documented using their corresponding QDoc + commands. + + \section3 QML Parser + + You may either document a QML component rom a C++ class or from a QML file. + QDoc supports both types and can parse both C++ and QML files. + + In a QML file, you may simply place the QDoc comment above the property, + signal, handler, or method. QDoc will collect the comments and form the API + documentation. Additionally, QDoc can detect + \l{qml-property-aliases}{property-aliases}, but the property type must be + manually declared using the \l{qmlproperty-command}{\\qmlproperty} command. + + QML components in C++ classes have their documentation above their property + or method documentation. However, the QML commands must be used instead of + the C++ documentation topic commands. Instead of the \c {\\class} + command, use the \l{qmlclass-command}{\\qmlclass} command. + + \section3 QML Modules + + A component belongs to a component \e set or a \e module. The module + may include all the related components for a platform or contain a certain + version of \l{Qt Quick}. For example, the Qt Quick 2 \l{QML Elements} belong + to the QtQuick2 module while there is also a QtQuick1 module for the older + elements introduced in Qt 4. + + Modules affect the way Qdoc link and relate the components. The + \l{qmlclass-command}{\\qmlclass} topic command must have an + \l{inqmlmodule-command}{\\inqmlmodule} context command to relate the + component to a module. Similarly, a \l{qmlmodule-command}{\\qmlmodule} topic + command must exist in a separate \c .qdoc file to create the overview page + for the module. The overview page will list the related components. + + The links to the components, must therefore, also contain the module name. + For example, if a component called \c TabWidget is in the \c UIComponents + module, it must be linked as \c {UIComponents::TabWidget}. + + The \l{componentset}{UIComponents} example demonstrates proper usage of + QDoc commands to document QML components and QML modules. + + \section3 Read-only and Internal QML Properties + + QDoc detects QML properties that are marked as \c readonly. Note that the + property must be initialized with a value. + + \code + readonly property int sampleReadOnlyProperty: 0 + \endcode + For example, the example \l{TabWidget} component has a fictitious read-only + property \c sampleReadOnlyProperty. Its declaration has the \c readonly + identifier and it has an initial value. + + Properties and signals that are not meant for the public interface may + be marked with the \l{internal-command}{\\internal} command. QDoc will not + publish the documentation in the generated outputs. + + \section1 Articles & Overviews + Articles and overviews are a style of writing best used for providing + summary detail on a topic or concept. It may introduce a technology or + discuss how a concept may be applied, but without discussing exact steps + in too much detail. However, this type of content could provide the entry + point for readers to find instructional and reference materials that do, + such as tutorials, examples and class documentation. An example of an + overview might be a product page, such as a top level discussion of + QtQuick, individual modules, design principles, or tools. + + To signify that a document is an article, you append the article keyword + to the \\page command: + + \snippet examples/samples.qdocinc sample-overview + + The \l{writing-topic-commands}{writing topic commands} section has a listing + of the available \\page command arguments. + + \section1 Tutorials, How-To's, FAQ's + + Tutorials, How-To's, and FAQ's are all instructional material, in that they + instruct or prescribe to the reader. Tutorials are content designed to guide + the reader along a progressive learning path for a concept or technology. + How-To's and FAQ's (\e{Frequently Asked Questions}) provide guidance by + presenting material in the form of answers to commonly asked topics. + How-To's and FAQ's are designed for easy reference and are not necessarily + presented in a linear progression. + + To create these types, mark the pages by providing a \c type argument to the + \l{page-command}{\\page} command. The \c type argument is the second + argument, with the file name being the first. + \snippet examples/samples.qdocinc sample-faq + + The \l{writing-topic-commands}{writing topic commands} section has a listing + of the available \\page command arguments. + + \section1 Code Examples + Examples are an effective way to demonstrate practical usage of a given + technology or concept. When it comes to middleware this is usually in the + form of an application using simple code and clear explanations of what the + code is doing. Any module, API, project, pattern etc. should have at least + one good example. + + An example may have an accompanying tutorial. The tutorial instructs and + describes the code, while the code example is the code content that users + may study. Code examples may have accompanying text that are not in the + tutorial. + + QDoc will create a page containing the example code with a description + using the \l{example-command}{\\example} command. + + \snippet examples/samples.qdocinc sample-example + + QDoc will use the directory specified in the input + \l{Input and Output Directories}{exampledirs} variable to find the Qt + Project (\c .pro) file to generate the example files. The generated HTML + will have the filename, \c {declarative-ui-components-tabwidget.html}. QDoc + will also list all of the example code. For reference, view QDoc's generated + page for the \l{UI Components: Tab Widget Example}{Tab Widget} example. + + \note The example's project file must be the same as the + directory name. +*/ + +/*! + \example config + \title Configuration File Example + \previouspage Categories of Documentation + \brief configuration files for the QDoc Manual and QDoc Guide + + The QDoc Manual uses these \c qdocconf files to generate the QDoc Guide and + the \l{Table of Contents}{QDoc Manual}. + + \note The configuration files are similar to the Qt Reference Documentation + and the QDoc Manual do not use all of the variables. The variables are + included to demonstrate a full project scenario. + + \section1 Macros and other Definitions + \list + \o \l{config/compat.qdocconf} + \o \l{config/macros.qdocconf} + \o \l{config/qt-cpp-ignore.qdocconf} + \o \l{config/qt-defines.qdocconf} + \endlist + + QDoc allows macros to help with aliasing and for inputting special HTML + characters within the documentation. Macros are a form of workarounds if + QDoc is unable to resolve formatting issues such as when QDoc should + disregard QDoc comments in documentation paragraphs. + + QDoc is also aware of the common C++ and Qt preprocessors and can decide + which documentation to generate according to the definitions in the + configuration files. + + \section1 Project Information + \list + \o \l{config/qdoc-online.qdocconf} + \o \l{config/qdoc.qdocconf} + \o \l{config/qdoc-project.qdocconf} + \endlist + + These configuration files dictate how QDoc will generate the project. + Depending which configuration file QDoc processes, the formatting and the + information about the project will be different. If QDoc processes + \c{qdoc-online.qdocconf}, QDoc will generate the HTML version of the manual + will have the style suitable for online viewing. + + Additionally, the settings for creating the + \l{The Qt Help Framework}{Qt Help File} is in the configuration. + + \note The project file uses variables used during Qt's + \l{Configuration Options for Qt}{configuration} step. + + \section1 HTML Styles + \list + \o \l{config/qt-html-default-styles.qdocconf} + \o \l{config/qt-html-online-styles.qdocconf} + \o \l{config/qt-html-templates-online.qdocconf} + \o \l{config/qt-html-templates.qdocconf} + \endlist + + These files indicate which styles QDoc should use for the HTML formats. + Typically, there are two templates, one for online viewing and one for + offline. Qt Creator is able to fit more content in a page with the offline + template. The templates store HTML information as strings and QDoc will copy + the template to each HTML page. + + \section1 Project File + \list + \o \l{config/config.pro} + \endlist + + Every example page (such as this one) needs a Qt project file. QDoc will + use the project file to determine which files are part of the example. QDoc + will then create a page listing all the files that are part of the example. + + \note the directory name of the example and the name of the project file + must match. The example directory is found using the + \l{qdoc-input-output-dir}{exampledirs} variable. +*/ + diff --git a/src/tools/qdoc/doc/qdoc-manual.qdoc b/src/tools/qdoc/doc/qdoc-manual.qdoc new file mode 100644 index 0000000000..f3e01658fd --- /dev/null +++ b/src/tools/qdoc/doc/qdoc-manual.qdoc @@ -0,0 +1,8780 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** 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$ +** +****************************************************************************/ + +/*! + \page index.html + \nextpage Introduction to QDoc + + \title Table of Contents + + \list + \o \l {Introduction to QDoc} + \o \l {Getting Started with QDoc} + \o \l {Command Index} + \o \l {Topic Commands} + \o \l {Context Commands} + \list + \o \l {Document Navigation} + \o \l {Reporting Status} + \o \l {Thread Support} + \o \l {Relating Things} + \o \l {Grouping Things} + \o \l {Naming Things} + \endlist + \o \l{Markup Commands} + \list + \o \l {Text Markup} + \o \l {Document Structure} + \o \l {Including Code Inline} + \o \l {Including External Code} + \o \l {Creating Links} + \o \l {Including Images} + \o \l {Tables and Lists} + \o \l {Special Content} + \o \l {Miscellaneous} + \endlist + \o \l {The QDoc Configuration File} + \list + \o \l {Generic Configuration Variables} + \o \l {Creating Help Project Files} + \o \l {C++ Specific Configuration Variables} + \o \l {HTML Specific Configuration Variables} + \o \l {Supporting Derived Projects} + \o \l {Compatibility Issues} + \o \l {qt.qdocconf} + \o \l {minimum.qdocconf} + \o \l {Generating DITA XML Output} + \endlist + \endlist + +*/ + +/*! + \page 01-qdoc-manual.html + \contentspage Table of Contents + \previouspage Table of Contents + \nextpage Command Index + + \title Introduction to QDoc + + QDoc is a tool used by Qt Developers to generate documentation for + software projects. It works by extracting \e {qdoc comments} from + project source files and then formatting these comments as HTML + pages or DITA XML documents, etc. QDoc finds qdoc comments in \c + {.cpp} files and in \c {.qdoc} files. QDoc does not look for qdoc + comments in \c {.h} files. A qdoc comment always begins with an + exclamation mark \bold{!} e.g.: + + \code + / *! + \class QObject + \brief The QObject class is the base class of all Qt objects. + + \ingroup objectmodel + + \reentrant + + QObject is the heart of the Qt \l{Object Model}. The + central feature in this model is a very powerful mechanism + for seamless object communication called \l{signals and + slots}. You can connect a signal to a slot with connect() + and destroy the connection with disconnect(). To avoid + never ending notification loops you can temporarily block + signals with blockSignals(). The protected functions + connectNotify() and disconnectNotify() make it possible to + track connections. + + QObjects organize themselves in \l {Object Trees & + Ownership} {object trees}. When you create a QObject with + another object as parent, the object will automatically + add itself to the parent's children() list. The parent + takes ownership of the object; i.e., it will automatically + delete its children in its destructor. You can look for an + object by name and optionally type using findChild() or + findChildren(). + + Every object has an objectName() and its class name can be + found via the corresponding metaObject() (see + QMetaObject::className()). You can determine whether the + object's class inherits another class in the QObject + inheritance hierarchy by using the inherits() function. + + .... + * / + \endcode + + From the qdoc comment above, QDoc generates the now famous HTML + page \l {http://doc.qt.nokia.com/qobject.html#details} + {QObject Class Reference}. + + This manual explains how to use the QDoc commands in qdoc comments + to embed good documentation in your source files. It also explains + how to make a \l {The QDoc Configuration File} {QDoc configuration + file}, which you will pass to QDoc on the command line. + + \section1 Running QDoc + + The current name of the QDoc program is \c {qdoc3}. To run qdoc3 + from the command line, give it the name of a configuration file: + + \quotation + \c {$ ../../bin/qdoc3 ./config.qdocconf} + \endquotation + + QDoc recognizes the \c {.qdocconf} suffix as a \l{The QDoc + Configuration File} {QDoc configuration file}. The configuration + file is where you tell QDoc where to find the project source + files, header files, and \c {.qdoc} files. It is also where you + tell QDoc what kind of output to generate (HTML, DITA XML,...), + and where to put the generated documentation. The configuration + file also contains other information for QDoc. + + See \l{The QDoc Configuration File} for a instructions on how to + build a Qdoc configuration file. + + \section1 How QDoc Works + + QDoc begins by reading the configuarion file you specified on the + command line. It stores all the variables from the configuration + file for later use. One of the first variables it uses is \c + {outputformats}. This variable tells QDoc which output generators + it will run. The default value is \e {HTML}, so if you don't set + \c {outputformats} in your configuration file, QDoc will generate + HTML output. That's usually what you will want anyway, but you can + also specify \e {DITAXML} to get DITA XML output instead. + + Next, QDoc uses the values of the \l + {22-qdoc-configuration-generalvariables.html#headerdirs-variable} + {headerdirs} variable and/or the \l + {22-qdoc-configuration-generalvariables.html#headers-variable} + {headers} variable to find and parse all the header files for your + project. QDoc does \e not scan header files for qdoc comments. It + parses the header files to build a master tree of all the items + that should be documented (i.e. the items that QDoc should find + qdoc comments for). + + After parsing all the header files and building the master tree of + items to be documented, QDoc uses the value of the \l + {22-qdoc-configuration-generalvariables.html#sourcedirs-variable} + {sourcedirs} variable and/or the value of the \l + {22-qdoc-configuration-generalvariables.html#sources-variable} + {sources} variable to find and parse all the \c {.cpp} and \c + {.qdoc} files for your project. These are the files QDoc scans for + \e {qdoc comments}. Remember that a qdoc comment begins with + an exclamation mark, i.e. \bold {/*!} . + + For each qdoc comment it finds, it searches the master tree for + the item where the documentation belongs. The it interprets the + qdoc commands in the comment and stores the interpreted commands + and the comment text in the tree node for the item. + + Finally, QDoc traverses the master tree. For each node, if the + node has stored documentation, QDoc calls the output generator + specified by the \c {outputformats} variable to format and write + the documentation in the directory specified in the configuration + file in the \l + {22-qdoc-configuration-generalvariables.html#outputdir-variable} + {outputdir} variable. + + \section1 Command Types + + QDoc interprets three types of commands: + + \list + \o \l {Topic Commands} + \o \l {Context Commands} + \o \l {Markup Commands} + \endlist + + Topic commands identify the elememt you are documenting, e.g. a C++ + class, function, or type, an example, or an extra page of text + that doesn't map to an underlying C++ elememnt. + + Context commands tell QDoc how the element being documented + relates to other documented elememnts, e.g. next and previous page + links or inclusion in page groups or library modules. Context + commands can also provide information about the documented element + that QDoc can't get from the source files, e.g. whether the + element is thread-safe, an overloaded or reimplemented function, + or that it has been deprecated. + + Markup commands tell QDoc how text and image elements in the + document should be rendered, or about the document's outline + structure. +*/ + +/*! + \page 03-qdoc-commands-markup.html + \contentspage Table of Contents + \previouspage Naming Things + \nextpage Text Markup + + \title Markup Commands + + The markup commands indicate the generated documentation's visual + appearance and logical structure. + + \list + \o \l {04-qdoc-commands-textmarkup.html#a-command} {\\a} + \o \l {11-qdoc-commands-specialcontent.html#abstract-command} {\\abstract} + \o \l {12-0-qdoc-commands-miscellaneous.html#annotatedlist-command} {\\annotatedlist} + \o \l {06-qdoc-commands-includecodeinline.html#badcode-command} {\\badcode} + \o \l {04-qdoc-commands-textmarkup.html#bold-command} {\\bold} + \o \l {11-qdoc-commands-specialcontent.html#brief-command} {\\brief} + \o \l {04-qdoc-commands-textmarkup.html#c-command} {\\c} + \o \l {09-qdoc-commands-includingimages.html#caption-command} {\\caption} + \o \l {05-qdoc-commands-documentstructure.html#chapter-command} {\\chapter} + \o \l {06-qdoc-commands-includecodeinline.html#code-command} {\\code} + \o \l {07-0-qdoc-commands-includingexternalcode.html#codeline-command} {\\codeline} + \o \l {04-qdoc-commands-textmarkup.html#div-command} {\\div} + \o \l {07-0-qdoc-commands-includingexternalcode.html#dots-command} {\\dots} + \o \l {12-0-qdoc-commands-miscellaneous.html#else-command} {\\else} + \o \l {12-0-qdoc-commands-miscellaneous.html#endif-command} {\\endif} + \o \l {12-0-qdoc-commands-miscellaneous.html#expire-command} {\\expire} + \o \l {11-qdoc-commands-specialcontent.html#footnote-command} {\\footnote} + \o \l {12-0-qdoc-commands-miscellaneous.html#generatelist-command} {\\generatelist} + \o \l {10-qdoc-commands-tablesandlists.html#header-command} {\\header} + \o \l {04-qdoc-commands-textmarkup.html#i-command} {\\i} + \o \l {12-0-qdoc-commands-miscellaneous.html#if-command} {\\if} + \o \l {09-qdoc-commands-includingimages.html#image-command} {\\image} + \o \l {12-0-qdoc-commands-miscellaneous.html#include-command} {\\include} + \o \l {12-0-qdoc-commands-miscellaneous.html#include-command} {\\input} + \o \l {09-qdoc-commands-includingimages.html#inlineimage-command} {\\inlineimage} + \o \l {08-qdoc-commands-creatinglinks.html#keyword-command} {\\keyword} + \o \l {08-qdoc-commands-creatinglinks.html#l-command} {\\l} + \o \l {11-qdoc-commands-specialcontent.html#legalese-command} {\\legalese} + \o \l {10-qdoc-commands-tablesandlists.html#list-command} {\\list} + \o \l {12-0-qdoc-commands-miscellaneous.html#meta-command} {\\meta} + \o \l {06-qdoc-commands-includecodeinline.html#newcode-command} {\\newcode} + \o \l {10-qdoc-commands-tablesandlists.html#o-command} {\\o} + \o \l {06-qdoc-commands-includecodeinline.html#oldcode-command} {\\oldcode} + \o \l {12-0-qdoc-commands-miscellaneous.html#omit-command} {\\omit} + \o \l {05-qdoc-commands-documentstructure.html#part-command} {\\part} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printline-command} {\\printline} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printto-command} {\\printto} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printuntil-command} {\\printuntil} + \o \l {11-qdoc-commands-specialcontent.html#quotation-command} {\\quotation} + \o \l {07-0-qdoc-commands-includingexternalcode.html#quotefile-command} {\\quotefile} + \o \l {07-0-qdoc-commands-includingexternalcode.html#quotefromfile-command} {\\quotefromfile} + \o \l {12-0-qdoc-commands-miscellaneous.html#raw-command} {\\raw} + \o \l {10-qdoc-commands-tablesandlists.html#row-command} {\\row} + \o \l {08-qdoc-commands-creatinglinks.html#sa-command} {\\sa} + \o \l {05-qdoc-commands-documentstructure.html#sectionOne-command} {\\section1} + \o \l {05-qdoc-commands-documentstructure.html#sectionTwo-command} {\\section2} + \o \l {05-qdoc-commands-documentstructure.html#sectionThree-command} {\\section3} + \o \l {05-qdoc-commands-documentstructure.html#sectionFour-command} {\\section4} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipline-command} {\\skipline} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipto-command} {\\skipto} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipuntil-command} {\\skipuntil} + \o \l {07-0-qdoc-commands-includingexternalcode.html#snippet-command} {\\snippet} + \o \l {04-qdoc-commands-textmarkup.html#span-command} {\\span} + \o \l {04-qdoc-commands-textmarkup.html#sub-command} {\\sub} + \o \l {04-qdoc-commands-textmarkup.html#sup-command} {\\sup} + \o \l {10-qdoc-commands-tablesandlists.html#table-command} {\\table} + \o \l {11-qdoc-commands-specialcontent.html#tableofcontents-command} {\\tableofcontents} + \o \l {08-qdoc-commands-creatinglinks.html#target-command} {\\target} + \o \l {04-qdoc-commands-textmarkup.html#tt-command} {\\tt} + \o \l {04-qdoc-commands-textmarkup.html#underline-command} {\\underline} + \o \l {12-0-qdoc-commands-miscellaneous.html#raw-command} {\\unicode} + \o \l {11-qdoc-commands-specialcontent.html#warning-command} {\\warning} + \o \l {04-qdoc-commands-textmarkup.html#backslash-command} {\\\\} + \endlist +*/ + +/*! + \page 04-qdoc-commands-textmarkup.html + \contentspage Table of Contents + \previouspage Markup Commands + \nextpage Document Structure + + \title Text Markup + + The text formatting commands indicate how text is to be rendered. + + \target a-command + \section1 \\a (parameter marker) + + The \\a command tells QDoc the next word is a formal parameter name. + + A warning is emitted when a formal parameter is not documented or + is misspelled, so when you document a function you should mention + each formal parameter by name in the function description, + preceded by the \\a command. The parameter name is then rendered + in italics. + + \code + / *! + Constructs a line edit containing the text + \a contents. The \a parent parameter is sent + to the QWidget constructor. + * / + + QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent) + { + ... + } + + \endcode + + QDoc renders this as: + + \quotation + \bold {QLineEdit::QLineEdit ( const QString & + contents, QWidget *parent )} + + Constructs a line edit containing the text \a contents. + The \a parent parameter is sent to the QWidget constructor. + \endquotation + + You can enclose the formal parameter name in curly brackets, if + you want to, but it isn't necessary. + + \target c-command + \section1 \\c (code font) + + The \\c command is used for rendering variable names, user-defined + class names, and C++ keywords (e.g. \c int and \c for) in the code + font. + + The command renders its argument using a typewriter font. For + example: + + \code + / *! + The \c AnalogClock class provides a clock widget with hour + and minute hands that is automatically updated every + few seconds. + * / + \endcode + + QDoc renders this as: + + \quotation + The \c AnalogClock class provides a clock widget with hour + and minute hands that is automatically updated every + few seconds. + \endquotation + + If the text to be rendered in the code font contains spaces, enclose the + entire text in curly brackets. + + \code + \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)} + \endcode + + QDoc renders this as: + + \quotation + \c {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)} + \endquotation + + The \\c command accepts the special character \c \ within its + argument, i.e. it renders it as a normal character. So if you want + to use nested commands, you must use the \l {tt-command} {teletype + (\\tt)} command instead. + + See also \l {tt-command} {\\tt} and \l {code-command} {\\code}. + + \target div-command + \section1 \\div + + The \\div and \\enddiv commands delimit a large or small block of + text (which may include other QDoc commands) to which special + formatting attributes should be applied. + + An argument must be provided in curly braces, as in the qdoc + comment shown below. The argument is not interpreted but is used + as attribute(s) of the tag that is ultimately output by qdoc. + + For example, we might want to render an inline image so that it + floats to the right of the current block of text: + + \code + / *! + \div {class="float-right"} + \inlineimage qml-column.png + \enddiv + + * / + \endcode + + If qdoc is generating HTML, it will translate these commands to: + + \code + <div class="float-right"><p><img src="images/qml-column.png" /></p></div> + \endcode + + For HTML, the attribute value \e {float-right} then will refer to + a clause in the style.css file. which in this case could be: + + \code + div.float-right + { + float: right; margin-left: 2em + } + \endcode + + If qdoc is generating DITA XML, it will translate the commands to: + + \code + <sectiondiv outputclass="float-right"> + <p> + <fig> + <image href="images/qml-column.png" placement="inline"/> + </fig> + </p> + </sectiondiv> + \endcode + + Your DITA XML publishing program must then recognize the \e + {outputclass} attribute value. + + \note The \bold {\\div} command can be nested. + + Below is an example taken from the index.qdoc file used to + generate index.html for Qt 4.7: + + \code + \div {class="indexbox guide"} + \div {class="heading"} + Qt Developer Guide + \enddiv + \div {class="indexboxcont indexboxbar"} + \div {class="section indexIcon"} \emptyspan + \enddiv + \div {class="section"} + Qt is a cross-platform application and UI + framework. Using Qt, you can write web-enabled + applications once and deploy them across desktop, + mobile and embedded operating systems without + rewriting the source code. + \enddiv + \div {class="section sectionlist"} + \list + \o \l{Getting Started Guides} {Getting started} + \o \l{Installation} {Installation} + \o \l{how-to-learn-qt.html} {How to learn Qt} + \o \l{tutorials.html} {Tutorials} + \o \l{Qt Examples} {Examples} + \o \l{qt4-7-intro.html} {What's new in Qt 4.7} + \endlist + \enddiv + \enddiv + \enddiv + \endcode + + When all the class attribute values are defined as they are in the + style.css file that is used for rendering the Qt 4.7 documentation, + the above example is rendered as: + + \div {class="indexbox guide"} + \div {class="heading"} + Qt Developer Guide + \enddiv + \div {class="indexboxcont indexboxbar"} + \div {class="section indexIcon"} \emptyspan + \enddiv + \div {class="section"} + Qt is a cross-platform application and UI + framework. Using Qt, you can write web-enabled + applications once and deploy them across desktop, + mobile and embedded operating systems without + rewriting the source code. + \enddiv + \div {class="section sectionlist"} + \list + \o \l{Getting Started Guides} {Getting started} + \o \l{Installation} {Installation} + \o \l{how-to-learn-qt.html} {How to learn Qt} + \o \l{tutorials.html} {Tutorials} + \o \l{Qt Examples} {Examples} + \o \l{qt4-7-intro.html} {What's new in Qt 4.7} + \endlist + \enddiv + \enddiv + \enddiv + + When generating DITA XML, qdoc outputs the nested \e {div} commands as: + + \code + <sectiondiv outputclass="indexbox guide"> + <sectiondiv outputclass="heading"> + <p>Qt Developer Guide</p> + </sectiondiv> + <sectiondiv outputclass="indexboxcont indexboxbar"> + <sectiondiv outputclass="section indexIcon"/> + <sectiondiv outputclass="section"> + <p>Qt is a cross-platform application and UI + framework. Using Qt, you can write + web-enabled applications once and deploy + them across desktop, mobile and embedded + operating systems without rewriting the + source code. + </p> + </sectiondiv> + <sectiondiv outputclass="section sectionlist"> + <ul> + <li> + <xref href="gettingstarted.xml#id-606ee7a8-219b-47b7-8f94-91bc8c76e54c">Getting started</xref> + </li> + <li> + <xref href="installation.xml#id-075c20e2-aa1e-4f88-a316-a46517e50443">Installation</xref> + </li> + <li> + <xref href="how-to-learn-qt.xml#id-49f509b5-52f9-4cd9-9921-74217b9a5182">How to learn Qt</xref> + </li> + <li> + <xref href="tutorials.xml#id-a737f955-a904-455f-b4aa-0dc69ed5a64f">Tutorials</xref> + </li> + <li> + <xref href="all-examples.xml#id-98d95159-d65b-4706-b08f-13d80080448d">Examples</xref> + </li> + <li> + <xref href="qt4-7-intro.xml#id-519ae0e3-4242-4c2a-b2be-e05d1e95f177">What's new in Qt 4.7</xref> + </li> + </ul> + </sectiondiv> + </sectiondiv> + </sectiondiv> + \endcode + + Your DITA XML publishing program must recognize the values of the + \e {outputclass} attribute. + + See also \l {span-command} {\\span}. + + \target span -command + \section1 \\span + + The \\span command is for applying special formatting + attributes to a small block of text. + + Two arguments must be provided, each argument in curly braces, as + shown in the qdoc comment below. The first argument is not + interpreted but is used as the formatting attribute(s) of the tag + that is ultimately output by qdoc. The second argument is the text + to be rendered with the special formatting attributes. + + For example, we might want to render the first word of each + element in a numeric list in blue. + + \code + / *! + Global variables with complex types: + \list 1 + \o \span {class="variableName"} {mutableComplex1} in globals.cpp at line 14 + \o \span {class="variableName"} {mutableComplex2} in globals.cpp at line 15 + \o \span {class="variableName"} {constComplex1} in globals.cpp at line 16 + \o \span {class="variableName"} {constComplex2} in globals.cpp at line 17 + \endlist + * / + \endcode + + Class \e {variableName} refers to a clause in your style.css. + + \code + .variableName + { + font-family: courier; + color: blue + } + \endcode + + Using the \e {variableName} clause shown above, the example is rendered as: + + Global variables with complex types: + \list 1 + \o \span {class="variableName"} {mutableComplex1} in globals.cpp at line 14 + \o \span {class="variableName"} {mutableComplex2} in globals.cpp at line 15 + \o \span {class="variableName"} {constComplex1} in globals.cpp at line 16 + \o \span {class="variableName"} {constComplex2} in globals.cpp at line 17 + \endlist + + \note The \bold span command does not cause a new paragraph to be + started. + + See also \l {div-command} {\\div}. + + \target tt-command + \section1 \\tt (teletype font) + + The \\tt command renders its argument in a monospace font. This + command behaves just like the \l {c-command} {\\c} command, except + that \\tt allows you to nest QDoc commands within the argument + (e.g. \l {i-command} {\\i}, \l {bold-command} {\\bold} and \l + {underline-command} {\\underline}). + + \code + / *! + After \c setupUi() populates the main container with + child widgets it scans the main container's list of + slots for names with the form + \tt{on_\e{objectName}_\e{signalName}().} + * / + \endcode + + QDoc renders this as: + + \quotation + After \c setupUi() populates the main container with + child widgets it scans the main container's list of + slots for names with the form + \tt{on_\e{objectName}_\e{signalName}().} + \endquotation + + If the text to be rendered in the code font contains spaces, enclose the + entire text in curly brackets. + + \code + \tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)} + \endcode + + QDoc renders this as: + + \quotation + \tt {QLineEdit::QLineEdit(const QString &contents, QWidget *parent) :QWidget(parent)} + \endquotation + + See also \l {c-command} {\\c}. + + \target bold-command + \section1 \\bold + + The \\bold command renders its argument in bold font. + + \code + / *! + This is regular text; \bold {this text is + rendered using the \\bold command}. + * / + \endcode + + QDoc renders this as: + + \quotation + This is regular text; \bold {this text is rendered using + the \\bold command}. + \endquotation + + \target i-command + \section1 \\i (italics) + + The \\i command renders its argument in italics. + + \warning If \\i doesn't work and you get some strange error + meesages from qdoc3 about using \\o outside of tables and lists, + use \bold{\\e} for italics instead of \\i. For more information, + see the relevant explanation in the section on \l + {26-qdoc-commands-compatibility.html#i-versus-e} {compatibility + issues}. + + If the argument contains spaces or other punctuation, enclose the + argument in curly brackets. + + \code + / *! + Here, we render \i {a few words} in italic. + * / + \endcode + + QDoc renders this as: + + \quotation + Here, we render \e {a few words} in italic. + \endquotation + + If you want to use other QDoc commands within an argument that + contains spaces, you always need to enclose the argument with + braces. But QDoc is smart enough to count parentheses [3], so you + don't need braces in cases like this: + + \code + / *! + An argument can sometimes contain whitespaces, + for example: \i QPushButton(tr("A Brand New Button")) + * / + \endcode + + QDoc renders this as: + + \quotation + An argument can sometimes contain whitespaces, + for example: \e QPushButton(tr("A Brand New Button")) + \endquotation + + Finally, trailing punctuation is not included in an argument [4], + nor is 's [5] + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + <tr valign="top" bgcolor="#a2c511"> + <th></th> + <th>QDoc Syntax</th> + <th>Generated Documentation</th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td>1</td> + <td>A variation of a command button is a \e menu + button.</td> + <td>A variation of a command button is a <i>menu</i> + button.</td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td>2</td> + <td>The QPushButton widget provides a + \e {command button}.</td> + <td>The QPushButton widget provides a + <i>command button</i>.</td> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td>3</td> + <td>Another class of buttons are option buttons + \e (see QRadioButton).</td> + <td>Another class of buttons are option buttons + <i> (see QRadioButton)</i>.</td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td>4</td> + <td>A push button emits the signal \e clicked().</td> + <td>A push button emits the signal <i>clicked</i>().</td> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td>5</td> + <td>The \e QPushButton's checked property is + false by default.</td> + <td>The <i>QPushButton</i>'s checked property is + false by default.</td> + </tr> + + </table> + \endraw + + \target sub-command + \section1 \\sub + + The \\sub command renders its argument lower than the baseline of + the regular text, using a smaller font. + + \code + / *! + Definition (Range): Consider the sequence + {x\sub n}\sub {n > 1} . The set + + {x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...} + + is called the range of the sequence. + * / + \endcode + + QDoc renders this as: + + \quotation + Definition (Range): Consider the sequence + {x\sub n}\sub {n > 1} . The set + + {x\sub 2, x\sub 3, x\sub 4, ...} = {x\sub n ; n = 2, 3, 4, ...} + + is called the range of the sequence. + \endquotation + + If the argument contains spaces or other punctuation, enclose the + argument in curly brackets. + + \target sup-command + \section1 \\sup + + The \\sup command renders its argument higher than + the baseline of the regular text, using a smaller font. + + \code + / *! + The series + + 1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ... + + is called the \i {geometric series}. + * / + \endcode + + QDoc renders this as: + + \quotation + The series + + 1 + a + a\sup 2 + a\sup 3 + a\sup 4 + ... + + is called the \e {geometric series}. + \endquotation + + If the argument contains spaces or other punctuation, enclose the + argument in curly brackets. + + \target underline-command + \section1 \\underline + + The \\underline command renders its argument underlined. + + \code + / *! + The \underline {F}ile menu gives the users the possibility + to open, and edit, an existing file, save a new or modified + file, and exit the application. + * / + \endcode + + QDoc renders this as: + + \quotation + The \underline {F}ile menu gives the users the possibility + to open, and edit, an existing file, save a new or modified + file, and exit the application. + \endquotation + + If the argument contains spaces or other punctuation, enclose the + argument in curly brackets. + + \target backslash-command + \section1 \\\\ (double backslash) + + The \\\\ command expands to a single backslash. + + QDoc commands always start with a backslash alone. To display an + actual backslash in the text you need to type two of the kind. If + you want to display two backslashes, you need to type four, and so + forth. + + \code + / *! + The \\\\ command is useful if you want a + backslash to appear verbatim, for example, + writing C:\\windows\\home\\. + * / + \endcode + + QDoc renders this as: + + \quotation + The \\\\ command is useful if you want a + backslash to appear verbatim, for example, + writing C:\\windows\\home\\. + \endquotation + + However, if you want your text to appear in a typewriter font as + well, you can use the \l {c-command} {\\c} command instead, which + accepts and renders the backslash as any other character. For + example: + + \code + / *! + The \\c command is useful if you want a + backslash to appear verbatim, and the word + that contains it written in a typewriter font, + like this: \c {C:\windows\home\}. + * / + \endcode + + QDoc renders this as: + + \quotation + The \\c command is useful if you want a + backslash to appear verbatim, and the word + that contains it written in a typewriter font, + like this: \c {C:\windows\home\}. + \endquotation + +*/ + +/*! + \page 05-qdoc-commands-documentstructure.html + \previouspage Text Markup + \contentspage Table of Contents + \nextpage Including Code Inline + + \title Document Structure + + The document structuring commands are for dividing your document + into sections. QDoc supports six kinds of sections: \c \part, \c + \chapter, \c \section1, \c \section2, \c \section3 and \c + \section4. The \c \section1..4 commands are the most useful. The + correspond to the traditional section, subsection, etc used in + outlining. + + \target part-command + \section1 \\part + + The \\part command is intended for use in a large document, like a + book. + + In general a document structuring command considers everything + that follows it until the first line break as its argument. The + argument is rendered as the unit's title. If the title needs to be + spanned over several lines, make sure that each line (except the + last one) is ended with a backslash. + + In total, there are six levels of sections in QDoc: \c \part, \c + \chapter, \c \section1, \c \section2, \c \section3 and \c + \section4. \c \section1 to \c \section4 correspond to the + traditional section, subsection, subsubsection and + subsubsubsection. + + There is a strict ordering of the section units: + + \code + part + | + chapter + | + section1 + | + section2 + | + section3 + | + section4 + \endcode + + For example, a \c section1 unit can only appear as the top level + section or inside a \c chapter unit. Skipping a section unit, for + example from \c part to \c section1, is not allowed. + + You can \e begin with either of the three: \c part, \c chapter or + \c section1. + + + \code + / *! + \part Basic Qt + + This is the first part. + + + \chapter Getting Started + + This is the first part's first chapter. + + + \section1 Hello Qt + + This is the first chapter's first section. + + + \section1 Making Connections + + This is the first chapter's second section. + + + \section1 Using the Reference Documentation + + This is the first chapter's third section. + + + \chapter Creating Dialogs + + This is the first part's second chapter. + + + \section1 Subclassing QDialog + + This is the second chapter's first section. + + ... + + + \part Intermediate Qt + + This is the second part. + + + \chapter Layout Management + + This is the second part's first chapter. + + + \section1 Basic Layouts + + This is the first chapter's first section. + + ... + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <a name="Basic Qt"> + <h1>Basic Qt</h1> + </a> + <p>This is the first part.</p> + + <a name="Getting started"> + <h2>Getting Started</h2> + </a> + This is the first part's first chapter.</p> + + <a name="Hello Qt"> + <h3>Hello Qt</h3> + </a> + <p>This is the first chapter's first section.</p> + + <a name="Making Connections"> + <h3>Making Connections</h3> + </a> + <p>This is the first chapter's second section.</p> + + <a name="Using the Reference Documentation"> + <h3>Using the Reference Documentation</h3> + </a> + <p>This is the first chapter's third section.</p> + + <a name="Creating Dialogs"> + <h2>Creating Dialogs</h2> + </a> + <p>This is the first part's second chapter.</p> + + <a name="Subclassing QDialog"> + <h3>Subclassing QDialog</h3> + </a> + <p>This is the second chapter's first section.</p> + + ... + + <a name="Intermediate Qt"> + <h1>Intermediate Qt</h1> + </a> + <p>This is the second part.</p> + + <a name="Layout Management"> + <h2>Layout Management</h2> + </a> + <p>This is the second part's first chapter.</p> + + <a name="Basic Layouts"> + <h3>Basic Layouts</h3> + </a> + <p>This is the first chapter's first section.</p> + + ... + + \endraw + \endquotation + + Each section is a logical unit in the document. The section + heading appears in the automatically generated table of contents + that normally appears in the upper righthand corner of the page. + + \target chapter-command + \section1 \\chapter + + The \\chapter command is intended for use in + larger documents, and divides the document into chapters. + + See \l{part} {\\part} for an explanation of the various + section units, command argument and rendering. + + \target sectionOne-command + \section1 \\section1 + + The \\section1 command starts a new section. + + See \l{part} {\\part} for an explanation of the various + section units, command argument and rendering. + + \target sectionTwo-command + \section1 \\section2 + + The \\section2 command starts a new section. + + See \l{part} {\\part} for an explanation of the various + section units, command argument and rendering. + + \target sectionThree-command + \section1 \\section3 + + The \\section3 command starts a new section. + + See \l{part} {\\part} for an explanation of the various + section units, command argument and rendering. + + \target sectionFour-command + \section1 \\section4 + + The \\section4 command starts a new section. + + See \l{part} {\\part} for an explanation of the various + section units, command argument and rendering. + +*/ + +/*! + \page 06-qdoc-commands-includecodeinline.html + \previouspage Document Structure + \contentspage Table of Contents + \nextpage Including External Code + + \title Including Code Inline + + The following commands are used to render source code without + formatting. The source code begins on a new line, rendered in the + code. + + \bold{Note:} Although all these commands are for rendering C++ + code, the + \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command} + {\\snippet} and + \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command} + {\\codeline} commands are preferred over the others. These + commands allow equivalent code snippets for other Qt language + bindings to be substituted for the C++ snippets in the + documentation. + + \target code-command + \section1 \\code + + The \\code and \\endcode commands enclose a snippet of source code. + + \note The \l {c-command} {\\c} command can be used for short code + fragments within a sentence. The \\code command is for longer code + snippets. It renders the code verbatim in a separate paragraph in + the code font. + + When processing any of the \\code, \l {badcode-command} + {\\badcode}, \l {newcode-command} {\\newcode} or \l + {oldcode-command} {\\oldcode} commands, QDoc removes all + indentation that is common for the verbatim code blocks within a + \c{/}\c{*!} ... \c{*}\c{/} comment before it adds the standard + indentation. For that reason the recommended style is to use 8 + spaces for the verbatim code contained within these commands + + \note This doesn't apply to externally quoted code using the \l + {quotefromfile-command} {\\quotefromfile} or \l + {quotefile-command} {\\quotefile} command. + + \code + / *! + \code + #include <QApplication> + #include <QPushButton> + + int main(int argc, char *argv[]) + { + ... + } + \ endcode + * / + \endcode + + QDoc renders this as: + + \code + #include <QApplication> + #include <QPushButton> + + int main(int argc, char *argv[]) + { + ... + } + \endcode + + Other QDoc commands are disabled within \\code... \\endcode, and + the special character '\\' is accepted and rendered like the rest + of the code. + + To include code snippets from an external file, use the + \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command} + {\\snippet} and + \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command} + {\\codeline} commands. + + See also \l {c-command} {\\c}, \l + {07-0-qdoc-commands-includingexternalcode.html#quotefromfile-command} + {\\quotefromfile}, \l {badcode-command} {\\badcode}, \l + {newcode-command} {\\newcode} and \l {oldcode-command} + {\\oldcode}. + + \target badcode-command + \section1 \\badcode + + The \\badcode and \\endcode commands delimit a snippet of code + that doesn't compile or is wrong for some other reason. + + The \\badcode command is similar to the \l {code-command} {\\code} + command, but it renders the code snippet using a grey font instead + of black. + + Like the \l {code-command} {\\code} command, this command begins + its code snippet on a new line rendered in the code font and with + the standard indentation. + + \code + / *! + The statement below is rendered using the + regular \\code command: + + \code + statusbar()->message(tr("Host %1 found").arg(hostName)); + \ endcode + + While the following statement is rendered using + the \\badcode command: + + \badcode + statusbar()->message(tr("Host" + hostName + " found")); + \ endcode + * / + \endcode + + QDoc renders this as: + + \quotation + The statement below is rendered using the + regular \\code command: + + \code + statusbar()->message(tr("Host %1 found").arg(hostName)); + \endcode + + While the following statement is rendered using + the \\badcode command: + + \badcode + statusbar()->message(tr("Host" + hostName + " found")); + \endcode + \endquotation + + Other QDoc commands are disabled within \\badcode... \\endcode, + and the special character '\\' is accepted and rendered like the + rest of the code. + + See also \l {code-command} {\\code}, \l {newcode-command} + {\\newcode} and \l {oldcode-command} {\\oldcode}. + + \target newcode-command + \section1 \\newcode + + The \\newcode, \\oldcode, and \\endcode commands enable you to + show how to port a snippet of code to a new version of an API. + + The \\newcode command, and its companion the \\oldcode command, is + a convenience combination of the \l {code-command} {\\code} and \l + {badcode-command} {\\badcode} commands: The combination provides a + text relating the two code snippets to each other. The command + requires a preceding \\oldcode statement. + + Like the \l {code-command} {\\code} and \l {badcode-command} + {\\badcode} commands, the \\newcode command renders its code on a + new line in the documentation using a typewriter font and the + standard indentation. + + \code + / *! + \oldcode + if (printer->setup(parent)) + ... + \newcode + QPrintDialog dialog(printer, parent); + if (dialog.exec()) + ... + \ endcode + * / + \endcode + + QDoc renders this as: + + \quotation + \oldcode + if (printer->setup(parent)) + ... + \newcode + QPrintDialog dialog(printer, parent); + if (dialog.exec()) + ... + \endcode + \endquotation + + Other QDoc commands are disabled within \\oldcode ... \\endcode, + and the '\\' character doesn't need to be escaped. + + \target oldcode-command + \section1 \\oldcode + + The \\oldcode command requires a corresponding + \\newcode statement; otherwise QDoc fails to parse the command + and emits a warning. + + See also \l {newcode-command} {\\newcode} and \l {badcode-command} {\\badcode}. + + \target qml-command + \section1 \\qml + + The \\qml and \\endqml commands enclose a snippet of QML source + code. Currently, QDoc handles \\qml and \\endqml exactly the same + as \\code and \\endcode. + + \code + / *! + \qml + import QtQuick 1.0 + + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml + * / + \endcode + + QDoc renders this as: + + \qml + import QtQuick 1.0 + + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml +*/ + +/*! + \page 07-0-qdoc-commands-includingexternalcode.html + \previouspage Including Code Inline + \contentspage Table of Contents + \nextpage Creating Links + + \title Including External Code + + The following commands enable you to include code snippets from + external files. You can make QDoc include the complete contents of + a file, or you can quote specific parts of the file and skip + others. The typical use of the latter is to quote a file chunk by + chunk. + + \bold{Note:} Although all these commands are for rendering C++ + code, the + \l{07-0-qdoc-commands-includingexternalcode.html#snippet-command} + {\\snippet} and + \l{07-0-qdoc-commands-includingexternalcode.html#codeline-command} + {\\codeline} commands are preferred over the others. These + commands allow equivalent code snippets for other Qt language + bindings to be substituted for the C++ snippets in the + documentation. + + \target quotefile-command + \section1 \\quotefile + + The \\quotefile command expands to the complete contents of the + file given as argument. + + The command considers the rest of the line as part of its + argument, make sure to follow the file name with a line break. + + The file's contents is rendered in a separate paragraph, using a + typewriter font and the standard indentation. The code is shown + verbatim. + + \code + / *! + This is a simple "Hello world" example: + + \quotefile examples/main.cpp + + It contains only the bare minimum you need + to get a Qt application up and running. + * / + \endcode + + QDoc renders this as: + + \quotation + This is a simple "Hello world" example: + + \quotefile examples/main.cpp + + It contains only the bare minimum you need to get a Qt + application up and running. + \endquotation + + See also \l {quotefromfile-command} {\\quotefromfile} and + \l {code-command} {\\code}. + + + \target quotefromfile-command + \section1 \\quotefromfile + + The \\quotefromfile command opens the file given as argument for + quoting. + + The command considers the rest of the line as part of its + argument, make sure to follow the file name with a line break. + + The command is intended for use when quoting parts from file with + the walkthrough commands: \l {printline-command} {\\printline}, \l + {printto-command} {\\printto}, \l {printuntil-command} + {\\printuntil}, \l {skipline-command} {\\skipline}, \l + {skipto-command} {\\skipto}, \l {skipuntil-command} + {\\skipuntil}. This enables you to quote specific portions of a + file. + + \code + / *! + The whole application is contained within + the \c main() function: + + \quotefromfile examples/main.cpp + + \skipto main + \printuntil app(argc, argv) + + First we create a QApplication object using + the \c argc and \c argv parameters. + + \skipto QPushButton + \printuntil resize + + Then we create a QPushButton, and give it a reasonable + size using the QWidget::resize() function. + + ... + * / + \endcode + + QDoc renders this as: + + \quotation + The whole application is contained within + the \c main() function: + + \quotefromfile examples/main.cpp + + \skipto main + \printuntil app(argc, argv) + + First we create a QApplication object using the \c argc + and \c argv parameters. + + \skipto QPushButton + \printuntil resize + + Then we create a QPushButton, and give it a reasonable + size using the QWidget::resize() function. + + ... + \endquotation + + (\l {Example File} {The complete example file...}) + + QDoc remembers which file it's quoting, and the current position + within that file (see \l {file} {\\printline} for more + information). There is no need to "close" the file. + + Earlier we called this command \\quotefile. For more information, + see the \l + {26-qdoc-commands-compatibility.html#quotefromfile-versus-quotefile} + {compatibility} section. + + See also \l {quotefile-command} {\\quotefile}, \l {code-command} + {\\code} and \l {dots} {\\dots}. + + \target printline-command + \section1 \\printline + + The \\printline command expands to the line from the current + position to the next non-blank line of the current souce file. + + To ensure that the documentation remains synchronized with the + source file, a substring of the line must be specified as an + argument to the command. Note that the command considers the rest + of the line as part of its argument, make sure to follow the + substring with a line break. + + The line from the source file is rendered as a separate paragraph, + using a typewriter font and the standard indentation. The code is + shown verbatim. + + \code + / *! + There has to be exactly one QApplication object + in every GUI application that uses Qt. + + \quotefromfile examples/main.cpp + + \printline QApplication + + This line includes the QApplication class + definition. QApplication manages various + application-wide resources, such as the + default font and cursor. + + \printline QPushButton + + This line includes the QPushButton class + definition. The QPushButton widget provides a command + button. + + \printline main + + The main function... + * / + \endcode + + QDoc renders this as: + + \quotation + There has to be exactly one QApplication object + in every GUI application that uses Qt. + + \quotefromfile examples/main.cpp + + \skipto QApplication + \printline QApplication + + This line includes the QApplication class + definition. QApplication manages various + application-wide resources, such as the + default font and cursor. + + \printline QPushButton + + This line includes the QPushButton class + definition. The QPushButton widget provides a command + button. + + \printline main + + The main function... + \endquotation + + (\l {Example File} {The complete example file...}) + + \target file + + QDoc reads the file sequentially. To move the current position + forward you can use either of the \l {skipline-command} + {\\skip...} commands. To move the current position backward, you + can use the \l {quotefromfile-command} {\\quotefromfile} command + again. + + \target substring + + If the substring argument is surrounded by slashes it is + interpreted as a \l {regular expression}. + + \code + / *! + \quotefromfile widgets/scribble/mainwindow.cpp + + \skipto closeEvent + \printuntil /^\}/ + + Close events are sent to widgets that the users want to + close, usually by clicking \c File|Exit or by clicking + the \c X title bar button. By reimplementing the event + handler, we can intercept attempts to close the + application. + * / + \endcode + + QDoc renders this as: + + \quotation + \quotefromfile widgets/scribble/mainwindow.cpp + + \skipto closeEvent + \printuntil /^\}/ + + Close events are sent to widgets that the users want to + close, usually by clicking \c File|Exit or by clicking + the \c X title bar button. By reimplementing the event + handler, we can intercept attempts to close the + application. + \endquotation + + (\l {widgets/scribble} {The complete example file...}) + + The regular expression \c /^\}/ makes QDoc print until the first + '}' character occurring at the beginning of the line without + indentation. /.../ encloses the regular expression, and '^' means + the beginning of the line. The '}' character must be escaped since + it is a special character in regular expressions. + + QDoc will emit a warning if the specified substring or regular + expression cannot be located, i.e. if the source code has changed. + + See also \l {printto-command} {\\printto} and \l + {printuntil-command} {\\printuntil}. + + \target printto-command + \section1 \\printto + + The \\printto command expands to all the lines from the current + position up to and \e excluding the next line containing a given + substring. + + The command considers the rest of the line as part of its + argument, make sure to follow the substring with a line break. The + command also follows the same conventions for \l {file} + {positioning} and \l {substring} {argument} as the \l + {printline-command} {\\printline} command. + + The lines from the source file are rendered in a separate + paragraph, using a typewriter font and the standard + indentation. The code is shown verbatim. + + \code + / *! + The whole application is contained within the + \c main() function: + + \quotefromfile examples/main.cpp + \printto hello + + First we create a QApplication object using the \c argc and + \c argv parameters... + * / + \endcode + + QDoc renders this as: + + \quotation + The whole application is contained within the + \c main() function: + + \quotefromfile examples/main.cpp + \skipto main + \printto hello + + First we create a QApplication object using the \c argc + and \c argv parameters... + \endquotation + + (\l {Example File} {The complete example file...}) + + See also \l {printline-command} {\\printline} and \l + {printuntil-command} {\\printuntil}. + + \target printuntil-command + \section1 \\printuntil + + The \\printuntil command expands to all the lines from the current + position up to and \e including the next line containing a given + substring. + + The command considers the rest of the line as part of its + argument, make sure to follow the substring with a line break. The + command also follows the same conventions for \l {file} + {positioning} and \l {substring} {argument} as the \l + {printline-command} {\\printline} command. + + The lines from the source file are rendered in a separate + paragraph, using a typewriter font and the standard + indentation. The code is shown verbatim. + + \code + / *! + The whole application is contained within the + \c main() function: + + \quotefromfile examples/main.cpp + \skipto main + \printuntil hello + + First we create a QApplication object using the + \c argc and \c argv parameters, then we create + a QPushButton. + * / + \endcode + + QDoc renders this as: + + \quotation + The whole application is contained within the + \c main() function: + + \quotefromfile examples/main.cpp + \skipto main + \printuntil hello + + First we create a \l + {http://qt.nokia.com/doc/4.0/qapplication} {QApplication} + object using the \c argc and \c argv parameters, then we + create a \l + {http://qt.nokia.com/doc/4.0/qpushbutton} {QPushButton}. + \endquotation + + (\l {Example File} {The complete example file...}) + + See also \l {printline-command} {\\printline} and \l + {printto-command} {\\printto}. + + \target skipline-command + \section1 \\skipline + + The \\skipline command ignores the next non-blank line in the + current source file. + + Doc reads the file sequentially, and the \\skipline command is + used to move the current position (omitting a line of the source + file). See the remark about \l {file} {file positioning} above. + + The command considers the rest of the line as part of its + argument, make sure to follow the substring with a line break. The + command also follows the same conventions for \l {substring} + {argument} as the \l {printline-command} {\\printline} command, + and it is used in conjunction with the \l {quotefromfile-command} + {\\quotefromfile} command. + + \code + / *! + QPushButton is a GUI push button that the user + can press and release. + + \quotefromfile examples/main.cpp + \skipline QApplication + \printline QPushButton + + This line includes the QPushButton class + definition. For each class that is part of the + public Qt API, there exists a header file of + the same name that contains its definition. + * / + \endcode + + QDoc renders this as: + + \quotation + \l + QPushButton is a GUI push button that the user + can press and release. + + \quotefromfile examples/main.cpp + \skipto QApplication + \skipline QApplication + \printline QPushButton + + This line includes the QPushButton class + definition. For each class that is part of the public + Qt API, there exists a header file of the same name + that contains its definition. + \endquotation + + (\l {Example File} {The complete example file...}) + + See also \l {skipto-command} {\\skipto}, \l {skipuntil-command} + {\\skipuntil} and \l {dots} {\\dots}. + + \target skipto-command + \section1 \\skipto + + The \\skipto command ignores all the lines from the current + position up to and \e excluding the next line containing a given + substring. + + QDoc reads the file sequentially, and the \\skipto command is used + to move the current position (omitting one or several lines of the + source file). See the remark about \l {file} {file positioning} + above. + + The command considers the rest of the line as part of its + argument, make sure to follow the substring with a line break. + + The command also follows the same conventions for \l {substring} + {argument} as the \l {printline-command} {\\printline} command, + and it is used in conjunction with the \l {quotefromfile-command} + {\\quotefromfile} command. + + \code + / *! + The whole application is contained within + the \c main() function: + + \quotefromfile examples/main.cpp + \skipto main + \printuntil } + + First we create a QApplication object. There + has to be exactly one such object in + every GUI application that uses Qt. Then + we create a QPushButton, resize it to a reasonable + size... + * / + \endcode + + QDoc renders this as: + + \quotation + The whole application is contained within + the \c main() function: + + \quotefromfile examples/main.cpp + \skipto main + \printuntil } + + First we create a QApplication object. There has to be + exactly one such object in every GUI application that + uses Qt. Then we create a QPushButton, resize it to a + reasonable size ... + \endquotation + + (\l {Example File} {The complete example file...}) + + See also \l {skipline-command} {\\skipline}, \l + {skipuntil-command} {\\skipuntil} and \l {dots} {\\dots}. + + \target skipuntil-command + \section1 \\skipuntil + + The \\skipuntil command ignores all the lines from the current + position up to and \e including the next line containing a given + substring. + + QDoc reads the file sequentially, and the \\skipuntil command is + used to move the current position (omitting one or several lines + of the source file). See the remark about \l {file} {file + positioning} above. + + The command considers the rest of the line as part of its + argument, make sure to follow the substring with a line break. + + The command also follows the same conventions for \l {substring} + {argument} as the \l {printline-command} {\\printline} command, + and it is used in conjunction with the \l {quotefromfile-command} + {\\quotefromfile} command. + + \code + / *! + The first thing we did in the \c main() function + was to create a QApplication object \c app. + + \quotefromfile examples/main.cpp + \skipuntil show + \dots + \printuntil } + + In the end we must remember to make \c main() pass the + control to Qt. QCoreApplication::exec() will return when + the application exits... + * / + \endcode + + QDoc renders this as: + + \quotation + The first thing we did in the \c main() function was to + create a QApplication object \c app. + + \quotefromfile examples/main.cpp + \skipuntil show + \dots + \printuntil } + + In the end we must remember to make \c main() pass the + control to Qt. QCoreApplication::exec() + will return when the application exits... + \endquotation + + (\l {Example File} {The complete example file...}) + + See also \l {skipline-command} {\\skipline}, \l {skipto-command} + {\\skipto} and \l {dots} {\\dots}. + + \target dots-command + \section1 \\dots + + The \\dots command indicates that parts of the source file have + been omitted when quoting a file. + + The command is used in conjunction with the \l + {quotefromfile-command} {\\quotefromfile} command, and should be + stated on its own line. The dots are rendered on a new line, using + a typewriter font. + + \code + / *! + \quotefromfile examples/main.cpp + \skipto main + \printuntil { + \dots + \skipuntil exec + \printline } + * / + \endcode + + QDoc renders this as: + + \quotefromfile examples/main.cpp + \skipto main + \printuntil { + \dots + \skipuntil exec + \printline } + + (\l {Example File} {The complete example file...}) + + The default indentation is 4 spaces, but this can be adjusted + using the command's optional argument. + + \code + / *! + \dots 0 + \dots + \dots 8 + \dots 12 + \dots 16 + * / + \endcode + + QDoc renders this as: + + \dots 0 + \dots + \dots 8 + \dots 12 + \dots 16 + + See also \l {skipline-command} {\\skipline}, \l {skipto-command} + {\\skipto} and \l {skipuntil-command} {\\skipuntil}. + + \target snippet-command + \section1 \\snippet + + The \\snippet command causes a code snippet to be included + verbatim as preformatted text, which may be syntax highlighted. + + Each code snippet are referenced by the file that holds it and by + a unique identifier for that file. Snippet files are typically + stored in a \c{snippets} directory inside the documentation + directory (e.g., \c{$QTDIR/doc/src/snippets}). + + For example, the following documentation references a snippet in a + file residing in a subdirectory of the documentation directory: + + \code + \snippet snippets/textdocument-resources/main.cpp Adding a resource + \endcode + + The text following the file name is the unique identifier for the + snippet. This is used to delimit the quoted code in the relevant + snippet file as shown in the following example that corresponds to + the above \c{\\snippet} command: + + \dots + \code + QImage image(64, 64, QImage::Format_RGB32); + image.fill(qRgb(255, 160, 128)); + + //! [Adding a resource] + document->addResource(QTextDocument::ImageResource, + QUrl("mydata://image.png"), QVariant(image)); + //! [Adding a resource] + \endcode + \dots + + \target codeline-command + \section1 \\codeline + + The \\codeline command inserts a blank line of preformatted + text. It is used to insert gaps between snippets without closing + the current preformatted text area and opening a new one. + +*/ + +/*! + \page 07-1-example.html + \previouspage Including External Code + \contentspage Table of Contents + + \title Example File + + \quotefile examples/main.cpp +*/ + +/*! + \page 08-qdoc-commands-creatinglinks.html + \previouspage Including External Code + \contentspage Table of Contents + \nextpage Including Images + + \title Creating Links + + These commands are for creating hyperlinks to classes, functions, + examples, and other targets. + + \target l-command + \section1 \\l (link) + + The \\l link command is used to create a hyperlink to many + different kinds of targets. The command's general syntax is: + + \code + \l {link target} {link text} + \endcode + + \code + / *! + Read the \l {http://qt.nokia.com/doc/4.0/} + {Qt's Reference Documentation} carefully. + * / + \endcode + + QDoc renders this as: + + \quotation + Read the \l {http://qt.nokia.com/doc/4.0/} + {Qt's Reference Documentation} carefully. + \endquotation + + If the link target is equivalent to the link text, the second + argument can be omitted. + + For example, if you have documentation like: + + \code + / *! + \target assertions + + Assertions make some statement about the text at the + point where they occur in the regexp but they do not + match any characters. + + ... + + Regexps are built up from expressions, quantifiers, and + \l {assertions} {assertions}. + * / + \endcode + + You can simplify this as follows: + + \code + / *! + \target assertions + + Assertions make some statement about the text at the + point where they occur in the regexp but they do not + match any characters. + + ... + + Regexps are built up from expressions, quantifiers, and + \l assertions. + * / + \endcode + + For the one-parameter version the braces can often be omitted. + The \\l command supports several kinds of links: + + \list + + \o \c {\l QWidget} - The name of a class documented with the \l + {class-command} {\\class} command. + + \o \c {\l QWidget::sizeHint()} - The name of a member function, + documented with or without an \l {fn-command} {\\fn} command. + + \o \c {\l <QtGlobal>} - The subject of a \l {headerfile-command} + {\\headerfile} command. + + \o \c {\l widgets/wiggly} - The relative path used in an \l + {example-command} {\\example} command. + + \o \c {\l {QWidget Class Reference}} - The title used in a + \l {title-command} {\\title} command. + + \o \c {\l {Introduction to QDoc}}- The text from one of the + \l{part-command} {\\part}, \l{chapter} {\\chapter} or \l + {sectionOne-command} {\\section} commands. + + \o \c {\l fontmatching} - The argument of a \l {target-command} + {\\target} command. + + \o \c {\l {Shared Classes}} - A keyword named in a \l + {keyword-command} {\\keyword} command. + + \o \c {\l network.html} - The file name used in a \l + {page-command} {\\page} command. + + \o \c {\l http://qt.nokia.com/} - A URL. + + \endlist + + QDoc also tries to make a link out of any words that don't + resemble any normal English words, for example Qt class names or + functions, like QWidget or QWidget::sizeHint(). In these cases, + the \\l command can actually be omitted, but by using the command, + you ensure that QDoc will emit a warning if it cannot find the + link target. In addition, if you only want the function name to + appear in the link, you can use the following syntax: + + \list + \o \c {\l {QWidget::} {sizeHint()}} + \endlist + + QDoc renders this as: + + \quotation + \l {QWidget::} {sizeHint()} + \endquotation + + See also \l {sa-command} {\\sa}, \l {target-command} {\\target} + and \l {keyword-command} {\\keyword}. + + + \target sa-command + \section1 \\sa (see also) + + The \\sa command defines a list of links that will be rendered in + a separate "See also" section at the bottom of the documentation + unit. + + The command takes a comma-separated list of links as its + argument. If the line ends with a comma, you can continue + the list on the next line. The general syntax is: + + \code + \sa {the first link}, {the second link}, + {the third link}, ... + \endcode + + QDoc will automatically try to generate "See also" links + interconnecting a property's various functions. For example, a + setVisible() function will automatically get a link to visible() + and vice versa. + + In general, QDoc will generate "See also" links that interconnect + the functions that access the same property. It recognizes four + different syntax versions: + + \list + \o \c property() + \o \c setProperty() + \o \c isProperty() + \o \c hasProperty() + \endlist + + The \\sa command supports the same kind of links as the \l + {l-command} {\\l} command. + + \code + / *! + Appends the actions \a actions to this widget's + list of actions. + + \sa removeAction(), QMenu, addAction() + * / + void QWidget::addActions(QList<QAction *> actions) + { + ... + } + \endcode + + QDoc renders this as: + + \quotation + \bold {void QWidget::addActions ( QList<QAction*> + \e actions )} + + Appends the actions \e actions to this widget's list of + actions. + + See also \l {QWidget::removeAction()} {removeAction()}, + \l QMenu, and \l {QWidget::addAction()} {addAction()}. + \endquotation + + See also \l {l-command} {\\l}, \l {target-command} {\\target} and + \l {keyword-command} {\\keyword}. + + + \target target-command + \section1 \\target + + The \\target command names a place in the documentation that you + can link to using the \l {l-command} {\\l (link)} and \l + {sa-command} {\\sa (see also)} commands. + + The text up to the line break becomes the target name. Be sure to + follow the target name with a line break. Curly brackets are not + required around the target name, but they may be required when the + target name is used in a link cammand. See below. + + \code + / *! + \target capturing parentheses + \section1 Capturing Text + + Parentheses allow us to group elements together so that + we can quantify and capture them. + + ... + * / + \endcode + + The target name \e{capturing parentheses} can be linked from + within the same document containing the target in two ways: + + \list + \o \c {\l {capturing parentheses}} (from within the same qdoc comment) + \o \c {\l qregexp.html#capturing-parentheses} (from elsewhere in the same document) + \endlist + + \note The brackets in the link example are required because the + target name contains spaces. + + From other documents, the target name can be linked this way: + + \list + \o \c {\l http://doc.qt.nokia.com/4.0/qregexp.html#capturing-parentheses} + \endlist + + See also \l {l-command} {\\l}, \l {sa-command} {\\sa} and \l + {keyword-command} {\\keyword}. + + \target keyword-command + \section1 \\keyword + + The \\keyword command names a place in the documentation that you + can link to using the \l {l-command} {\\l (link)} and \l + {sa-command} {\\sa (see also)} commands. + + The \\keyword command is like the \l {target-command} {\\target} + command, but stronger. A keyword can be linked from anywhere using + a simple syntax. + + Keywords must be unique over all the documents processed during + the QDoc run. The command uses the rest of the line as its + argument. Be sure to follow the keyword with a line break. + + + \code + / *! + \class QRegExp + \reentrant + \brief The QRegExp class provides pattern + matching using regular expressions. + \ingroup tools + \ingroup misc + \ingroup shared + \mainclass + + \keyword regular expression + + Regular expressions, or "regexps", provide a way to + find patterns within text. + + ... + * / + \endcode + + The location marked with the keyword can be linked with: + + \code + / *! + When a string is surrounded by slashes, it is + interpreted as a \l {regular expression}. + * / + \endcode + + QDoc renders this as: + + \quotation + When a string is surrounded by slashes, it's + interpreted as a \l {regular expression}. + \endquotation + + If the keyword text contains spaces, the brackets are required. + + See also \l {l-command} {\\l (link)}, \l {sa-command} {\\sa (see + also)} and \l {target-command} {\\target}. + +*/ + +/*! + \page 09-qdoc-commands-includingimages.html + \previouspage Creating Links + \contentspage Table of Contents + \nextpage Tables and Lists + + \title Including Images + + The graphic commands makes it possible to include images in the + documentation. The images can be rendered as separate paragraphs, + or within running text. + + \target image-command + \section1 \\image + + The \\image command expands to the image specified by its first + argument, and renders it centered as a separate paragraph. + + The \\image command replaces the old \\img command. For more + information, see the \l + {26-qdoc-commands-compatibility.html#image-versus-img} + {compatibility} section. + + The command takes two arguments. The first argument is the name of + the image file. The second argument is optional and is a simple + description of the image, equivalent to the HTML alt="" in an image + tag. The description is used for tooltips, and for when a browser + doesn't support images, like the Lynx text browser. + + The remaining text \e{after} the file name is the optional, + description argument. Be sure to follow the file name or the + description with a line break. Curly brackets are required if the + description argument spans multiple lines. + + \code + / *! + Qt is a C++ toolkit for cross-platform GUI application development. + + \image happyguy.jpg "Happy guy" + + Qt provides single-source portability across Microsoft + Windows, Mac OS X, Linux, and all major commercial Unix + variants. It is also available for embedded devices. + * / + \endcode + + QDoc renders this as: + + \quotation + Qt is a C++ toolkit for cross-platform GUI application development. + + \image happyguy.jpg image "Happy guy" + + Qt provides single-source portability across Microsoft + Windows, Mac OS X, Linux, and all major commercial Unix + variants. It is also available for embedded devices. + \endquotation + + See also \l {inlineimage-command} {\\inlineimage} and \l + {caption-command} {\\caption}. + + \target inlineimage-command + \section1 \\inlineimage + + The \\inlineimage command expands to the image specified by its + argument. The image is rendered inline with the rest of the text. + + The command takes two arguments. The first argument is the name of + the image file. The second argument is optional and is a simple + description of the image, equivalent to the HTML alt="" in an image + tag. The description is used for tooltips, and for when a browser + doesn't support images, like the Lynx text browser. + + The most common use of the \\inlineimage command is in lists and + tables. Here is an example of including inline images in a list: + + \code + / *! + \list 1 + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \endlist + * / + \endcode + + QDoc renders this as: + + \list 1 + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \endlist + + Here is an example of including inline images in a table: + + \code + / *! + \table + \header + \o Qt + \o Qt Creator + \row + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \row + \o \inlineimage happy.gif Oh so happy! + \o \inlineimage happy.gif Oh so happy! + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + <tr valign="top" bgcolor="#a2c511"> + <th>Qt</th> + <th>Qt Creator</th> + </tr> + <tr valign="top" bgcolor="#f0f0f0"> + <td><img src="images/happy.gif" alt="Oh so happy!" /> + </td> + <td><img src="images/happy.gif" alt="Oh so happy!" /> + </td> + </tr> + <tr valign="top" bgcolor="#f0f0f0"> + <td><img src="images/happy.gif" alt="Oh so happy!"/> + </td> + <td><img src="images/happy.gif" alt="Oh so happy!" /> + </td> + </tr> + </table> + \endraw + + The command can also be used to insert an image inline with the + text. + + \code + / *! + \inlineimage training.jpg Qt Training + The Qt Programming course is offered as a + five day Open Enrollment Course. The classes + are open to the public. While the course is open + to anyone who wants to learn, attendees should + have significant experience in C++ development + to derive maximum benefit from the course. + * / + \endcode + + QDoc renders this as: + + \quotation + \inlineimage training.jpg Qt Training + The Qt Programming course is offered as a + five day Open Enrollment Course. The classes + are open to the public. While the course is open + to anyone who wants to learn, attendees should + have significant experience in C++ development + to derive maximum benefit from the course. + \endquotation + + See also \l {image-command} {\\image} and \l {caption-command} {\\caption}. + + \target caption-command + \section1 \\caption + + The \\caption command provides a caption for an image. + + The command takes all the text up to the end of the paragraph to + be the caption. Experiment until you get the effect you want. + + \code + / *! + \table 100% + \row + \o \image windowsvista-pushbutton.png + \caption The QPushButton widget provides a command button. + \o \image windowsvista-toolbutton.png + \caption The QToolButton class provides a quick-access button to commands + or options, usually used inside a QToolBar. + \endtable + * / + \endcode + + QDoc renders this as: + + \table 100% + \row + \o \image windowsvista-pushbutton.png + \caption The QPushButton widget provides a command button. + \o \image windowsvista-toolbutton.png + \caption The QToolButton class provides a quick-access button to commands + or options, usually used inside a QToolBar. + \endtable + + See also \l {image-command} {\\image} and \l {inlineimage-command} + {\\inlineimage} +*/ + +/*! + \page 10-qdoc-commands-tablesandlists.html + \previouspage Including Images + \contentspage Table of Contents + \nextpage Special Content + + \title Tables and Lists + + These commands enable creating lists and tables. A list is + rendered left aligned as a separate paragraph. A table is rendered + centered as a separate paragraph. The table width depends on the + width of its contents. + + \target table-command + \section1 \\table + + The \\table and \\endtable commands delimit the contents of a + table. + + The command accepts a single argument specifying the table's width + as a percentage of the page width: + + \code + / *! + \table 100 % + + ... + + \endtable + * / + \endcode + + The code above ensures that the table will fill all available + space. If the table's width is smaller than 100 %, the table will + be centered in the generated documentation. + + A table can contain headers, rows and columns. A row starts with a + \l {row-command} {\\row} command and consists of cells, which + starts with a \l {o-command} {\\o} command. There is also a \l + {header-command} {\\header} command which is a special kind of row + with a special formatting. + + \code + / *! + \table + \header + \o Qt Core Feature + \o Brief Description + \row + \o \l {Signal and Slots} + \o Signals and slots are used for communication + between objects. + \row + \o \l {Layout Management} + \o The Qt layout system provides a simple + and powerful way of specifying the layout + of child widgets. + \row + \o \l {Drag and Drop} + \o Drag and drop provides a simple visual + mechanism which users can use to transfer + information between and within applications. + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + <tr valign="top" bgcolor="#a2c511"> + <th>Qt Core Feature</th> + <th>Brief Description</th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td> + <a href="http://qt.nokia.com/doc/4.0/signalsandslots.html"> + Signals and Slots</a> + </td> + <td>Signals and slots are used for communication + between objects.</td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td> + <a href=http://qt.nokia.com/doc/4.0/layout.html"> + Layout Management</a></td> + <td>The Qt layout system provides a simple + and powerful way of specifying the layout + of child widgets.</td> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td> + <a href=http://qt.nokia.com/doc/4.0/dnd.html"> + Drag and Drop</a></td> + <td>Drag and drop provides a simple visual + mechanism which users can use to transfer + information between and within applications.</td> + </tr> + + </table> + \endraw + + You can also make cells span several rows and columns. For + example: + + \code + / *! + \table + \header + \o {3,1} This header cell spans three columns + but only one row. + \row + \o {2, 1} This table cell spans two columns + but only one row + \o {1, 2} This table cell spans only one column, + but two rows. + \row + \o A regular table cell + \o A regular table cell + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" cellspacing="1" + border="0"> + + <tr valign="top" bgcolor="#a2c511"> + <th colspan="3" rowspan=" 1"> + This header cell spans three columns but only one row + </th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td colspan="2" rowspan=" 1"> + This table cell spans two columns but only one row + </td> + <td rowspan=" 2"> + This table cell spans only one column, but two rows. + </td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td>A regular table cell</td> + <td>A regular table cell</td> + </tr> + + </table> + \endraw + + See also \l {header-command} {\\header}, \l {row-command} {\\row} and \l {o-command} {\\o}. + + \target header-command + \section1 \\header + + The \\header command indicates that the following table cells are + the current table's column headers. + + The command can only be used within the \l{table-command} + {\\table...\\endtable} commands. A header can contain several + cells. A cell is created with the \l {o-command} {\\o} command. + + A header cell's text is centered within the table cell and + rendered using a bold font. + + \code + / *! + \table + \header + \o Qt Core Feature + \o Brief Description + \row + \o \l {Signal and Slots} + \o Signals and slots are used for communication + between objects. + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + <tr valign="top" bgcolor="#a2c511"> + <th>Qt Core Feature</th> + <th>Brief Description</th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td> + <a href="http://qt.nokia.com/doc/4.0/signalsandslots.html"> + Signals and Slots</a> + </td> + <td>Signals and slots are used for communication + between objects.</td> + </tr> + </table> + \endraw + + See also \l {table-command} {\\table}, \l {row-command} {\\row} and \l {o-command} {\\o}. + + \target row-command + \section1 \\row + + The \\row command begins a new row in a table. The \l {o-command} + {\\o items} that belong in the new row will immediately follow the + \\row. + + The command can only be used within the \l{table-command} + {\\table...\\endtable} commands. A row can contain several + cells. A cell is created with the \l {o-command} {\\o} command. + + The background cell color of each row alternates between two + shades of grey, making it easier to distinguish the rows from each + other. The cells' contents is left aligned. + + \code + / *! + \table + \header + \o Qt Core Feature + \o Brief Description + \row + \o \l {Signal and Slots} + \o Signals and slots are used for communication + between objects. + \row + \o \l {Layout Management} + \o The Qt layout system provides a simple + and powerful way of specifying the layout + of child widgets. + \row + \o \l {Drag and Drop} + \o Drag and drop provides a simple visual + mechanism which users can use to transfer + information between and within applications. + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + <tr valign="top" bgcolor="#a2c511"> + <th>Qt Core Feature</th> + <th>Brief Description</th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td> + <a href="http://qt.nokia.com/doc/4.0/signalsandslots.html"> + Signals and Slots</a> + </td> + <td>Signals and slots are used for communication + between objects.</td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td> + <a href=http://qt.nokia.com/doc/4.0/layout.html"> + Layout Management</a></td> + <td>The Qt layout system provides a simple + and powerful way of specifying the layout + of child widgets.</td> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td> + <a href=http://qt.nokia.com/doc/4.0/dnd.html"> + Drag and Drop</a></td> + <td>Drag and drop provides a simple visual + mechanism which users can use to transfer + information between and within applications.</td> + </tr> + + </table> + \endraw + + See also \l {table-command} {\\table}, \l {header-command} + {\\header} and \l {o-command} {\\o}. + + \target value-command + \section1 \\value + + The \\value command starts the documentation of a C++ enum item. + + The command's first argument is the enum item. Then follows its + associated description. The description argument ends at the next + blank line or \\value. The arguments are rendered within a table. + + The documentation will be located in the associated class, header + file or namespace documentation. See the \l {enum-command} + {\\enum} documentation for an example. + + See also \l {enum-command} {\\enum} and \l {omitvalue-command} {\\omitvalue}. + + \target omitvalue-command + \section1 \\omitvalue + + The \\omitvalue command excludes a C++ enum item from the + documentation. + + The command's only argument is the name of the enum item that will + be omitted. See the \l {enum-command} {\\enum} documentation for + an example. + + See also \l {enum-command} {\\enum} and \l {value-command} + {\\value}. + + \target list-command + \section1 \\list + + The \\list and \\endlist commands delimit a list of items. + + Create each list item with the \l {o-command} {\\o} command. A + list always contains one or more items. Lists can be nested. For + example: + + \code + / *! + \list + \o Qt Reference Documentation: Getting Started + \list + \o How to Learn Qt + \o Installation + \list + \o Qt/X11 + \o Qt/Windows + \o Qt/Mac + \o Qt/Embedded + \endlist + \o Tutorial and Examples + \endlist + \endlist + * / + \endcode + + QDoc renders this as: + + \list + \o Qt Reference Documentation: Getting Started + \list + \o How to Learn Qt + \o Installation + \list + \o Qt/X11 + \o Qt/Windows + \o Qt/Mac + \o Qt/Embedded + \endlist + \o Tutorial and Examples + \endlist + \endlist + + The \\list command takes an optional argument providing + alternative appearances for the list items. + + \code + / *! + \list + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + * / + \endcode + + QDoc renders the list items with bullets (the default): + + \list + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + + \warning There appears to be a bug in qdoc3 here. If you include + any of the argument types, you get a numeric list. We're looking + into it. + + If you provide 'A' as an argument to the \\list command, the + bullets are replaced with characters in alphabetical order: + + \list A + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + + If you replace 'A' with '1', the list items are numbered in + ascending order: + + \list 1 + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + + \endlist + + If you provide 'i' as the argument, the bullets are replaced with + roman numerals: + + \list i + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + + Finally, you can make the list items appear with roman numbers + following in ascending order if you provide 'I' as the optional + argument: + + \list I + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + + You can also make the listing start at any character or number by + simply provide the number or character you want to start at. For + example: + + \code + / *! + \list G + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + * / + \endcode + + QDoc renders this as: + + \list G + \o How to Learn Qt + \o Installation + \o Tutorial and Examples + \endlist + + See also \l {o-command} {\\o}. + + \target o-command + \section1 \\o (cell, item) + + The \\o command announce a table or list item. + + Earlier we used the \l {i-command} {\\i} command for this + purpose. For more information see the \l + {26-qdoc-commands-compatibility.html#o-versus-i} {compatibility} + section. + + The command can only be used within the \l{table-command} + {\\table...\\endtable} or \l{list-command} {\\list... \\endlist} + commands. + + It considers everything until the next occurrence of the \\o + command, or the currently applicable \l {table-command} + {\\endtable} or \l {list-command} {\\endlist} command, as its + argument. For examples, see \l {table-command} {\\table} and \l + {list-command} {\\list}. + + If the command is used within a table, you can in addition specify + how many rows or columns the item should span. + + \code + / *! + \table + \header + \o {3,1} This header cell spans three columns + but only one row. + \row + \o {2, 1} This table item spans two columns + but only one row + \o {1, 2} This table item spans only one column, + but two rows. + \row + \o A regular table item + \o A regular table item + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" cellspacing="1" + border="0"> + + <tr valign="top" bgcolor="#a2c511"> + <th colspan="3" rowspan=" 1"> + This header cell spans three columns but only one row + </th> + </tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td colspan="2" rowspan=" 1"> + This table item spans two columns but only one row + </td> + <td rowspan=" 2"> + This table item spans only one column, but two rows. + </td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td>A regular table item</td> + <td>A regular table item</td> + </tr> + + </table> + \endraw + + If not specified, the item will span one column and one row. + + See also \l {table-command} {\\table}, \l {header-command} + {\\header}, \l {list-command} {\\list} and \l {o-command} {\\o}. + +*/ + +/*! + \page 11-qdoc-commands-specialcontent.html + \previouspage Tables and Lists + \contentspage Table of Contents + \nextpage Miscellaneous + + \title Special Content + + The document contents commands identify parts of the documentation, + i.e. parts with a special rendering, conceptual meaning or + function. + + \target abstract-command + \section1 \\abstract + + The \\abstract and \\endabstract commands delimit a + document's abstract section. + + The abstract section is rendered as an indented italicized + paragraph. + + \warning The \bold{\\abstract} and \bold{\\endabstract} commands + have not been implemented. The abstract section is rendered as a + regular HTML paragraph. + + \target quotation-command + \section1 \\quotation + + The \\quotation and \\endquotation commands delimit a long quotation. + + The text in the delimited block is surrounded by + \bold{<blockquote>} and \bold{</blockquote>} in the html output, + e.g.: + + \code + / *! + While the prospect of a significantly broader market is + good news for Firstlogic, the notion also posed some + challenges. Dave Dobson, director of technology for the La + Crosse, Wisconsin-based company, said: + + \quotation + As our solutions were being adopted into new + environments, we saw an escalating need for easier + integration with a wider range of enterprise + applications. + \endquotation + * / + \endcode + + The text in the \bold{\\quotation} block will appear in the generated HTML as: + + \code + <blockquote> + <p>As our solutions were being adopted into new environments, + we saw an escalating need for easier integration with a wider + range of enterprise applications.</p> + </blockquote> + \endcode + + The built-in style sheet for most browsers will render the + contents of the <blockquote> tag with left and right + indentations. The example above would be rendered as: + + \quotation + As our solutions were being adopted into new + environments, we saw an escalating need for easier + integration with a wider range of enterprise + applications. + \endquotation + + But you can redefine the \bold{<blockquote>} tag in your style.css file. + + This command replaces the old \\quote command. For more + information see the \l + {26-qdoc-commands-compatibility.html#quotation-versus-quote} + {compatibility} section. + + \target footnote-command + \section1 \\footnote + + The \\footnote and \\endfootnote commands delimit a footnote. + + The footnote is rendered at the bottom of the page. + + \warning The \bold{\\footnote} and \bold{\\endfootnote} commands + have not been implemented. The footnote is rendered as a regular + HTML paragraph. + + \target tableofcontents-command + \section1 \\tableofcontents + + The \\tableofcontents command has been disabled because QDoc + now generates a table of contents automatically. + + The automatically generated table of contents appears in the upper + righthand corner of the page. + + \target brief-command + \section1 \\brief + + The \\brief command introduces a one-sentence description of a + class, namespace, header file, property or variable. + + The brief text is used to introduce the documentation of the + associated object, and in lists generated using the \l + {generatelist-command} {\\generatelist} command and the \l + {annotatedlist-command} {\\annotatedlist} command. + + The \\brief command can be used in two significant different ways: + \l {brief class} {One for classes, namespaces and header files}, + and \l {brief-property} {one for properties and variables}. + + \target brief-property + + When the \\brief command is used to describe a property or a + variable, the brief text must be a sentence fragment starting with + "whether" (for a boolean property or variable) or starting with + "the" (for any other property or variable). + + For example the boolean QWidget::isWindow property: + + \code + / *! + \property QWidget::isActiveWindow + \brief whether this widget's window is the active window + + The active window is the window that contains the widget that + has keyboard focus. + + When popup windows are visible, this property is true + for both the active window \e and for the popup. + + \sa activateWindow(), QApplication::activeWindow() + * / + \endcode + + and the QWidget::geometry property + + \code + / *! + \property QWidget::geometry + \brief the geometry of the widget relative to its parent and + excluding the window frame + + When changing the geometry, the widget, if visible, + receives a move event (moveEvent()) and/or a resize + event (resizeEvent()) immediately. + + ... + + \sa frameGeometry(), rect(), ... + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h3>geometry : + <a href="http://qt.nokia.com/doc/4.0/qrect.html">QRect</a> + </h3> + \endraw + + This property holds the geometry of the widget relative + to its parent and excluding the window frame. + + ... + + Access functions: + \list + \o \bold {const QRect & geometry () const} + \o \bold {void setGeometry ( int x, int y, int w, int h )} + \o \bold {void setGeometry ( const QRect & )} + \endlist + + See also \l + {QWidget::frameGeometry()} {frameGeometry()}, \l + {QWidget::rect()} {rect()}, ... + \endquotation + + \target brief class + + When the \\brief command is used to describe a class, the brief + text should be a complete sentence and must start like this: + + \code + The <classname> class is|provides|contains|specifies... + \endcode + + \warning The brief statement is used as the first paragraph of the + detailed description. Do not repeat the sentence. + + \code + / *! + \class PreviewWindow + \brief The PreviewWindow class is a custom widget + displaying the names of its currently set + window flags in a read-only text editor. + + The PreviewWindow class inherits QWidget. The widget + displays the names of its window flags set with the + setWindowFlags() function. It is also provided with a + QPushButton that closes the window. + + ... + + \sa QWidget + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h1>PreviewWindow Class Reference</h1> + \endraw + + The PreviewWindow class is a custom widget displaying + the names of its currently set window flags in a + read-only text editor. \l {preview window} {More...} + + \raw HTML + <h3>Properties</h3> + \endraw + + \list + \o 52 properties inherited from QWidget + \o 1 property inherited from QObject + \endlist + + \raw HTML + <h3>Public Functions</h3> + \endraw + + \list + \o \l {constructor} {PreviewWindow}(QWidget *parent = 0) + \o void \l {function} {setWindowFlags}(Qt::WindowFlags flags) + \endlist + + \list + \o 183 public functions inherited from QWidget + \o 28 public functions inherited from QObject + \endlist + + \raw HTML + <h3>Public Slots</h3> + \endraw + + \list + \o 17 public slots inherited from QWidget + \o 1 public slot inherited from QObject + \endlist + + \raw HTML + <h3>Additional Inherited Members</h3> + \endraw + + \list + \o 1 signal inherited from QWidget + \o 1 signal inherited from QObject + \o 4 static public members inherited from QWidget + \o 4 static public members inherited from QObject + \o 39 protected functions inherited from QWidget + \o 7 protected functions inherited from QObject + \endlist + + \target preview window + + \raw HTML + <hr /> + <h2>Detailed Description</h2> + \endraw + + The PreviewWindow class is a custom widget displaying + the names of its currently set window flags in a + read-only text editor. + + The PreviewWindow class inherits QWidget. The widget + displays the names of its window flags set with the \l + {function} {setWindowFlags()} function. It is also + provided with a QPushButton that closes the window. + + ... + + See also QWidget. + + \raw HTML + <hr /> + <h2>Member Function Documentation</h2> + \endraw + + \target constructor + \raw HTML + <h3>PreviewWindow(QWidget *parent = 0)</h3> + \endraw + + Constructs a preview window widget with \e parent. + + \target function + \raw HTML + <h3>setWindowFlags(Qt::WindowFlags flags)</h3> + \endraw + + Sets the widgets flags using the + QWidget::setWindowFlags() function. + + Then runs through the available window flags, + creating a text that contains the names of the flags + that matches the flags parameter, displaying + the text in the widgets text editor. + \endquotation + + Using \\brief in a \l{namespace-command}{\\namespace}: + + \code + / *! + \namespace Qt + + \brief The Qt namespace contains miscellaneous identifiers + used throughout the Qt library. + * / + \endcode + + Using \\brief in a \l{headerfile-command}{\\headerfile}: + + \code + / *! + \headerfile <QtGlobal> + \title Global Qt Declarations + + \brief The <QtGlobal> header file provides basic + declarations and is included by all other Qt headers. + + \sa <QtAlgorithms> + * / + \endcode + + See also \l{property-command} {\\property}, \l{class-command} + {\\class}, \l{namespace-command} {\\namespace} and + \l{headerfile-command} {\\headerfile}. + + \target legalese-command + \section1 \\legalese + + The \\legalese and \\endlegalese commands delimit a licence agreement. + + In the generated HTML, the delimited text is surrounded by a \bold + {<div class="LegaleseLeft">} and \bold {</div>} tags. + + For example, here is a license agreement enclosed in \\legalese + and \\endlegalese: + + \code + / *! + \legalese + Copyright 1996 Daniel Dardailler. + + Permission to use, copy, modify, distribute, and sell this + software for any purpose is hereby granted without fee, + provided that the above copyright notice appear in all + copies and that both that copyright notice and this + permission notice appear in supporting documentation, and + that the name of Daniel Dardailler not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. Daniel + Dardailler makes no representations about the suitability of + this software for any purpose. It is provided "as is" + without express or implied warranty. + + Modifications Copyright 1999 Matt Koss, under the same + license as above. + \endlegalese + * / + \endcode + + It will appear in the generated HTML as: + + \code + <div class="LegaleseLeft"> + <p>Copyright 1996 Daniel Dardailler.</p> + <p>Permission to use, copy, modify, distribute, and sell + this software for any purpose is hereby granted without fee, + provided that the above copyright notice appear in all + copies and that both that copyright notice and this + permission notice appear in supporting documentation, and + that the name of Daniel Dardailler not be used in + advertising or publicity pertaining to distribution of the + software without specific, written prior permission. Daniel + Dardailler makes no representations about the suitability of + this software for any purpose. It is provided "as is" + without express or implied warranty.</p> + + <p>Modifications Copyright 1999 Matt Koss, under the same + license as above.</p> + </div> + \endcode + + If the \\endlegalese command is omitted, QDoc will process the + \\legalese command but considers the rest of the documentation + page as the license agreement. + + Ideally, the license text is located with the licensed code. + + Elsewhere, the documentation identified as \e{\\legalese} command + can be accumulated using \l {generatelist-command} {\\generatelist} + with \c {legalese-command} as the argument. This is useful for + generating an overview of the license agreements associated with + the source code. + + \target warning-command + \section1 \\warning + + The \\warning command prepends "Warning:" to the command's + argument, in bold font. + + \code + / *! + Qt::HANDLE is a platform-specific handle type + for system objects. This is equivalent to + \c{void *} on Windows and Mac OS X, and to + \c{unsigned long} on X11. + + \warning Using this type is not portable. + * / + \endcode + + QDoc renders this as: + + \quotation + Qt::HANDLE is a platform-specific handle type + for system objects. This is equivalent to + \c{void *} on Windows and Mac OS X, and to + \c{unsigned long} on X11. + + \warning Using this type is not portable. + \endquotation + +*/ + +/*! + \page 12-0-qdoc-commands-miscellaneous.html + \previouspage Special Content + \contentspage Table of Contents + \nextpage The QDoc Configuration File + + \title Miscellaneous + + These commands provide miscellaneous functions connected to the + visual appearance of the documentation, and to the process of + generating the documentation. + + \target expire-command + \section1 \\expire + + The \\expire command allows you to define an expiration + date for your documentation. + + When using the \\expire command, QDoc will emit a warning when the + current date is larger than the specified date. The command + accepts one argument; the argument's format is yyyy-mm-dd. For + example: + + \code + / *! + \page porting.html + + \title Porting to Qt 3.x + + \expire 2004-12-31 + + This document describes porting applications from Qt + 2.x to Qt 3.x. + + The Qt 3.x series is not binary compatible with the + 2.x series. + ... + * / + \endcode + + If you run QDoc on 4 July 2005, it will emit the warning + + \quotation + porting.qdoc:6: Documentation expired 185 days ago + \endquotation + + + \target annotatedlist-command + \section1 \\annotatedlist + + The \\annotatedlist command expands to a list of the members of a + group, each member listed with its \e {brief} text. Below is an + example from the Qt Reference Documentation: + + \code + / *! + ... + \section1 Drag and Drop Classes + + These classes deal with drag and drop and the necessary mime type + encoding and decoding. + + \annotatedlist draganddrop + + * / + \endcode + + This generates a list of all the classes in the \e{draganddrop} group. + A class in the \e{draganddrop} group will have the \\ingroup command + in its \\class or \\qmlclass comment. + + + \target generatelist-command + \section1 \\generatelist + + The \\generatelist command expands to a list of various + documentation or links to documentation. Below is an example from + the Qt Reference Documentation: + + \code + / *! + \page classes.html + \title All Classes + + For a shorter list that only includes the most + frequently used classes, see \l{Qt's Main Classes}. For + a list of Qt 3 support classes, see \l{Qt3Support + Classes}. + + \generatelist classes + * / + \endcode + + This generates the \l {All Classes} page. The command accepts the + following arguments: + + \target table example + \section2 \c annotatedclasses + + The \c annotatedclasses argument provides a table containing the + names of all the classes, and a description of each class. Each + class name is a link to the class's reference documentation. For + example: + + \table + \row + \o QDial + \o Rounded range control (like a speedometer or potentiometer) + \row + \o QDialog + \o The base class of dialog windows + \row + \o QDir + \o Access to directory structures and their contents + \endtable + + A C++ class is documented with the \l {class-command} {\\class} + command. The annotation for the class is taken from the argument + of the class comment's \l {brief-command} {\\brief} command. + + \target list example + \section2 \c classes + + The \c classes argument provides a complete alphabetical list of + the classes. Each class name is a link to the class's reference + documentation. This command is uded to generate the \l + {classes.html} {All Classes} page this way: + + \code + / *! + \page classes.html + \title All Classes + \ingroup classlists + + \brief If you know the name of the class you want, find it here. + + This is a list of all Qt classes. For a list of the classes + provided for compatibility with Qt3, see \l{Qt3 Support + Classes}. For classes that have been deprecated, see the + \l{Obsolete Classes} list. + + \generatelist classes + * / + \endcode + + A C++ class is documented with the \l {class-command} {\\class} + command. + + \section2 \c classesbymodule + + When this argument is used, a second argument is required, which + specifies the module whose classes are to be listed. QDoc + generates a table containing those classes. Each class is listed + with the text of its \l{brief-command} {\\brief} command. + + This command is used to generate the \l {phonon-module.html} + {Phonon Module} page this way. + + \code + / *! + \page phonon-module.html + \module Phonon + \title Phonon Module + \ingroup modules + + \brief The Phonon module contains namespaces and classes for multimedia functionality. + + \generatelist{classesbymodule Phonon} + + ... + + * / + \endcode + + Each class that is a member of the specified module must be marked + with the \l {inmodule-command} {\\inmodule} command in its \\class + comment. + + \section2 \c compatclasses + + The \c compatclasses argument generates a list in alphabetical + order of the support classes. It is normally used only to + generate the \l {compatclasses.html} {Qt3 Support Classes} page + this way: + + \code + / *! + \page compatclasses.html + \title Qt3 Support Classes + \ingroup classlists + + \brief These classes ease the porting of code from Qt 3 to Qt 4. + + These are the classes that Qt provides for compatibility with Qt + 3. Most of these are provided by the Qt3Support module. + + \generatelist compatclasses + * / + \endcode + + A support class is identified in the \\class comment with the \l + {compat-command} {\\compat} command. + + \section2 \c functionindex + + The \c functionindex argument provides a complete alphabetical + list of all the documented member functions. It is normally used + only to generate the \l {functions.html} {Qt function index} page + this way: + + \code + / *! + \page functions.html + \title All Functions + \ingroup funclists + + \brief All documented Qt functions listed alphabetically with a + link to where each one is declared. + + This is the list of all documented member functions and global + functions in the Qt API. Each function has a link to the + class or header file where it is declared and documented. + + \generatelist functionindex + * / + \endcode + + \section2 \c legalese + + The \c legalese argument tells QDoc to generate a complete list of + licenses in the documentation. Each license is identified using + the \l {legalese-command} {\\legalese} command. This command is + used to generate the \l {licenses.html} {Qt license information} + page this way: + + \code + / *! + \page licenses.html + \title Other Licenses Used in Qt + \ingroup licensing + \brief Information about other licenses used for Qt components and third-party code. + + Qt contains some code that is not provided under the + \l{GNU General Public License (GPL)}, + \l{GNU Lesser General Public License (LGPL)} or the + \l{Qt Commercial Edition}{Qt Commercial License Agreement}, but rather under + specific licenses from the original authors. Some pieces of code were developed + by Nokia and others originated from third parties. + This page lists the licenses used, names the authors, and links + to the places where it is used. + + Nokia gratefully acknowledges these and other contributions + to Qt. We recommend that programs that use Qt also acknowledge + these contributions, and quote these license statements in an + appendix to the documentation. + + See also: \l{Licenses for Fonts Used in Qt for Embedded Linux} + + \generatelist legalese + * / + \endcode + + \section2 \c mainclasses + + The \c mainclasses argument tells QDoc to generate an alphabetical + list of the main classes. A class is marked as a main class by + including a \l {mainclass-command} {\\mainclass} command in the + \\class comment. + + \note The Qt documentation no longer includes a main classes page, + but you can generate one for your main classes if you want it. + + \section2 \c overviews + + The \c overviews argument is used to tell QDoc to generate a list + by concatenating the contents of all the \l {group-command} + {\\group} pages. Qt uses it to generate the \l {overviews.html} + {overviews} page this way: + + \code + / *! + \page overviews.html + + \title All Overviews and HOWTOs + + \generatelist overviews + * / + \endcode + + \section2 \c related + + The \c related argument is used in combination with the \l + {group-command} {\\group} and \l {ingroup-command} {\\ingroup} + commands to list all the overviews related to a specified + group. For example, the page for the \l {Programming with Qt} + {Programming with Qt} page is generated this way: + + \code + / *! + \group qt-basic-concepts + \title Programming with Qt + + \brief The basic architecture of the Qt cross-platform application and UI framework. + + Qt is a cross-platform application and UI framework for + writing web-enabled applications for desktop, mobile, and + embedded operating systems. This page contains links to + articles and overviews explaining key components and + techniuqes used in Qt development. + + \generatelist {related} + * / + \endcode + + Each page listed on this group page contains the command: + + \code + \ingroup qt-basic-concepts + \endcode + + \section2 \c service + + The \c service argument tells QDoc to generate an alphabetical + list of the services. Each service name is a link to the service's + reference documentation. + + A service is identified with the \l {service-command} {\\service} + command. + + \note This command and the \l {service-command} {\\service} + command are not used in the Qt documentation. + + \target if-command + \section1 \\if + + The \\if command and the corresponding \\endif command + enclose parts of a QDoc comment that only will be included if + the condition specified by the command's argument is true. + + The command reads the rest of the line and parses it as an C++ #if + statement. + + \code + / *! + \if defined(opensourceedition) + + \bold{Note:} This edition is for the development of + \l{Qt Open Source Edition} {Free and Open Source} + software only; see \l{Qt Commercial Editions}. + + \endif + * / + \endcode + + This QDoc comment will only be rendered if the \c + opensourceedition preprocessor symbol is defined, and specified in + the \l {defines-variable} {defines} variable in the configuration + file to make QDoc process the code within #ifdef and #endif: + + \code + defines = opensourceedition + \endcode + + You can also define the preprocessor symbol manually on the + command line. For more information see the documentation of the \l + {defines-variable} {defines} variable. + + See also \l{endif-command} {\\endif}, \l{else-command} {\\else}, + \l {defines-variable} {defines} and \l {falsehoods-variable} + {falsehoods}. + + \target endif-command + \section1 \\endif + + The \\endif command and the corresponding \\if command + enclose parts of a QDoc comment that will be included if + the condition specified by the \l {if-command} {\\if} command's + argument is true. + + For more information, see the documentation of the \l {if-command} + {\\if} command. + + See also \l{if-command} {\\if}, \l{else-command} {\\else}, \l + {defines-variable} {defines} and \l {falsehoods-variable} + {falsehoods}. + + \target else-command + \section1 \\else + + The \\else command specifies an alternative if the + condition in the \l {if-command} {\\if} command is false. + + The \\else command can only be used within \l {if-command} + {\\if...\\endif} commands, but is useful when there is only two + alternatives. + + \code + / *! + The Qt 3 support library is provided to keep old + source code working. + + In addition to the \c Qt3Support classes, Qt 4 provides + compatibility functions when it's possible for an old + API to cohabit with the new one. + + \if !defined(QT3_SUPPORT) + \if defined(QT3_SUPPORTWARNINGS) + The compiler emits a warning when a + compatibility function is called. (This works + only with GCC 3.2+ and MSVC 7.) + \else + To use the Qt 3 support library, you need to + have the line QT += qt3support in your .pro + file (qmake automatically define the + QT3_SUPPORT symbol, turning on compatibility + function support). + + You can also define the symbol manually (e.g., + if you don't want to link against the \c + Qt3Support library), or you can define \c + QT3_SUPPORT_WARNINGS instead, telling the + compiler to emit a warning when a compatibility + function is called. (This works only with GCC + 3.2+ and MSVC 7.) + \endif + \endif + * / + \endcode + + If the \c QT3_SUPPORT is defined, the comment will be rendered + like this: + + \quotation + The Qt 3 support library is provided to keep old source + code working. + + In addition to the Qt3Support classes, Qt 4 provides + compatibility functions when it's possible for an old + API to cohabit with the new one. + \endquotation + + If \c QT3_SUPPORT is not defined but \c QT3_SUPPORT_WARNINGS is + defined, the comment will be rendered like this: + + \quotation + The Qt 3 support library is provided to keep old source + code working. + + In addition to the Qt3Support classes, Qt 4 provides + compatibility functions when it's possible for an old + API to cohabit with the new one. + + The compiler emits a warning when a compatibility + function is called. (This works only with GCC 3.2+ and + MSVC 7.) + \endquotation + + If none of the symbols are defined, the comment will be + rendered as + + \quotation + The Qt 3 support library is provided to keep old + source code working. + + In addition to the \c Qt3Support classes, Qt 4 provides + compatibility functions when it's possible for an old + API to cohabit with the new one. + + To use the Qt 3 support library, you need to have the + line QT += qt3support in your .pro file (qmake + automatically define the QT3_SUPPORT symbol, turning on + compatibility function support). + + You can also define the symbol manually (e.g., if you + don't want to link against the \c Qt3Support library), + or you can define \c QT3_SUPPORT_WARNINGS instead, + telling the compiler to emit a warning when a + compatibility function is called. (This works only with + GCC 3.2+ and MSVC 7.) + \endquotation + + See also \l{if-command} {\\if}, \l{endif-command} {\\endif}, \l + {defines-variable} {defines} and \l {falsehoods-variable} + {falsehoods}. + + \target include-command + \section1 \\include + + The \\include command sends all or part of the file specified by + its first argument to the QDoc input stream to be processed as a + qdoc comment snippet. This command is often assigned the alias, + \e {input}, in the QDoc configuration file, e.g. \e {alias.include + = input}. + + The command is useful when some snippet of commands and text is to + be used in multiple places in the documentation. In that case, + move the snippet into a separate file and use the \\include + command wherever you want to insert the snippet into the + documentation. To prevent QDoc from reading the file as a + stand-alone page of documentation, we recommend that you use the + \c .qdocinc extension for these \e {include} files. + + The command can have either one or two arguments. The first + argument is always a file name. The contents of the file must be + QDoc input, i.e. a sequence of QDoc commands and text, but without + the enclosing qdoc comment \c{/}\c{*!} ... \c{*}\c{/} delimeters. + If you want to include the entire named file, don't use the second + argument. If you want to include only part of the file, see the + \l{2-argument-form}{two argument form} below. Here is an example + of the one argument form: + + \code + / *! + \page corefeatures.html + \title Core Features + + \include examples/signalandslots.qdocinc + \include examples/objectmodel.qdocinc + \include examples/layoutmanagement.qdocinc + * / + \endcode + + Here are links to the \c .qdocinc files used above: + \l{signalandslots.qdocinc}, \l{objectmodel.qdocinc}, + \l{layoutmanagement.qdocinc}. QDoc renders this page + \l{corefeatures.html} {as shown here}. + + \target 2-argument-form} + \section2 \\include filename snippet-identifier + + It is kind of a pain to make a separate \c .qdocinc file for every + QDoc include snippet you want to use in multiple places in the + documentation, especially given that you probably have to put the + copyright/license notice in every one of these files. So if you + have lots of these include snippets, you can put them all in a + single file if you want, and surround each one with: + \code + //! [snippet-id1] + + QDoc commands and text... + + //! [snippet-id1] + + //! [snippet-id2] + + More QDoc commands and text... + + //! [snippet-id2] + \endcode + + Then you can use the two-argument form of the command: + + \code + \input examples/signalandslots.qdocinc snippet-id2 + \input examples/objectmodel.qdocinc another-snippet-id + \endcode + + It works as expected. The sequence of QDoc commands and text found + between the two tags with the same name as the second argument is + sent to the QDoc input stream. You can even nest these snippets, + although it's not clear why you would want to do that. + + \target meta-command + \section1 \\meta + + The \\meta command is mainly used for including metadata in DITA + XML files. It is also used when generating HTML output for specifying + the \e maintainer(s) of a C++ class. + + The command has two arguments: The first argument is the name of the + metadata attribute you wish to set, and the second argument is the + value for the attribute. Each argument should be enclosed in curly + brackets, as shown in this example: + + \code + / *! + \class QWidget + \brief The QWidget class is the base class of all user interface objects. + + \ingroup basicwidgets + + \meta {technology} {User Interface} + \meta {platform} {OS X 10.6} + \meta {platform} {Symbian} + \meta {platform} {MeeGo} + \meta {audience} {user} + \meta {audience} {programmer} + \meta {audience} {designer} + * / + \endcode + + When running QDoc to generate HTML, the example above will have no + effect on the generated output, but if you run QDoc to generate + DITA XML, the example will generate the following: + + \code + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE cxxClass PUBLIC "-//NOKIA//DTD DITA C++ API Class Reference Type v0.6.0//EN" "dtd/cxxClass.dtd"> + <!--qwidget.cpp--> + <cxxClass id="id-9a14268e-6b09-4eee-b940-21a00a0961df"> + <apiName>QWidget</apiName> + <shortdesc>the QWidget class is the base class of all user interface objects.</shortdesc> + <prolog> + <author>Qt Development Frameworks</author> + <publisher>Nokia</publisher> + <copyright> + <copyryear year="2011"/> + <copyrholder>Nokia</copyrholder> + </copyright> + <permissions view="all"/> + <metadata> + <audience type="designer"/> + <audience type="programmer"/> + <audience type="user"/> + <category>Class reference</category> + <prodinfo> + <prodname>Qt Reference Documentation</prodname> + <vrmlist> + <vrm version="4" release="7" modification="3"/> + </vrmlist> + <component>QtGui</component> + </prodinfo> + <othermeta name="platform" content="MeeGo"/> + <othermeta name="platform" content="Symbian"/> + <othermeta name="platform" content="OS X 10.6"/> + <othermeta name="technology" content="User Interface"/> + </metadata> + </prolog> + \endcode + + In the example output, several values have been set using defualt + values obtained from the QDoc configuration file. See \l + {Generating DITA XML Output} for details. + + \target omit-command + \section1 \\omit + + The \\omit command and the correspondning \\endomit command + delimit parts of the documentation that you want QDoc to skip. For + example: + + \code + / *! + \table + \row + \o Basic Widgets + \o Basic GUI widgets such as buttons, comboboxes + and scrollbars. + + \omit + \row + \o Component Model + \o Interfaces and helper classes for the Qt + Component Model. + \endomit + + \row + \o Database Classes + \o Database related classes, e.g. for SQL databases. + \endtable + * / + \endcode + + QDoc renders this as: + + \raw HTML + <table align="center" cellpadding="2" + cellspacing="1" border="0"> + + <tr valign="top" bgcolor="#d0d0d0"> + <td>Basic Widgets</td> + <td>Basic GUI widgets such as buttons, comboboxes + and scrollbars.</td> + </tr> + + <tr valign="top" bgcolor="#c0c0c0"> + <td>Database Classes</td> + <td>Database related classes, e.g. for SQL databases.</td> + </tr> + </table> + \endraw + + \target raw-command + \section1 \\raw \span {class="newStuff"} {(avoid)} + + The \\raw command and the corresponding + \\endraw command delimit a block of raw mark-up language code. + + \note Avoid using this command if possible, because it generates + DITA XML code that causes problems. If you are trying to generate + special table or list behavior, try to get the behavior you want + using the \l {span-command} {\\span} and \l {div-command} {\\div} + commands in your \l {table-command} {\\table} or \l {list-command} + {\\list}. + + The command takes an argument specifying the code's format; + currently the only supported format is HTML. + + The \\raw command is useful if you want some special HTML effects + in your documentation. + + \code + / *! + Qt has some predefined QColor objects. + + \raw HTML + <style type="text/css" id="colorstyles"> + #color-blue { background-color: #0000ff; color: #ffffff } + #color-darkBlue { background-color: #000080; color: #ffffff } + #color-cyan { background-color: #00ffff; color: #000000 } + </style> + + <p> + <tt id="color-blue">Blue(#0000ff)</tt>, + <tt id="color-darkBlue">dark blue(#000080)</tt> and + <tt id="color-cyan">cyan(#00ffff)</tt>. + </p> + \endraw + * / + \endcode + + QDoc renders this as: + + \quotation + Qt has some predefined QColor objects. + + \raw HTML + <style type="text/css" id="colorstyles"> + #color-blue { background-color: #0000ff; color: #ffffff } + #color-darkBlue { background-color: #000080; color: #ffffff } + #color-cyan { background-color: #00ffff; color: #000000 } + </style> + + <p> + <tt id="color-blue">Blue(#0000ff)</tt>, + <tt id="color-darkBlue">dark blue(#000080)</tt> and + <tt id="color-cyan">cyan(#00ffff)</tt>. + </p> + \endraw + \endquotation + + \note But you can achieve the exact same thing using qdoc + commands. In this case, all you have to do is include the color + styles in your style.css file. Then you can write: + + \code + \tt {\span {id="color-blue"} {Blue(#0000ff)}}, + \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and + \tt {\span {id="color-cyan"} {cyan(#00ffff)}}. + \endcode + + ...which is rendered again as: + + \tt {\span {id="color-blue"} {Blue(#0000ff)}}, + \tt {\span {id="color-darkBlue"} {dark blue(#000080)}} and + \tt {\span {id="color-cyan"} {cyan(#00ffff)}}. + + \target unicode-command + \section1 \\unicode + + The \\unicode command allows you to insert an arbitrary Unicode + character in the document. + + The command takes an argument specifying the character as an + integer. By default, base 10 is assumed, unless a '0x' or '0' + prefix is specified (for base 16 and 8, respectively). For + example: + + \code + O G\unicode{0xEA}nio e as Rosas + + \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour + + \unicode 0x3A3 \e{a}\sub{\e{i}} + \endcode + + QDoc renders this as: + + \quotation + O G\unicode{0xEA}nio e as Rosas + + \unicode 0xC0 table en famille avec 15 \unicode 0x20AC par jour + + \unicode 0x3A3 \e{a}\sub{\e{i}} + \endquotation +*/ + +/*! + \page 12-1-signalandslots.html + \previouspage Miscellaneous + \contentspage Table of Contents + + \title signalandslots.qdocinc + + \quotefile examples/signalandslots.qdocinc +*/ + +/*! + \page 12-2-objectmodel.html + \previouspage Miscellaneous + \contentspage Table of Contents + + \title objectmodel.qdocinc + + \quotefile examples/objectmodel.qdocinc +*/ + +/*! + \page 12-3-layoutmanagement.html + \previouspage Miscellaneous + \contentspage Table of Contents + + \title layoutmanagement.qdocinc + + \quotefile examples/layoutmanagement.qdocinc +*/ + +/*! + \page 13-qdoc-commands-topics.html + \previouspage Command Index + \contentspage Table of Contents + \nextpage Context Commands + + \title Topic Commands + + A topic command tells QDoc which source code element is being + documented. Some topic commands allow you to create documentation + pages that aren't tied to any underlying source code element. + + When QDoc processes a QDoc comment, it tries to connect the + comment to an element in the source code by first looking for a + topic command that names the source code element. If there is no + topic command, QDoc tries to connect the comment to the source + code element that immediately follows the comment. If it can't do + either of these and if there is no topic command that indicates + the comment does not have an underlying source code element (e.g. + \l{page-command} {\\page}), then the comment is discarded. + + \target topic argument + + The name of the entity being documented is usually the only + argument for a topic command. Use the complete name. Sometimes + there can be a second parameter in the argument. See e.g. \l + {page-command} {\\page}. + + \code + \enum QComboBox::InsertPolicy + \endcode + + The \l {fn-command} {\\fn} command is a special case. For the \l + {fn-command} {\\fn} command, use the function's signature + including the class qualifier. + + \code + \fn void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) + \endcode + + A topic command can appear anywhere in a comment but must stand + alone on its own line. Best practice is to let the topic commend + be the first line of the comment. If the argument spans several + lines, make sure that each line (except the last one) is ended + with a backslash. In addition QDoc counts parentheses, which means + that if it encounters a '(' it considers everything until the + closing ')' as its argument. + + If a topic command is repeated with different arguments, the + same documentation will appear for both the units. + + \code + / *! + \fn void PreviewWindow::setWindowFlags() + \fn void ControllerWindow::setWindowFlags() + + Sets the widgets flags using the QWidget::setWindowFlags() + function. + + Then runs through the available window flags, creating a text + that contains the names of the flags that matches the flags + parameter, displaying the text in the widgets text editor. + * / + \endcode + + The \c PreviewWindow::setWindowFlags() and \c + ControllerWindow::setWindowFlags() functions will get the same + documentation. + + \target class-command + \section1 \\class + + The \\class command is for documenting a C++ class. The argument + is the complete name of the class. The command tells QDoc that a + class is part of the public API, and lets you enter a detailed + description. + + \code + / *! + \class QMap::iterator + + \brief The QMap::iterator class provides an STL-style + non-const iterator for QMap and QMultiMap. + + QMap features both \l{STL-style iterators} and + \l{Java-style iterators}. The STL-style iterators ... + * / + \endcode + + The HTML documentation for the named class is written to a + \c{.html} file named from the class name, in lower case, and with + the double colon qulifier(s) replaced with '-'. For example, the + documentation for the \c QMap::Iterator class is written to \c + qmap-iterator.html. + + \target framework + + The file contains the class description from the \\class comment, + plus the documentation generated from QDoc comments for all the + class members, i.e. a list of the class's types, properties, + functions, signals, and slots. + + In addition to the detailed description of the class, the \\class + comment typically contains a \l {brief-command} {\\brief} command + and one or more \l{Markup Commands}. See the \\class command for + any of the Qt class for examples. Here is a very simple example: + + \code + / *! + \class PreviewWindow + \brief The PreviewWindow class is a custom widget + displaying the names of its currently set + window flags in a read-only text editor. + + \ingroup miscellaneous + + The PreviewWindow class inherits QWidget. The widget + displays the names of its window flags set with the \l + {function} {setWindowFlags()} function. It is also + provided with a QPushButton that closes the window. + + ... + + \sa QWidget + * / + \endcode + + The way QDoc renders this \\class will depend a lot on your \c + {style.css} file, but the general outline of the class reference + page will look like this: + + \quotation + \raw HTML + <h1>PreviewWindow Class Reference</h1> + \endraw + + The PreviewWindow class is a custom widget displaying + the names of its currently set window flags in a + read-only text editor. \l {preview window} {More...} + + \raw HTML + <h3>Properties</h3> + \endraw + + \list + \o 52 properties inherited from QWidget + \o 1 property inherited from QObject + \endlist + + \raw HTML + <h3>Public Functions</h3> + \endraw + + \list + \o \l {constructor} {PreviewWindow}(QWidget *parent = 0) + \o void \l {function} {setWindowFlags}(Qt::WindowFlags flags) + \endlist + + \list + \o 183 public functions inherited from QWidget + \o 28 public functions inherited from QObject + \endlist + + \raw HTML + <h3>Public Slots</h3> + \endraw + + \list + \o 17 public slots inherited from QWidget + \o 1 public slot inherited from QObject + \endlist + + \raw HTML + <h3>Additional Inherited Members</h3> + \endraw + + \list + \o 1 signal inherited from QWidget + \o 1 signal inherited from QObject + \o 4 static public members inherited from QWidget + \o 4 static public members inherited from QObject + \o 39 protected functions inherited from QWidget + \o 7 protected functions inherited from QObject + \endlist + + \target preview window + + \raw HTML + <hr /> + <h2>Detailed Description</h2> + \endraw + + The PreviewWindow class is a custom widget displaying + the names of its currently set window flags in a + read-only text editor. + + The PreviewWindow class inherits QWidget. The widget + displays the names of its window flags set with the \l + {function} {setWindowFlags()} function. It is also + provided with a QPushButton that closes the window. + + ... + + See also QWidget. + + \raw HTML + <hr /> + <h2>Member Function Documentation</h2> + \endraw + + \target constructor + \raw HTML + <h3>PreviewWindow(QWidget *parent = 0)</h3> + \endraw + + Constructs a preview window widget with \e parent. + + \target function + \raw HTML + <h3>setWindowFlags(Qt::WindowFlags flags)</h3> + \endraw + + Sets the widgets flags using the + QWidget::setWindowFlags() function. + + Then runs through the available window flags, + creating a text that contains the names of the flags + that matches the flags parameter, displaying + the text in the widgets text editor. + \endquotation + + \target enum-command + \section1 \\enum + + The \\enum command is for documenting a C++ enum type. The + argument is the full name of the enum type. + + The enum values are documented in the \\enum comment using the \l + {value-command} {\\value} command. If an enum value is not + documented with \\value, QDoc emits a warning. These warnings can + be avoided using the \l {omitvalue-command} {\\omitvalue} command + to tell QDoc that an enum value should not be documented. The enum + documentation will be included on the class reference page, header + file page, or namespace page where the enum type is defined. For + example, consider the enum type \c {Corner} in the Qt namespace: + + \code + enum Corner { + TopLeftCorner = 0x00000, + TopRightCorner = 0x00001, + BottomLeftCorner = 0x00002, + BottomRightCorner = 0x00003 + #if defined(QT3_SUPPORT) && !defined(Q_MOC_RUN) + ,TopLeft = TopLeftCorner, + TopRight = TopRightCorner, + BottomLeft = BottomLeftCorner, + BottomRight = BottomRightCorner + #endif + }; + \endcode + + This enum can be cocumented this way: + + \code + / *! + \enum Qt::Corner + + This enum type specifies a corner in a rectangle: + + \value TopLeftCorner + The top-left corner of the rectangle. + \value TopRightCorner + The top-right corner of the rectangle. + \value BottomLeftCorner + The bottom-left corner of the rectangle. + \value BottomRightCorner + The bottom-right corner of the rectangle. + + \omitvalue TopLeft + \omitvalue TopRight + \omitvalue BottomLeft + \omitvalue BottomRight + * / + \endcode + + Note the inclusion of the namespace qualifier. QDoc will render + this enum type in \c {qt.html} like this: + + \quotation + \raw HTML + <h3 class="fn"><a name="Corner-enum"></a>enum Qt::Corner</h3> + + <p>This enum type specifies a corner in a rectangle:</p> + + <table border="1" cellpadding="2" cellspacing="1" width="100%"> + <tr> + <th width="25%">Constant</th> + <th width="15%">Value</th> + <th width="60%">Description</th> + </tr> + + <tr> + <td valign="top"><tt>Qt::TopLeftCorner</tt></td> + <td align="center" valign="top"><tt>0x00000</tt></td> + <td valign="top">The top-left corner of the rectangle.</td> + </tr> + + <tr> + <td valign="top"><tt>Qt::TopRightCorner</tt></td> + <td align="center" valign="top"><tt>0x00001</tt></td> + <td valign="top">The top-right corner of the rectangle.</td> + </tr> + + <tr> + <td valign="top"><tt>Qt::BottomLeftCorner</tt></td> + <td align="center" valign="top"><tt>0x00002</tt></td> + <td valign="top">The bottom-left corner of the rectangle.</td> + </tr> + + <tr> + <td valign="top"><tt>Qt::BottomRightCorner</tt></td> + <td align="center" valign="top"><tt>0x00003</tt></td> + <td valign="top">The bottom-right corner of the rectangle.</td> + </tr> + + </table> + \endraw + \endquotation + + See also \l {value-command} {\\value} and \l {omitvalue-command} {\\omitvalue}. + + \target example-command + \section1 \\example + + The \\example command is for documenting an example. The argument + is the example's path relative to omne of the paths listed in the + \l {exampledirs-variable} {exampledirs} variable in the QDoc + configuration file. + + The documentation page will be output to \c {path-to-example}.html. + QDoc will add a list of all the example's source files at the top + of the page. + + For example, if \l {exampledirs-variable} {exampledirs} contains + \c $QTDIR/examples/widgets/imageviewer, then + + \code + / *! + \example widgets/imageviewer + \title ImageViewer Example + \subtitle + + The example shows how to combine QLabel and QScrollArea + to display an image. + + ... + * / + \endcode + + QDoc renders this example in widgets-imageviewer.html: + + \quotation + \raw HTML + <center><h1>Image Viewer Example</h1></center> + \endraw + + Files: + \list + \o \l{http://qt.nokia.com/doc/4.0/widgets-imageviewer-imageviewer-cpp.html} + {widgets/imageviewer/imageviewer.cpp} + \o \l{http://qt.nokia.com/doc/4.0/widgets-imageviewer-imageviewer-h.html} + {widgets/imageviewer/imageviewer.h} + \o \l{http://qt.nokia.com/doc/4.0/widgets-imageviewer-main-cpp.html} + {widgets/imageviewer/main.cpp} + \endlist + + The example shows how to combine QLabel and QScrollArea + to display an image. + + ... + \endquotation + + \target externalpage-command + \section1 \\externalpage + + The \\externalpage command assigns a title to an external URL. + + \code + / *! + \externalpage http://doc.qt.nokia.com/index.html + \title Qt Documentation Site + * / + \endcode + + This allows you to include a link to the external page in your + documentation this way: + + \code + / *! + At the \l {Qt Documentation Site} you can find the latest + documentation for Qt, Qt Creator, the Qt SDK and much more. + * / + \endcode + + QDoc renders this as: + + \quotation + At the \l {http://doc.qt.nokia.com/index.html}{Qt Documentation Site} + you can find the latest documentation for Qt, Qt Creator, the Qt SDK + and much more. + \endquotation + + To achieve the same result without using the \\externalpage + command, you would have to hard code the address into your + documentation: + + \code + / *! + At the \l {http://doc.qt.nokia.com/index.html}{Qt Documentation Site} + you can find the latest documentation for Qt, Qt Creator, the Qt SDK + and much more. + * / + \endcode + + The \\externalpage command makes it easier to maintain the + documentation. If the address changes, you only need to change the + argument of the \\externalpage command. + + \target fn-command + \section1 \\fn (function) + + The \\fn command is for documenting a function. The argument is + the function's signature, including its return type, const-ness, + and list of formal arguments with types. If the named function + doesn't exist, QDoc emits a warning. + + \note The \\fn command is QDoc's default command, i.e. when no + topic command can be found in a QDoc comment, QDoc tries to tie + the documentation to the following code as if it is the + documentation for a function. Hence, it is normally not necessary + to include this command when documenting a function, if the + function's QDoc comment is written immediately above the function + implementation in the \c .cpp file. But it must be present when + documenting an inline function in the \c .cpp file that is + implemented in the \c .h file. + + \code + / *! + \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const + + Returns true if this toolbar is dockable in the given + \a area; otherwise returns false. + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h3>bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const + </h3> + \endraw + + Returns true if this toolbar is dockable in the given + \a area; otherwise returns false. + \endquotation + + See also \l {overload-command} {\\overload}. + + \target group-command + \section1 \\group + + The \\group command creates a separate page that lists the classes + belonging to the group. The argument is the group name. + + A class is included in a group by using the \l {ingroup-command} + {\\ingroup} command. Overview pages can also be related to a group + using the same command, but the list of overview pages must be + requested explicitly using the \l {generatelist-command} + {\\generatelist} command (see example below). + + The \\group command is typically followed by a \l {title-command} + {\\title} command and a short introduction to the group. The + HTML page for the group is written to a \c {.html} file put in + <lower-case>\e{group}.html. + + Each class name is listed as a link to the class reference page + followed by the text from the class's \l {brief-command} {\\brief} + texts. + + \code + / *! + \group io + + \title Input/Output and Networking + + These classes are used to handle input and output to + and from external devices, processes, files etc. as + well as manipulating files and directories. + * / + \endcode + + QDoc generates a group page in \c{io.html} that will look + like this: + + \quotation + \raw HTML + + <h1>Input/Output and Networking</h1> + + <p>These classes are used to handle input and output + to and from external devices, processes, files etc. as + well as manipulating files and directories.</p> + + <p> + <table width="100%"> + <tr valign="top" bgcolor="#e0e0e0"> + <td><b> + <a href="http://qt.nokia.com/doc/4.0/qabstractsocket.html">QAbstractSocket</a> + </b></td> + <td> + The base functionality common to all socket types + </td></tr> + + <tr valign="top" bgcolor="#e0e0e0"> + <td><b> + <a href="http://qt.nokia.com/doc/4.0/qbuffer.html">QBuffer</a> + </b></td> + <td> + QIODevice interface for a QByteArray + </td></tr> + + <tr valign="top" bgcolor="#e0e0e0"> + <td><b> + <a href="http://qt.nokia.com/doc/4.0/qclipboard.html">QClipboard</a> + </b></td> + <td> + Access to the window system clipboard + </td></tr> + </table> + \endraw + \endquotation + + Note that overview pages related to the group, must be listed + explicitly using the \l {generatelist-command} {\\generatelist} + command with the \c related argument. + + \code + / *! + \group architecture + + \title Architecture + + These documents describe aspects of Qt's architecture + and design, including overviews of core Qt features and + technologies. + + \generatelist{related} + * / + \endcode + + See also \l {ingroup-command} {\\ingroup} and \l + {generatelist-command} {\\generatelist}. + + \target headerfile-command + \section1 \\headerfile + + The \\headerfile command is for documenting the global functions, + types and macros that are declared in a header file but not in a + namespace. The argument is the name of the header file. The HTML + page is written to a \c {.html} file constructed from the header + file aregument. + + The documentation for a function, type, or macro that is declared + in the header file being documented is included in the header file + page using the \l {relates-command} {\\relates} command. + + If the argument doesn't exist as a header file, the \\headerfile + command creates a documentation page for the header file anyway. + + \code + / *! + \headerfile <QtAlgorithms> + + \title Generic Algorithms + + \brief The <QtAlgorithms> header file provides + generic template-based algorithms. + + Qt provides a number of global template functions in \c + <QtAlgorithms> that work on containers and perform + well-know algorithms. + * / + \endcode + + QDoc generates a header file page \c{qtalgorithms.html} that looks + like this: + + \quotation + \raw HTML + <center><h1><QtAlgorithms> - + Generic Algorithms</h1></center> + <p>The <QtAlgorithms> header file provides generic + template-based algorithms. + <a href="13-qdoc-commands-topics.html#header-command">More...</a> + </p> + + <h3>Functions</h3> + <ul> + <li>RandomAccessIterator + <a href="http://qt.nokia.com/doc/4.0/qlineedit.html#EchoMode-enum">qBinaryFind</a></b> + (RandomAccessIterator begin, RandomAccessIterator end, + const T & value)</li> + <li>...</li></ul> + <hr /> + \endraw + + \target header + + \raw HTML + <h2>Detailed Description</h2> + <p>The <QtAlgorithms> header file provides generic + template-based algorithms. </p> + \endraw + + Qt provides a number of global template functions in \c + <QtAlgorithms> that work on containers and perform + well-know algorithms. + + ... + \endquotation + + \target macro-command + \section1 \\macro + + The \\macro command is for documententin a C++ macro. The argument + is the macro in one of three styles: function-like macros like + Q_ASSERT(), declaration-style macros like Q_PROPERTY(), and macros + without parentheses like Q_OBJECT. + + The \\macro comment must contain a \l {relates-command} + {\\relates} command that attaches the macro comment to a class, + header file, or namespace. Otherwise, the documentation will be + lost. Here are three example macro comments followed by what they + might look like in \c {qtglobal.html} or \c {qobject.html}: + + \code + / *! + \macro void Q_ASSERT(bool test) + \relates <QtGlobal> + + Prints a warning message containing the source code + file name and line number if \a test is false. + + ... + + \sa Q_ASSERT_X(), qFatal(), {Debugging Techniques} + * / + \endcode + + \quotation + \raw HTML + <h3>void Q_ASSERT ( bool <i>test</i> )</h3> + \endraw + + Prints a warning message containing the source code + file name and line number if \a test is false. + + ... + + See also Q_ASSERT_X(), qFatal() and \l {Debugging Techniques}. + + \endquotation + + \code + / *! + \macro Q_PROPERTY(...) + \relates QObject + + This macro declares a QObject property. The syntax is: + + ... + + \sa {Qt's Property System} + * / + \endcode + + \quotation + \raw HTML + <h3>Q_PROPERTY ( ... )</h3> + \endraw + + This macro declares a QObject property. The syntax is: + + ... + + See also \l {Qt's Property System}. + \endquotation + + \code + / *! + \macro Q_OBJECT + \relates QObject + + The Q_OBJECT macro must appear in the private section + of a class definition that declares its own signals and + slots or that uses other services provided by Qt's + meta-object system. + + ... + + \sa {Meta-Object System}, {Signals and Slots}, {Qt's + Property System} + * / + \endcode + + \quotation + \raw HTML + <h3>Q_OBJECT</h3> + \endraw + + The Q_OBJECT macro must appear in the private section + of a class definition that declares its own signals and + slots or that uses other services provided by Qt's + meta-object system. + + ... + + See also \l {Meta-Object System}, \l {Signals & + Slots} and \l {Qt's Property System}. + \endquotation + + \target module-command + \section1 \\module + + The \\module creates a page that lists the classes belonging to + the module specified by the command's argument. A class included + in the module by including the \l {inmodule-command} {\\inmodule} + command in the \\class comment. + + The \\module command is typically followed by a \l {title-command} + {\\title} and a \l {brief-command} {\\brief} command. Each class + is listed as a link to the class reference page followed by the + text from the class's \l {brief-command} {\\brief} command. For + example: + + \code + / *! + \module QtNetwork + + \title QtNetwork Module + + \brief The QtNetwork module offers classes that allow + you to write TCP/IP clients and servers. + + The network module provides classes to make network + programming easier and portable. It offers both + high-level classes such as QNetworkAccessManager that + implements application-level protocols, and + lower-level classes such as QTcpSocket, QTcpServer, and + QUdpSocket. + * / + \endcode + + QDoc renders this in \c {qtnetwork.html} like this: + + \quotation + \raw HTML + <h1><center>QtNetwork Module</center></h1> + \endraw + + The QtNetwork module offers classes that allow you to + write TCP/IP clients and servers.\l {module + details} {More...} + + \raw HTML + <p> + <table width="100%"> + <tr valign="top" bgcolor="#d0d0d0"> + <td><b> + <a href="http://qt.nokia.com/doc/4.0/qabstractsocket.html">QAbstractSocket</a> + </b></td> + <td> + The base functionality common to all socket types + </td></tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td><b> + <a href="http://qt.nokia.com/doc/4.0/qftp.html">QFtp</a> + </b></td> + <td> + Implementation of the FTP protocol + </td></tr> + + <tr valign="top" bgcolor="#d0d0d0"> + <td>...</td> + <td>...</td> + </tr> + </table> + + <p><hr /></p> + \endraw + + \target module details + + \raw HTML + <h2>Detailed Description</h2> + + <p> + The QtNetwork module offers classes that allow you to + write TCP/IP clients and servers. + </p> + + <p> + The network module provides classes to make network + programming easier and portable. It offers both + high-level classes such as QNetworkAccessManager that + implements application-level protocols, and + lower-level classes such as QTcpSocket, QTcpServer, and + QUdpSocket. + </p> + \endraw + + ... + + \endquotation + + See also \l {inmodule-command} {\\inmodule} + + \target namespace-command + \section1 \\namespace + + The \\namespace command is for documenting the contents of the C++ + namespace named as its argument. The documentation outline QDoc + generates for a namespace is similar to the outline it generates + for a C++ class. + + \code + / *! + \namespace Qt + + \brief The Qt namespace contains miscellaneous + identifiers used throughout the Qt library. + * / + \endcode + + QDoc renders this in \c{qt.html} like this: + + \quotation + \raw HTML + <center><h1>Qt Namespace Reference</h1></center> + <p>The Qt namespace contains miscellaneous + identifiers used throughout the Qt library. + <a href="13-qdoc-commands-topics.html#name">More...</a> + </p> + + <pre>#include <Qt></pre> + <ul> + <li> + <a href="http://qt.nokia.com/doc/4.0/qt-qt3.html"> + Qt 3 support members</a></li> + </ul> + + + <h3>Types</h3> + <ul> + <li>flags + <a href="http://qt.nokia.com/doc/4.0/qt.html#AlignmentFlag-enum">Alignment</a></b></li> + <li>...</li></ul> + <hr /> + \endraw + + \target name + + \raw HTML + <h2>Detailed Description</h2> + <p>The Qt namespace contains miscellaneous identifiers + used throughout the Qt library.</p> + \endraw + + ... + \endquotation + + \target page-command + \section1 \\page + + The \\page command is for creating a stand-alone documentation + page. The argument can consist of two parts separated by a + space. The first part is the name of the file where QDoc should + store the page. The second part, if present, is a word that + specifies the page type. Currently, the second part can be one of + the following list of words: + + \list + + \o faq - A frequently asked question. + + \o howto - A user guide for how to use some component of the + software. + + \o example - A page that describes a working example. + + \o overview - For text pages that provide an overview of some + important subject. + + \o tutorial - For text pages that are part of a tutorial. + + \o api - This is the type of page used for C++ class references + and QML element references, etc. You should never use this one for + the pages you write, because this one is reserved for qdoc. + + \endlist + + The page title is set using the \l {title-command} {\\title} + command. + + \code + / *! + \page aboutqt.html + + \title About Qt + + Qt is a C++ toolkit for cross-platform GUI + application development. Qt provides single-source + portability across Microsoft Windows, Mac OS X, Linux, + and all major commercial Unix variants. + + Qt provides application developers with all the + functionality needed to build applications with + state-of-the-art graphical user interfaces. Qt is fully + object-oriented, easily extensible, and allows true + component programming. + + ... + * / + \endcode + + QDoc renders this page in \c {aboutqt.html}. + + \target property-command + \section1 \\property + + The \\property command is for documenting a Qt property. The + argument is the full property name. + + A property is defined using the Q_PROPERTY() macro. The macro + takes as arguments the property's name and its set, reset and get + functions. + + \code + Q_PROPERTY(QString state READ state WRITE setState) + \endcode + + The set, reset and get functions don't need to be documented, + documenting the property is sufficient. QDoc will generate a list + of the access function that will appear in the property + documentation which in turn will be located in the documentation + of the class that defines the property. + + The \\property command comment typically includes a \l + {brief-command} {\\brief} command. Forproperties the \l + {brief-command} {\\brief} command's argument is a sentence + fragment that will be included in a one line description of the + property. The command follows the same rules for the \l + {brief-property} {description} as the \l {variable-command} + {\\variable} command. + + \code + / *! + \property QPushButton::flat + \brief whether the border is disabled + + This property's default is false. + * / + \endcode + + QDoc includes this in \c {qpushbutton.html} like this: + + \quotation + \raw HTML + <h3>flat : bool</h3> + \endraw + + This property holds whether the border is disabled. + + This property's default is false. + + Access functions: + + \list + \o \bold { bool isFlat () const} + \o \bold { void setFlat ( bool )} + \endlist + + \endquotation + + \code + / *! + \property QWidget::width + \brief the width of the widget excluding any window frame + + See the \l {Window Geometry} documentation for an + overview of window geometry. + + \sa geometry, height, size + * / + \endcode + + QDoc includes this in \c {qwidget.html} like this: + + \quotation + \raw HTML + <h3>width : const int</h3> + \endraw + + This property holds the width of the widget excluding + any window frame. + + See the \l {Window Geometry} documentation for an + overview of window geometry. + + Access functions: + + \list + \o \bold { int width () const} + \endlist + + See also \l{QWidget::geometry} {geometry}, + \l{QWidget::height} {height}, and \l{QWidget::size} {size}. + \endquotation + + \target service-command + \section1 \\service + + The \\service command tells QDoc that a class is a service class + and names the service. The command takes two arguments, the name + of the class and the name of the service. Currently, this command + is not used in the Qt documentation. + + \code + / *! + \service TimeService Time + ... + * / + class TimeService : public QCopObjectService + { + ... + } + \endcode + + See also \l {class-command} {\\class} and \l + {generatelist-command} {\\generatelist}. + + \target qmlattachedproperty-command + \section1 \\qmlattachedproperty + + The \\qmlattachedproperty command is for documenting a QML + property that will be attached to some QML element type. See + \l{http://doc.qt.nokia.com/4.7/qdeclarativeintroduction.html#attached-properties} + {Attached Properties}. The argument is the rest of the line. The + argument text should be the property type, followed by the QML + element name where the property is being declared, the \c{::} + qualifier, and finally the property name. If we have a QML + attached property named \c isCurrentItem in QML element \c ListView, + and the property has type \c {bool}, the \\qmlattachedproperty for + it would look like this: + + \code + / *! + \qmlattachedproperty bool ListView::isCurrentItem + This attached property is true if this delegate is the current + item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current + item, for example: + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem + * / + \endcode + + QDoc includes this attached property on the QML reference page for the + \l{http://doc.qt.nokia.com/4.7/qml-listview.html#isCurrentItem-prop} + {ListView} element. + + \target qmlattachedsignal-command + \section1 \\qmlattachedsignal + + The \\qmlattachedsignal command is for documenting an attachable + \l{http://doc.qt.nokia.com/4.7/qdeclarativeintroduction.html#signal-handlers} + {signal handler}. The \\qmlattachedsignal command is used just like + the \l{qmlsignal-command} {\\qmlsignal} command. + + The argument is the rest of the line. It should be the name of the + QML element where the signal handler is declared, the \c{::} + qualifier, and finally the signal handler name. If we have a QML + attached signal handler named \c onAdd() in the \c GridView + element, the \\qmlattachedsignal for it would look like this: + + \code + / *! + \qmlattachedsignal GridView::onAdd() + This attached handler is called immediately after an item is + added to the view. + * / + \endcode + + QDoc includes this documentation on the QML reference page for the + \l{http://doc.qt.nokia.com/4.7/qml-gridview.html#onAdd-signal} + {GridView} element. + + \target qmlbasictype-command + \section1 \\qmlbasictype + + The \\qmlbasictype command is for documenting a basic type for QML. + The argument is the type name. The type must be included in the + QML basic types group using the \l{ingroup-command}{\\ingroup} + command as shown below. This will cause QDoc to include the + documentation for the type on the + \l{http://doc.qt.nokia.com/4.7/qdeclarativebasictypes.html} + {QML Basic Types} page. The \l{brief-command} {\\brief} command + is also required, because it appears on the + \l{http://doc.qt.nokia.com/4.7/qdeclarativebasictypes.html} + {QML Basic Types} page as well. + + \code + / *! + \qmlbasictype int + \ingroup qmlbasictypes + + \brief An integer is a whole number, e.g. 0, 10, or -20. + + An integer is a whole number, e.g. 0, 10, or -20. The possible + \c int values range from around -2000000000 to around + 2000000000, although most elements will only accept a reduced + range (which they mention in their documentation). + + Example: + \qml + Item { width: 100; height: 200 } + \endqml + + \sa {QML Basic Types} + * / + \endcode + + QDoc outputs this as \l{http://doc.qt.nokia.com/4.7/qml-int.html} + {qml-int.html}. + + \target qmlclass-command + \section1 \\qmlclass + + The \\qmlclass command is for documenting a QML element that is + instantiated by a C++ class. The command has two arguments. The + first argument is the name of the QML element. The second argument + is the name of the C++ class that instantiates the QML element. + + \code + / *! + \qmlclass Transform QGraphicsTransform + \ingroup qml-transform-elements + \since 4.7 + \brief The Transform elements provide a way of building + advanced transformations on Items. + + The Transform element is a base type which cannot be + instantiated directly. The following concrete Transform types + are available: + + \list + \o \l Rotation + \o \l Scale + \o \l Translate + \endlist + + The Transform elements let you create and control advanced + transformations that can be configured independently using + specialized properties. + + You can assign any number of Transform elements to an \l + Item. Each Transform is applied in order, one at a time. + + * / + \endcode + + This example generates the + \l {http://doc.qt.nokia.com/4.7/qml-transform.html} {QML Transform + Element} page. The \\qmlclass comment should include the \l + {since-command} {\\since} command, because all QML elements are + new. It should also include the \l{brief-command} {\\brief} + command. And since every QML element is a member of a group of QML + elements, it should also include one or more \l{ingroup-command} + {\\ingroup} commands. + + \target qmlmethod-command + \section1 \\qmlmethod + + The \\qmlmethod command is for documenting a QML method. The + argument is the complete method signature, including return + type and parameter names and types. + + \code + / *! + \qmlmethod void TextInput::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser and + selectionEnd will become the greater (regardless of the order + passed to this method). + + \sa selectionStart, selectionEnd + * / + \endcode + + QDoc includes this documentation on the element refence page for the + \l{http://doc.qt.nokia.com/4.7/qml-textinput.html#select-method} + {TextInput} element. + + \target qmlproperty-command + \section1 \\qmlproperty + + The \\qmlproperty command is for documenting a QML property. The + argument is the rest of the line. The argument text should be the + property type, followed by the QML element name, the \c{::} + qualifier, and finally the property name. If we have a QML + property named \c x in QML element \c Translate, and the property + has type \c {real}, the \\qmlproperty for it would look like this: + + \code + / *! + \qmlproperty real Translate::x + + The translation along the X axis. + * / + \endcode + + QDoc includes this QML property on the QML reference page for the + \l {http://doc.qt.nokia.com/4.7/qml-translate.html} {Translate} + element. + + \target qmlsignal-command + \section1 \\qmlsignal + + The \\qmlsignal command is for documenting a + \l{http://doc.qt.nokia.com/4.7/qdeclarativeintroduction.html#signal-handlers} + {signal handler}. + The argument is the rest of the line. It should be the QML element where the + signal handler is declared, the \c{::} qualifier, and finally the signal + handler name. If we have a QML signal handler named \c onAdd() in QML + element \c MouseArea, the \\qmlsignal for it would look like this: + + \code + / *! + \qmlsignal MouseArea::onEntered() + + This handler is called when the mouse enters the mouse area. + + By default the onEntered handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onEntered when no mouse button is pressed. + + \sa hoverEnabled + * / + \endcode + + QDoc includes this documentation on the QML reference page for the + \l{http://doc.qt.nokia.com/4.7/qml-mousearea.html#onEntered-signal} + {MouseArea} element. + + \target qmlmodule-command + \section1 \\qmlmodule + + Insert the \c{\\qmlmodule} command to create a \c QML module page. A QML + module is a collection of QML components or any related material. This + command is similar to the \l{group-command}. + + A QML class may belong to a module by inserting the + \l{inqmlmodule-command}{\\inqmlmodule} command as a topic command. + Every member of a group must be linked to using the module name and two + colons (\c{::}). + + \code + A link to the UI Component's TabWidget is \l {UIComponent::TabWidget}. + \endcode + + QDoc will generate a page for the module with a listing of the members + of the module. + + \code + \qmlmodule ClickableComponents + + This is a list of the Clickable Components set. A Clickable component + responds to a \c clicked() event. + \endcode + + The \l{componentset}{UIComponents} example demonstrates proper usage of + QDoc commands to document QML components and QML modules. + + \target inqmlmodule-command + \section1 \\inqmlmodule + + A QML class may belong to a \l{qmlmodule-command}{QML module} by inserting + the \l{inqmlmodule-command}{\\inqmlmodule} command as a topic command. + Every member of a group must be linked to using the module name and two + colons (\c{::}). + + \code + \qmlclass ClickableButton + \inqmlmodule ClickableComponents + + A clickable button that responds to the \c click() event. + \endcode + + To link to the \c ClickableButton, use the + \c{\l ClickableComponents::ClickableButton} format. + + The \l{componentset}{UIComponents} example demonstrates proper usage of + QDoc commands to document QML components and QML modules. + + \target typedef-command + \section1 \\typedef + + The \\typedef command is for documenting a C++ typedef. The + argument is the name of the typedef. The documentation for + the typedef will be included in the refernece documentation + for the class, namespace, or header file in which the typedef + is declared. To relat the \\typedef to a class, namespace, or + header file, the \\typedef comment must contain a + \l {relates-command} {\\relates} command. + + \code + / *! + \typedef QObjectList + \relates QObject + + Synonym for QList<QObject>. + * / + \endcode + + QDoc includes this in \c {qobject.html} as: + + \quotation + \raw HTML + <h3>typedef QObjectList</h3> + \endraw + + Synonym for QList<QObject>. + \endquotation + + Another, although more rare, example: + + \code + / *! + \typedef QMsgHandler + \relates QtGlobal + + This is a typedef for a pointer to a function with the + following signature: + + \code + void myMsgHandler(QtMsgType, const char *); + \ endcode + + \sa QtMsgType, qInstallMsgHandler() + * / + \endcode + + QDoc includes this in \c {qtglobal.html} as: + + \quotation + \raw HTML + <h3>typedef QtMsgHandler</h3> + \endraw + + This is a typedef for a pointer to a function with the + following signature: + + \raw HTML + <tt> + <pre> void myMsgHandler(QtMsgType, const char *);</pre> + </tt> + \endraw + + See also QtMsgType and qInstallMsgHandler(). + \endquotation + + Other typedefs are located on the reference page for the class + that defines them. + + \code + / *! + \typedef QLinkedList::Iterator + + Qt-style synonym for QList::iterator. + * / + \endcode + + QDoc includes this one on the reference page for class QLinkedList as: + + \quotation + \raw HTML + <h3>typedef QLinkedList::Iterator</h3> + \endraw + + Qt-style synonym for QList::iterator. + \endquotation + + \target variable-command + \section1 \\variable + + The \\variable command is for documenting a class member variable + or a constant. The argument is the variable or constant name. The + \\variable command comment includes a \l {brief-command} {\\brief} + command. QDoc generates the documentation based on the text from + \\brief command. + + The documentation will be located in the in the associated class, + header file or namespace documentation. + + In case of a member variable: + + \code + / *! + \variable QStyleOption::palette + \brief the palette that should be used when painting + the control + * / + \endcode + + QDoc includes this in qstyleoption.html as: + + \quotation + \raw HTML + <h3> + <a href="http://qt.nokia.com/doc/4.0/qpalette.html"> + QPalette + </a> + QStyleOption::palette + </h3> + \endraw + + This variable holds the palette that should be used + when painting the control. + \endquotation + + You can also document constants with the \\variable command. For + example, suppose you have the \c Type and \c UserType constants in + the QTreeWidgetItem class: + + \code + enum { Type = 0, UserType = 1000 }; + \endcode + + For these, the \\vaqriable command can be used this way: + + \code + / *! + \variable QTreeWidgetItem::Type + + The default type for tree widget items. + + \sa UserType, type() + * / + \endcode + \code + / *! + \variable QTreeWidgetItem::UserType + + The minimum value for custom types. Values below + UserType are reserved by Qt. + + \sa Type, type() + * / + \endcode + + QDoc includes these in qtreewidget.html as: + + \quotation + \raw HTML + <h3> + const int QTreeWidgetItem::Type + </h3> + \endraw + + The default type for tree widget items. + + See also \l {QTreeWidgetItem::UserType} {UserType} and \l + {QTreeWidgetItem::type()} {type()}. + + \raw HTML + <h3> + const int QTreeWidgetItem::UserType + </h3> + \endraw + + The minimum value for custom types. Values below + UserType are reserved by Qt. + + See also \l {QTreeWidgetItem::Type} {Type} and + \l{QTreeWidgetItem::type()} {type()}. + + \endquotation +*/ + +/*! + \page 14-qdoc-commands-contextcommands.html + \previouspage Topic Commands + \contentspage Table of Contents + \nextpage Document Navigation + + \title Context Commands + + The context commands provide information about the element being + documented that QDoc can't deduce on its own. e.g. Is a class + thread-safe? Is a function reentrant? Which module is the class a + member of? Context commands can appear anywhere in a QDoc comment, + but they are normally placed near the top of the comment, just + below the \l {Topic Commands} {topic} command. + + \list + \o \l {16-qdoc-commands-status.html#compat-command}{\\compat}, + \o \l {15-qdoc-commands-navigation.html#contentspage-command}{\\contentspage}, + \o \l {15-qdoc-commands-navigation.html#indexpage-command}{\\indexpage}, + \o \l {19-qdoc-commands-grouping.html#ingroup-command}{\\ingroup}, + \o \l {18-qdoc-commands-relating.html#inherits-command}{\\inherits}, + \o \l {19-qdoc-commands-grouping.html#inmodule-command}{\\inmodule}, + \o \l {16-qdoc-commands-status.html#internal-command}{\\internal}, + \o \l {19-qdoc-commands-grouping.html#mainclass-command}{\\mainclass}, + \o \l {15-qdoc-commands-navigation.html#nextpage-command}{\\nextpage}, + \o \l {17-qdoc-commands-thread.html#nonreentrant-command}{\\nonreentrant}, + \o \l {16-qdoc-commands-status.html#obsolete-command}{\\obsolete}, + \o \l {18-qdoc-commands-relating.html#overload-command}{\\overload}, + \o \l {16-qdoc-commands-status.html#preliminary-command}{\\preliminary}, + \o \l {15-qdoc-commands-navigation.html#previouspage-command}{\\previouspage}, + \o \l {17-qdoc-commands-thread.html#reentrant-command}{\\reentrant}, + \o \l {18-qdoc-commands-relating.html#reimp-command}{\\reimp}, + \o \l {18-qdoc-commands-relating.html#relates-command}{\\relates}, + \o \l {16-qdoc-commands-status.html#since-command}{\\since}, + \o \l {15-qdoc-commands-navigation.html#startpage-command}{\\startpage}, + \o \l {20-qdoc-commands-namingthings.html#subtitle-command}{\\subtitle} + \o \l {17-qdoc-commands-thread.html#threadsafe-command}{\\threadsafe}, + \o \l {20-qdoc-commands-namingthings.html#title-command}{\\title} + \endlist + +*/ + +/*! + \page 15-qdoc-commands-navigation.html + \previouspage Context Commands + \contentspage Table of Contents + \nextpage Reporting Status + + \title Document Navigation + + The navigation commands are for linking the pages of a document in + a meaningful sequence. Below is a sequence of QDoc comments that + shows a typical use of the navigation commands. + + \section1 Example + + \code + / *! + \page basicqt.html + \contentspage {Basic Qt} {Contents} + \nextpage Getting Started + + \indexpage Index + \startpage Basic Qt + + \title Basic Qt + + The Qt toolkit is a C++ class library and a set of tools for + building multiplatform GUI programs using a "write once, + compile anywhere approach". + + Table of contents: + + \list + \o \l {Getting Started} + \o \l {Creating Dialogs} + \o \l {Creating Main Windows} + \endlist + * / + + / *! + \page gettingstarted.html + \previouspage Basic Qt + \contentspage {Basic Qt} {Contents} + \nextpage Creating Dialogs + + \indexpage Index + \startpage Basic Qt + + \title Getting Started + + This chapter shows how to combine basic C++ with the + functionality provided by Qt to create a few small graphical + interface (GUI) applications. + * / + + / *! + \page creatingdialogs.html + \previouspage Getting Started + \contentspage {Basic Qt} {Contents} + + \indexpage Index + \startpage Basic Qt + + \title Creating Dialogs + + This chapter will teach you how to create dialog boxes using Qt. + * / + + / *! + \page index.html + + \indexpage Index + \startpage Basic Qt + + \title Index + + \list + \o \l {Basic Qt} + \o \l {Creating Dialogs} + \o \l {Getting Started} + \endlist + * / + \endcode + + QDoc renders the "Getting Started" page in \c{creatingdialogs.html}: + + \quotation + \raw HTML + <table border="0" cellpadding="0" cellspacing="5" width="100%"> + + <tr> + <p> + [Previous: <a href="15-qdoc-commands-navigation.html#deadlink"> + Basic Qt</a>] + [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>] + [Next: <a href="15-qdoc-commands-navigation.html#deadlink"> + Creating Dialogs</a>] + </p> + + <h1 align="center">Getting Started<br /></h1> + + <p> + This chapter shows how to combine basic C++ with the + functionality provided by Qt to create a few small graphical + interface (GUI) applications. + </p> + + <p> + [Previous: <a href="15-qdoc-commands-navigation.html#deadlink"> + Basic Qt</a>] + [<a href="15-qdoc-commands-navigation.html#deadlink">Contents</a>] + [Next: <a href="15-qdoc-commands-navigation.html#deadlink"> + Creating Dialogs</a>] + </p> + + </table> + \endraw + \endquotation + + The \l {indexpage-command} {\\indexpage} and \l + {startpage-command} {\\startpage} commands create links to the + page's index page and start page. These links can be used by + browsers and search engines. + + The index page is typically an alphabetical list of the document's + titles and topics, while the start page is the page considered by + the author to be the starting point of a multipage document. + + The links are included in the generated HTML source code but have + no visual effect on the documentation: + + \code + <head> + ... + <link rel="index" href="index.html" /> + <link rel="start" href="basicqt.html" /> + ... + </head> + \endcode + + \section1 Commands + + \target previouspage-command + \section2 \\previouspage + + The \\previouspage command links the current page to the previous + page in a sequence.a The command has two arguments, each enclosed + by curly braces: The first is the link target, i.e. the title of + the previous page, the second is the link text. If the page's + title is equivalent to the link text, the second argument can be + omitted. + + The command must stand alone on its own line. + + \target nextpage-command + \section2 \\nextpage + + The \\nextpage command links the current page to the next page in + a sequence. The command follows the same syntax and argument + convention as the \l {previouspage-command} {\\previouspage} + command. + + \target startpage-command + \section2 \\startpage + + The \\startpage command specifies the first page of a sequence of + pages. The command must stand alone on its own line, and its + unique argument is the title of the first document. + + QDoc will generate a link to the start page and include it in the + generated HTML file, but this has no visual effect on the + documentation. The generated link type tells browsers and search + engines which document is considered by the author to be the + starting point of the collection. + + \target contentspage-command + \section2 \\contentspage + + The \\contentspage command links the current page to a table of + contents page. The command follows the same syntax and argument + convention as the \l {previouspage-command} {\\previouspage} + command. + + \target indexpage-command + \section2 \\indexpage + + The \\indexpage command specifies an index page for the current + document. The command must stand alone on its own line, and its + unique argument is the title of the index document. + + QDoc will generate a link to the index page and include it in the + generated HTML file, but this has no visual effect on the + documentation. The generated link type tells browsers and search + engines which document is considered by the author to be the + index page of the collection. +*/ + +/*! + \page 16-qdoc-commands-status.html + \previouspage Document Navigation + \contentspage Table of Contents + \nextpage Thread Support + + \title Reporting Status + + These commands are for indicating that a documented element is + still under development, is becoming obsolete, is provided for + compatibility reasons, or is simply not to be included in the + public interface. The \l {since-command}{\\since} command is for + including information about the version when a function or class + first appeared. + + \target compat-command + \section1 \\compat + + The \\compat command is for indicating that a class or function is + part of the support library provided to keep old source code + working. + + The command must stand on its own line. + + Usually an equivalent function or class is provided as an + alternative. + + If the command is used in the documentation of a class, the + command expands to a warning that the referenced class is part of + the support library. The warning is located at the top of the + documentation page. + + \code + / *! + \class MyQt3SupportClass + \compat + * / + \endcode + + QDoc renders this at the top of the MyQt3SupportClass class + reference page. + + \quotation + \bold {This class is part of the Qt 3 support + library.} It is provided to keep old source code + working. We strongly advise against using it in new + code. See the \l + {http://qt.nokia.com/doc/4.0/porting4.html} {Porting + Guide} for more information. + \endquotation + + If the command is used when documenting a function, QDoc will + create and link to a separate page documenting Qt 3 support + members when generating the reference documentation for the + associated class. + + \code + / *! + \fn MyClass::MyQt3SupportMemberFunction + \compat + + Use MyNewFunction() instead. + * / + \endcode + + QDoc renders this in \c{myclass-qt3.html} as: + + \quotation + \raw HTML + <h1>Qt 3 Support Members for MyClass</h1> + \endraw + + \bold {The following class members are part of the Qt 3 + support layer.} They are provided to help you port old code to + Qt 4. We advise against using them in new code. + + ... + + \list + \o void MyQt3SupportMemberFunction() + \o ... + \endlist + + \raw HTML + <hr /> + <h2>Member Function Documentation</h2> + <h3>void MyQt3SupportMemberFunction ()</h3> + <p>Use MyNewFunction() instead.</p> + \endraw + ... + \endquotation + + \target default-command + \section1 \\default + + The \\default command is for marking a QML property as the + \l {http://doc.qt.nokia.com/4.7/qdeclarativeintroduction.html#default-properties} + {default property}. The word \span {class="newStuff"} {default} is shown in red in + the documentation of the property. + + \code + / *! + \qmlproperty list<Change> State::changes + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. + * / + \endcode + + See how QDoc renders this property on the reference page for the + \l {http://doc.qt.nokia.com/4.7/qml-state.html#changes-prop} {State} + element. + + \target obsolete-command + \section1 \\obsolete + + The \\obsolete command is for indicating that a function is being + deprecated, and it should no longer be used in new code. There is + no guarantee for how long it will remain in the library. + + The command must stand on its own line. + + When generating the reference documentation for a class, QDoc will + create and link to a separate page documenting its obsolete + functions. Usually an equivalent function is provided as an + alternative. + + \code + / *! + \fn MyClass::MyObsoleteFunction + \obsolete + + Use MyNewFunction() instead. + * / + \endcode + + QDoc renders this in \c{myclass-obsolete.html} as: + + \quotation + \raw HTML + <h1>Obsolete Members for MyClass</h1> + \endraw + + \bold {The following class members are obsolete.} They are + provided to keep old source code working. We strongly advise + against using them in new code. + + ... + + \list + \o void MyObsoleteFunction() \c (obsolete) + \o ... + \endlist + + \raw HTML + <hr /> + <h2>Member Function Documentation</h2> + <h3>void MyObsoleteFunction ()</h3> + <p>Use MyNewFunction() instead.</p> + \endraw + ... + \endquotation + + \target internal-command + \section1 \\internal + + The \\internal command indicates that the referenced + function is not part of the public interface. + + The command must stand on its own line. + + QDoc ignores the documentation as well as the documented item, + when generating the associated class reference documenation. + + \code + / *! + \internal + + Tries to find the decimal separator. If it can't find + it and the thousand delimiter is != '.' it will try to + find a '.'; + * / + int QDoubleSpinBoxPrivate::findDelimiter + (const QString &str, int index) const + { + int dotindex = str.indexOf(delimiter, index); + if (dotindex == -1 && thousand != dot && delimiter != dot) + dotindex = str.indexOf(dot, index); + return dotindex; + } + \endcode + + This function will not be included in the documentation. + + \target preliminary-command + \section1 \\preliminary + + The \\preliminary command is for indicating that a referenced + function is still under development. + + The command must stand on its own line. + + The \\preliminary command expands to a notification in the + function documentation, and marks the function as preliminary when + it appears in lists. + + \code + / *! + \preliminary + + Returns information about the joining properties of the + character (needed for certain languages such as + Arabic). + * / + QChar::Joining QChar::joining() const + { + return ::joining(*this); + } + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h3> + <a href="http://qt.nokia.com/doc/4.0/qchar.html#Joining-enum">Joining</a> + QChar::joining () const</h3> + \endraw + + \bold {This function is under development and + subject to change.} + + Returns information about the joining properties of the + character (needed for certain languages such as + Arabic). + \endquotation + + And the function's entry in QChar's list of functions will be + rendered as: + + \quotation + \list + \o ... + \o Joining + \l {http://qt.nokia.com/doc/4.0/qchar.html#Joining-enum} + {joining}() + const \c (preliminary) + \o ... + \endlist + \endquotation + + \target since-command + \section1 \\since + + The \\since command tells in which minor release + the associated functionality was added. + + \code + / *! + \since 4.1 + + Returns an icon for \a standardIcon. + + ... + + \sa standardIconImplementation(), standardPixmap() + * / + QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const + { + } + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h3>QIcon QStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const</h3> + \endraw + + This function was introduced in Qt version 4.1 + + Returns an icon for \a standardIcon. + + ... + + See also \l {QStyle::standardIconImplementation()} + {standardIconImplementation()} and \l + {QStyle::standardPixmap()} {standardPixmap()}. + \endquotation + + QDoc generates the "Qt" reference from the \l + {25-qdoc-configuration-derivedprojects.html#project} {\c project} + configuration variable. For that reason this reference will change + according to the current documentation project. + + See also \l {25-qdoc-configuration-derivedprojects.html#project} + {\c project}. +*/ + +/*! + \page 17-qdoc-commands-thread.html + \previouspage Reporting Status + \contentspage Table of Contents + \nextpage Relating Things + + \title Thread Support + + The thread support commands are for specifying the level of + support for multithreaded programming in a class or function. + There are three levels of support: \c threadsafe, \c reentrant and + \c nonreentrant. + + The default is \c nonreentrant which means that the associated + class or function cannot be called by multiple threads. \c + Reentrant and \c threadsafe are levels primarily used for classes. + + \c Reentrant means that all the functions in the referenced class + can be called simultaneously by multiple threads, provided that + each invocation of the functions reference unique data. While \c + threadsafe means that all the functions in the referenced class + can be called simultaneously by multiple threads even when each + invocation references shared data. + + When a class is marked \l {reentrant-command} {\\reentrant} or \l + {threadsafe-command} {\\threadsafe}, functions in that class can + be marked \c nonreentrant using the \l {nonreentrant-command} + {\\nonreentrant} command. + + \section1 Example + + \target reentrant-example + \code + / *! + \class QLocale + \brief The QLocale class converts between numbers and their + string representations in various languages. + + \reentrant + \ingroup i18n + \ingroup text + \mainclass + + QLocale is initialized with a language/country pair in its + constructor and offers number-to-string and string-to-number + conversion functions similar to those in QString. + + ... + * / + + / *! + \nonreentrant + + Sets the global default locale to \a locale. These values are + used when a QLocale object is constructed with no + arguments. If this function is not called, the system's locale + is used. + + \warning In a multithreaded application, the default locale + should be set at application startup, before any non-GUI + threads are created. + + \sa system() c() + * / + void QLocale::setDefault(const QLocale &locale) + { + default_d = locale.d; + } + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h1><center>QLocale Class Reference</center></h1> + \endraw + + The QLocale class converts between numbers and their string + representations in various languages. More... + + \code + #include <QLocale> + \endcode + + \bold {Note:} All the functions in this class are \l + {threads.html#reentrant} {reentrant}, except \l + {QLocale::setDefault()} {setDefault()}. + + ... + + \raw HTML + <hr /> + <h2>Member Type Documentation</h2> + \endraw + + ... + + \raw HTML + <h3>void QLocale::setDefault ( const QLocale & locale ) </h3> + \endraw + + Sets the global default locale to locale. These values are + used when a QLocale object is constructed with no + arguments. If this function is not called, the system's locale + is used. + + \warning In a multithreaded application, the default locale + should be set at application startup, before any non-GUI + threads are created. + + \warning This function is not reentrant. + + See also \l {QLocale::system()} {system()} and \l + {QLocale::c()} {c()}. + + ... + \endquotation + + As shown above, QDoc generates a notification when a class is + declared reentrant, and lists the exceptions (the declared + nonreentrant functions). A link to the general documentation on \l + {threads.html#reentrant} {reentrancy and thread-safety} is + included. In addition a warning, "\bold Warning: This function is + not reentrant.", is generated in the nonreentrant functions' + documentation. + + QDoc will generate the same notification and warnings when a class + is declared threadsafe. + + For more information see the general documentation on \l + {threads.html#reentrant} {reentrancy and thread-safety}. + + \section1 Commands + + \target threadsafe-command + \section2 \\threadsafe + + The \\threadsafe command includes a line in the documentation to + indicate that the associated class or function is \e threadsafe + and can be called simultaneously by multiple threads, even when + separate invocations reference shared data. + + The command must stand on its own line. + + The documentation generated from this command will be similar to + the what is generated for the \l {reentrant-command} {\\reentrant} + command. See the example above in the \l {reentrant-example} + {introduction}. + + See also \l{reentrant-command} {\\reentrant} and + \l{nonreentrant-command} {\\nonreentrant}. + + \target reentrant-command + \section2 \\reentrant + + The \\reentrant command indicates that the associated class or + function can be called simultaneously by multiple threads, + provided that each invocation references its own data. See the \l + {reentrant-example} {example} above. + + The command must stand on its own line. + + See also \l{nonreentrant-command} {\\nonreentrant} and + \l{threadsafe-command} {\\threadsafe}. + + \target nonreentrant-command + \section2 \\nonreentrant + + The \\nonreentrant command indicates that the associated class or + function cannot be called by multiple threads. Nonreentrant is the + default case. + + The command must stand on its own line. + + When a class is marked \l {reentrant-command} {\\reentrant} or \l + {threadsafe-command} {\\threadsafe}, functions in that class can + be marked \c nonreentrant using this command in the \l{fn-command} + {\\fn} comment of the functions to be excluded. + + See also \l{reentrant-command} {\\reentrant} and + \l{threadsafe-command} {\\threadsafe}. +*/ + +/*! + \page 18-qdoc-commands-relating.html + \previouspage Thread Support + \contentspage Table of Contents + \nextpage Grouping Things + + \title Relating Things + + The relating commands are for specifying how one documented + element relates to another documented element. e.g., This function + is an overload of another function, or this function is a + reimplementation of another function, or this typedef is \e + related to some class or header file. There is also a command + for documenting that a QML element inherits some other QML + element. + + \section1 Commands + + \target inherits-command + \section2 \\inherits + + The \\inherits command is for documenting that one QML element + inherits some other QML element. It must be included in the + inheriting element's \l{qmlclass-command}{\\qmlclass} comment. + The argument is the name of the inherited QML element. + + \code + / *! + \qmlclass PauseAnimation QQuickPauseAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PauseAnimation element provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step + when nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + + SequentialAnimation { + NumberAnimation { ... duration: 200 } + PauseAnimation { duration: 100 } + NumberAnimation { ... duration: 200 } + } + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} + * / + \endcode + + QDoc includes this line on the reference page for the + \l{http://doc.qt.nokia.com/4.7/qml-pauseanimation.html} {PauseAnimation} + element: + + \quotation + Inherits \l{http://doc.qt.nokia.com/4.7/qml-animation.html} {Animation} + \endquotation + + \target overload-command + \section2 \\overload + + The \\overload command is for indicating that a function is a + secondary overload of its name. + + The command must stand on its own line. + + For a function name that is overloaded (except constructors), QDoc + expects one primary version of the function, and all the others + marked with the \bold {\\overload command}. The primary version + should be fully documented. Each overload can have whatever extra + documentation you want to add for just that overloaded version. + + From Qt 4.5, you can include the function name plus '()' as a + parameter to the \bold{\\overload} command, which will include a + standard \e{This function overloads...} line of text with a link + to the documentation for the primary version of the function. + + \code + / *! + \overload addAction() + + This convenience function creates a new action with an + \a icon and some \a text. The function adds the newly + created action to the menu's list of actions, and + returns it. + + \sa QWidget::addAction() + * / + QAction *QMenu::addAction(const QIcon &icon, const QString &text) + { + QAction *ret = new QAction(icon, text, this); + addAction(ret); + return ret; + } + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h3><a href="http://qt.nokia.com/doc/4.0/qaction.html">QAction</a> + * QMenu::addAction ( const QIcon & <i>icon</i>, + const QString & <i>text</i> ) + </h3> + \endraw + + This function overloads \l {http://qt.nokia.com/doc/4.0/qwidget.html#addAction} {addAction()} + + This convenience function creates a new action with an + \e icon and some \e text. The function adds the newly + created action to the menu's list of actions, and + returns it. + + See also + \l {http://qt.nokia.com/doc/4.0/qwidget.html#addAction} + {QWidget::addAction}(). + \endquotation + + If you don't include the function name with the \bold{\\overlaod} + command, then instead of the "This function overloads..." line + with the link to the documentation for the primary version, you + get the old standard line: + + \quotation + This is an overloaded member function, provided for + convenience. + \endquotation. + + \target reimp-command + \section2 \\reimp + + The \\reimp command is for indicating that a function is a + reimplementation of a virtual function. + + The command must stand on its own line. + + QDoc will omit the reimplemented function from the class + reference. + + \code + / *! + \reimp + * / + void QToolButton::nextCheckState() + { + Q_D(QToolButton); + if (!d->defaultAction) + QAbstractButton::nextCheckState(); + else + d->defaultAction->trigger(); + } + \endcode + + This function will not be included in the documentation. Instead, + a link to the base function QAbstractButton::nextCheckState() will + appear in the documentation. + + \target relates-command + \section2 \\relates + + The \\relates command is for including the documentation of a + global element to some class or header file. The argument is a + class name or header file. + + \code + / *! + \relates QChar + + Reads a char from the stream \a in into char \a chr. + + \sa {Format of the QDataStream operators} + * / + QDataStream &operator>>(QDataStream &in, QChar &chr) + { + quint16 u; + in >> u; + chr.unicode() = ushort(u); + return in; + } + \endcode + + The documentation for this function will be included on the reference page + for class QChra. +*/ + +/*! + \page 19-qdoc-commands-grouping.html + \previouspage Relating Things + \contentspage Table of Contents + \nextpage Naming Things + + \title Grouping Things + + The grouping commands relate classes to defined groups and + modules. The groups are used when generating lists of related + classes in the documentation, while the modules are elements of + Qt's structure. + + \section1 Commands + + \target mainclass-command + \section2 \\mainclass + + The \\mainclass command relates the documented class to + a group called mainclasses. + + The command must stand on its own line. + + \code + / *! + \class QWidget qwidget.h + \brief The QWidget class is the base class of + all user interface objects. + + \mainclass + + ... + * / + \endcode + + This will include the QWidget class in the \e mainclasses + group, which means, for example, that the class will appear on the + list created by calling the \l {generatelist-command} + {\\generatelist} command with the \c mainclasses argument: + + \l http://qt.nokia.com/doc/4.0/mainclasses.html + + \note The Qt documentation no longer includes the \e mainclasses + page. + + See also \l {generatelist-command} {\\generatelist}. + + \target ingroup-command + \section2 \\ingroup + + The \\ingroup command indicates that the given + overview or documented class belongs to a certain group of + related docmentation. + + A class or overview may belong to many groups. + + The \\ingroup command's argument is a group name, but note + that the command considers the rest of the line as part of + its argument. Make sure that the group name is followed by + a linebreak. + + \code + / *! + \class QDir + \brief The QDir class provides access to directory + structures and their contents. + + \ingroup io + ... + * / + \endcode + + This will include the QDir class in the \c io group, which means, + for example, that QDir will appear on the list created by calling + the \l {group-command} {\\group} command with the \c io argument. + + To list overviews that are related to a certain group, you must + generate the list explicitly using the \l {generatelist-command} + {\\generatelist} command with the \c related argument. + + See also \l {group-command} {\\group}. + + \target inmodule-command + \section2 \\inmodule + + The \\inmodule command relates a class to the module specified by + the command's argument. + + For the basic classes in Qt, a class's module is determined by its + location, i.e. its directory. However, for extensions, like + ActiveQt and Qt Designer, a class must be related to a module + explicitly. + + The command's argument is a module name, but note that the command + considers the rest of the line as part of its argument. Make sure + that the module name is followed by a linebreak. + + \code + /*! + \class QDesignerTaskMenuExtension + \inmodule QtDesigner + * / + \endcode + + This ensures that the QDesignerTaskMenuExtension class is included + in the \c QtDesigner module, which means, for example, that the + class will appear on the list created by calling the \l + {generatelist-command} {\\generatelist} command with the \c + {{classesbymodule QtDesigner}} argument. + + See also \l {module-command} {\\module} and \l + {generatelist-command} {\\generatelist}. +*/ + +/*! + \page 20-qdoc-commands-namingthings.html + \previouspage Grouping Things + \contentspage Table of Contents + \nextpage Markup Commands + + \title Naming Things + + In general, a title command considers everything that follows it + until the first line break as its argument. If the title is so + long it must span multiple lines, end each line (except the last + one) with a backslash. + + \section1 Commands + + \target title-command + \section2 \\title + + The \\title command sets the title for a documentation page, or + allows you to override it. + + \code + / *! + \page signalandslots.html + + \title Signals & Slots + + Signals and slots are used for communication between + objects. The signals and slots mechanism is a central + feature of Qt and probably the part that differs most + from the features provided by other frameworks. + + ... + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h1><center>Signal and Slots</center></h1> + \endraw + + Signals and slots are used for communication between + objects. The signals and slots mechanism is a central + feature of Qt and probably the part that differs most + from the features provided by other frameworks. + ... + \endquotation + See also \l {subtitle-command} {\\subtitle}. + + \target subtitle-command + \section2 \\subtitle + + The \\subtitle command sets a subtitle for a documentation page. + + \code + / *! + \page qtopiacore-overview.html + + \title Qtopia Core + \subtitle Qt for Embedded Linux + + Qt/Embedded, the embedded Linux port of Qt, is a + complete and self-contained C++ GUI and platform + development tool for Linux-based embedded development. + ... + * / + \endcode + + QDoc renders this as: + + \quotation + \raw HTML + <h1><center>Qtopia Core</center></h1> + <h2><center>Qt for Embedded Linux</center></h2> + \endraw + + Qt/Embedded, the embedded Linux port of Qt, is a + complete and self-contained C++ GUI and platform + development tool for Linux-based embedded development. + ... + \endquotation + + See also \l {title-command} {\\title}. + +*/ + +/*! + \page 21-0-qdoc-configuration.html + \previouspage Miscellaneous + \contentspage Table of Contents + \nextpage Generic Configuration Variables + + \title The QDoc Configuration File + + Before running QDoc, you must create a QDoc configuration file to + tell QDoc where to find the source files that contain the QDoc + comments. The pathname to your configuration file is passed to + QDoc on the command line: + + \quotation + \c {/current/dir$ ../../bin/qdoc3 ./config.qdocconf} + \endquotation + + \section1 General Description + + The configuration file is a list of entries of the form \e + {"variable = value"}. Using the configuration variables, you can + define where QDoc should find the various source files, images and + examples, where to put generated documentation etc. The + configuration file can also contain directives like \c + include. For an example, see the \l minimum.qdocconf file. + + You can also use configuration variables to get QDoc to support + \l{Supporting Derived Projects} {derived projects}, i.e QDoc can + generate links in your project's documentation to elements in the + Qt online documentation. See the \l {Supporting Derived projects} + section. + + The value of a configuration variable can be set using either '=' + or '+='. The difference is that '=' overrides the previous value, + while '+=' adds a new value to the current one. + + Some configuration variables accept a list of strings as their + value, e.g. + \l {22-qdoc-configuration-generalvariables.html#sourcedirs-variable} + {\c{sourcedirs}}, while others accept only a single string. Double + quotes around a value string are optional, but including them allows + you to use special characters like '=' and ' \" ' within the valuem + string, e.g.: + + \code + HTML.postheader = "<a href=\"index.html\">Home</a>" + \endcode + + If an entry spans many lines, use a backslash at the end of every + line but the last: + + \code + sourcedirs = kernel \ + tools \ + widgets + \endcode + + \section1 Configuration Variables + + \section1 Variable List + + \list + \o \l {22-qdoc-configuration-generalvariables.html#alias-variable} {alias} + \o \l {23-qdoc-configuration-cppvariables.html#Cpp.ignoredirectives-variable} {Cpp.ignoredirectives} + \o \l {23-qdoc-configuration-cppvariables.html#Cpp.ignoretokens-variable} {Cpp.ignoretokens} + \o \l {23-qdoc-configuration-cppvariables.html#basedir-variable} {basedir} \span {class="newStuff"} {(experimental)} + \o \l {22-qdoc-configuration-generalvariables.html#defines-variable} {defines} + \o \l {22-qdoc-configuration-generalvariables.html#edition-variable} {edition} + \o \l {22-qdoc-configuration-generalvariables.html#exampledirs-variable} {exampledirs} + \o \l {22-qdoc-configuration-generalvariables.html#examples-variable} {examples} + \o \l {22-qdoc-configuration-generalvariables.html#examples.fileextensions-variable} {examples.fileextensions} + \o \l {22-qdoc-configuration-generalvariables.html#excludedirs-variable} {excludedirs} + \o \l {22-qdoc-configuration-generalvariables.html#excludefiles-variable} {excludefiles} + \o \l {22-qdoc-configuration-generalvariables.html#extraimages-variable} {extraimages} + \o \l {22-qdoc-configuration-generalvariables.html#falsehoods-variable} {falsehoods} + \o \l {22-qdoc-configuration-generalvariables.html#headerdirs-variable} {headerdirs} + \o \l {22-qdoc-configuration-generalvariables.html#headers-variable} {headers} + \o \l {22-qdoc-configuration-generalvariables.html#headers.fileextensions-variable} {headers.fileextensions} + \o \l {24-qdoc-configuration-htmlvariables.html#HTML.footer-variable} {HTML.footer} + \o \l {24-qdoc-configuration-htmlvariables.html#HTML.postheader-variable} {HTML.postheader} + \o \l {24-qdoc-configuration-htmlvariables.html#HTML.style-variable} {HTML.style} + \o \l {22-qdoc-configuration-generalvariables.html#imagedirs-variable} {imagedirs} + \o \l {22-qdoc-configuration-generalvariables.html#images-variable} {images} + \o \l {22-qdoc-configuration-generalvariables.html#images.fileextensions-variable} {images.fileextensions} + \o \l {22-qdoc-configuration-generalvariables.html#language-variable} {language} + \o \l {22-qdoc-configuration-generalvariables.html#macro-variable} {macro} + \o \l {22-qdoc-configuration-generalvariables.html#outputdir-variable} {outputdir} + \o \l {22-qdoc-configuration-generalvariables.html#outputformats-variable} {outputformats} + \o \l {22-qdoc-configuration-generalvariables.html#sourcedirs-variable} {sourcedirs} + \o \l {22-qdoc-configuration-generalvariables.html#sources-variable} {sources} + \o \l {22-qdoc-configuration-generalvariables.html#sources.fileextensions-variable} {sources.fileextensions} + \o \l {22-qdoc-configuration-generalvariables.html#spurious-variable} {spurious} + \o \l {22-qdoc-configuration-generalvariables.html#tabsize-variable} {tabsize} + \o \l {22-qdoc-configuration-generalvariables.html#version-variable} {version} + \o \l {22-qdoc-configuration-generalvariables.html#versionsym-variable} {versionsym} + \endlist + + \section1 Categories + + \list + \o \l {Generic Configuration Variables} + \o \l {C++ Specific Configuration Variables} + \o \l {HTML Specific Configuration Variables} + \endlist + + \section1 Configuration File Examples + + \list + \o A minimum configuration file: \l minimum.qdocconf + \o The Qt configuration file: \l qt.qdocconf + \endlist +*/ + +/*! + \page 21-1-minimum-qdocconf.html + \previouspage qt.qdocconf + \contentspage Table of Contents + \nextpage Generating DITA XML Output + + \title minimum.qdocconf + + \quotefile examples/minimum.qdocconf +*/ + +/*! + \page 21-2-qt-qdocconf.html + \previouspage Compatibility Issues + \contentspage Table of Contents + \nextpage minimum.qdocconf + + \title qt.qdocconf + + \quotefile files/qt.qdocconf +*/ + +/*! + \page 21-3-qt-dita-xml-output.html + \previouspage minimum.qdocconf + \contentspage Table of Contents + \nextpage Table of Contents + + \title Generating DITA XML Output + + QDoc can generate \l {http://dita.xml.org} {DITA XML output}. + + In your confifiguration file, set your \c {outputformats} variable + to \c {DITAXML}, and send the output to an appropriate directory: + + \code + outputdir = $QTDIR/doc/ditaxml + outputformats = DITAXML + \endcode + + And include these macros in your configuration file to prevent + QDoc from doing some escaping that doesn't validate in XML: + + \code + macro.aacute.DITAXML = "á" + macro.Aring.DITAXML = "Å" + macro.aring.DITAXML = "å" + macro.Auml.DITAXML = "Ä" + macro.br.DITAXML = " " + macro.BR.DITAXML = " " + macro.copyright.DITAXML = "©" + macro.eacute.DITAXML = "é" + macro.hr.DITAXML = " " + macro.iacute.DITAXML = "í" + macro.oslash.DITAXML = "ø" + macro.ouml.DITAXML = "ö" + macro.raisedaster.DITAXML = "<sup>*</sup>" + macro.rarrow.DITAXML = "→" + macro.reg.DITAXML = "<sup>®</sup>" + macro.uuml.DITAXML = "ü" + macro.mdash.DITAXML = "—" + macro.emptyspan.DITAXML = " " + \endcode + + You can also set default values for some of the tags in the DITA + \c {<prolog>} and \c {<metadata>} elements: + + \code + dita.metadata.default.author = Qt Development Frameworks + dita.metadata.default.permissions = all + dita.metadata.default.publisher = Nokia + dita.metadata.default.copyryear = 2011 + dita.metadata.default.copyrholder = Nokia + dita.metadata.default.audience = programmer + \endcode + + See the \l {12-0-qdoc-commands-miscellaneous.html#meta-command} + {\\meta} command for more details on DITA metadata. + +*/ + +/*! + \page 22-qdoc-configuration-generalvariables.html + \previouspage The QDoc Configuration File + \contentspage Table of Contents + \nextpage Creating Help Project Files + + \title Generic Configuration Variables + + With the general QDoc configuration variables, you can define + where QDoc will find the various source files it needs to generate + the documentation, as well as the directory to put the generated + documentation. You can also do some minor manipulation of QDoc + itself, controlling its output and processing behavior. + + \target alias-variable + \section1 alias + + The \c alias variable renames a QDoc command. + + The general syntax is \tt {alias.\e{original-command-name} = \e + temporary-command-name}. + + \code + alias.i = e + \endcode + + This renames the built-in command \\i (italics) to \\e. The \c + alias variable is often used for compatibility reasons; for more + information see the \l {Compatibility Issues} {compatibility + section}. + + See also \l {macro-variable} {macro}. + + \target codeindent-variable + \section1 codeindent + + The \c codeindent variable specifies the level of indentation that + QDoc uses when writing code snippets. + + QDoc originally used a hard-coded value of four spaces for code + indentation to ensure that code snippets could be easily + distinguished from surrounding text. Since we can use \l{HTML + Specific Configuration Variables#HTML.stylesheets} {stylesheets} + to adjust the appearance of certain types of HTML elements, this + level of indentation is not always required. + + \target basedir-variable \span {class="newStuff"} {(experimental)} + \section1 basedir + + The \c basedir variable tells QDoc two things. First, the fact that + it is set it tells QDoc to the put the output files in subdirectories + of the output directory. Second, the value of basedir is the name of + the bundle directory for your project. .e.g. if you are working with + the Qt5 bundle, you will have checked out the bundle into some root + subdirectory (the base directory), and that root directory might + very well be \e {qt5} + + Then in your qdocconf file, you would assign to the basedir variable: + + \code + basedir = qt5 + \endcode + + Now, QDoc knows to scan the file path of each source file it parses, + looking for \e qt5. For example, this file would be: + + \code + ~/depot/qt5/qtdoc/tools/qdoc3/doc/qdoc-manual.qdoc + \endcode + + QDoc scans the path for the basedir \e{qt5} and the next subdirectory + \e{qtdoc} becomes one of the subdirectories in the output directory. + The HTML output file created from this file will be stored in the + \e{qtdoc} subdirectory. + + \note This is an experimental command. It is currently used only by + the Qt documentation group. If you use it, be advised that you might + find some broken links in your HTML output due to remaining problems + with cross-subdirectory linking. + + \target defines-variable + \section1 defines + + The \c defines variable specifies the C++ preprocessor symbols + that QDoc will recognize and respond to. + + When a preprocessor symbol is specified using the \c defines + variable, you can also use the \l {if-command} {\\if} command to + enclose documentation that only will be included if the + preprocessor symbol is defined. + + The values of the variable are regular expressions (see QRegExp + for details). By default, no symbol is defined, meaning that code + protected with #ifdef...#endif will be ignored. + + \code + defines = Q_QDOC \ + QT_.*_SUPPORT \ + QT_.*_LIB \ + QT_COMPAT \ + QT3_SUPPORT \ + Q_WS_.* \ + Q_OS_.* \ + Q_BYTE_ORDER \ + __cplusplus + \endcode + + This ensures that QDoc will process the code that requires these + symbols to be defined. For example: + + \code + #ifdef Q_WS_WIN + HDC getDC() const; + void releaseDC(HDC) const; + #endif + \endcode + + Since the Q_WS_.* regular expression (specified using the \c + defines variable) matches Q_WS_WIN, QDoc will process the code + within #ifdef and #endif in our example. + + You can also define preprocessor symbols manually on the command + line using the -D option. For example: + + \code + currentdirectory$ qdoc3 -Dconsoleedition qt.qdocconf + \endcode + + In this case the -D option ensures that the \c consoleedition + preprocessor symbol is defined when QDoc processes the source + files defined in the qt.qdocconf file. + + See also \l {falsehoods-variable} {falsehoods} and \l {if-command} {\\if}. + + \target edition-variable + \section1 edition + + The \c edition variable specifies which modules are included in + each edition of a package, and provides QDoc with information to + provide class lists for each edition. + + This feature is mostly used when providing documentation for Qt + packages. + + The \c edition variable is always used with a particular edition + name to define the modules for that edition: + + \code + edition.Console = QtCore QtNetwork QtSql QtXml + edition.Desktop = QtCore QtGui QtNetwork QtOpenGL QtSql QtXml \ + QtDesigner QtAssistant Qt3Support QAxContainer \ + QAxServer + edition.DesktopLight = QtCore QtGui Qt3SupportLight + \endcode + + In the above examples, the \c Console edition only includes the + contents of four modules. Only the classes from these modules will + be used when the \l{Miscellaneous#generatelist-command} + {generatelist} command is used to generate a list of classes for + this edition: + + \code + \generatelist{classesbyedition Console} + \endcode + + \target exampledirs-variable + \section1 exampledirs + + The \c exampledirs variable specifies the directories containing + the source code of the example files. + + The \l {examples-variable} {examples} {examples} and \l + {exampledirs-variable} {exampledirs} variables are used by the \l + {quotefromfile-command} {\\quotefromfile}, \l {quotefile-command} + {\\quotefile} and \l {example-command} {\\example} commands. If + both the \l {examples-variable} {examples} and \l + {exampledirs-variable} {exampledirs} variables are defined, QDoc + will search in both, first in \l {examples-variable} {examples} + then in \l {exampledirs-variable} {exampledirs}. + + QDoc will search through the directories in the specified order, + and accept the first matching file it finds. It will only search + in the specified directories, \e not in subdirectories. + + \code + exampledirs = $QTDIR/doc/src \ + $QTDIR/examples \ + $QTDIR \ + $QTDIR/qmake/examples + + examples = $QTDIR/examples/widgets/analogclock/analogclock.cpp + \endcode + + When processing + + \code + \quotefromfile widgets/calculator/calculator.cpp + \endcode + + QDoc will then see if there exists a file called \c calculator.cpp + listed as a value in the \l {examples} {\c examples} variable. If + it doesn't, it will search in the \c exampledirs variable, and + first see if there exists a file called + + \code + $QTDIR/doc/src/widgets/calculator/calculator.cpp + \endcode + + If it doesn't, QDoc will continue looking for a file called + + \code + $QTDIR/examples/widgets/calculator/calculator.cpp + \endcode + + and so forth. + + See also \l examples. + + \target examples-variable + \section1 examples + + The \c examples variable allows you to specify individual example + files in addition to those located in the directories specified by + the \l {exampledirs-variable} {\c exampledirs} variable. + + The \c examples and \l {exampledirs-variable} {\c exampledirs} + variables are used by the \l {quotefromfile-command} + {\\quotefromfile}, \l {quotefile-command} {\\quotefile} and \l + {example} {\\example} commands. If both the \c examples and \l + {exampledirs-variable} {\c exampledirs} variables are defined, + QDoc will search in both, first in \c examples then in \l + {exampledirs-variable} {\c exampledirs}. + + QDoc will search through the values listed for the \c examples + variable, in the specified order, and accept the first one it + finds. + + For an extensive example, see the \l {exampledirs-variable} {\c + exampledirs} command. But note that if you know the file is listed + in the \c examples variable, you don't need to specify its path: + + \code + \quotefromfile calculator.cpp + \endcode + + See also \l {exampledirs-variable} {exampledirs}. + + \target examples.fileextensions-variable + \section1 examples.fileextensions + + The \c examples.fileextensions variable specifies the file + extensions that qdoc will look for when collecting example files + for display in the documentation. + + The default extensions are *.cpp, *.h, *.js, *.xq, *.svg, *.xml + and *.ui. + + The extensions are given as standard wildcard expressions. You + can add a file extension to the filter using '+='. For example: + + \code + examples.fileextensions += *.qrc + \endcode + + See also \l{headers.fileextensions}. + + \target excludedirs-variable + \section1 excludedirs + + The \c excludedirs variable is for listing directories that should \e{not} + be processed by qdoc, even if the same directories are included by the + \l {sourcedirs-variable} {sourcedirs} or \l {headerdirs-variable} {headerdirs} + variables. + + For example in \l qt.qdocconf + + \code + excludedirs = $QTDIR/extensions/activeqt \ + $QTDIR/extensions/motif \ + $QTDIR/tools/designer/src/lib/extension \ + $QTDIR/tools/designer/src/lib/sdk \ + $QTDIR/tools/designer/src/lib/uilib + \endcode + + When executed, QDoc will exclude the listed directories from + further consideration. Files in these directories will not be + read by qdoc. + + See also \l {excludefiles-variable} {excludefiles}. + + \target excludefiles-variable + \section1 excludefiles + + The \c excludefiles variable allows you to specify individual files + that should \e{not} be processed by qdoc. + + \code + excludefiles += $QT_CORE_SOURCES/../../src/widgets/kernel/qwidget.h \ + $QT_CORE_SOURCES/../../src/widgets/kernel/qwidget.cpp + \endcode + + If you include the above in your qdocconf file for qtbase, there + will be no qwidget.html generated for html and no qwidget.xml + generated for DITA XML. + + See also \l {excludedirs-variable} {excludedirs}. + + \target extraimages-variable + \section1 extraimages + + The \c extraimages variable tells QDoc to incorporate specific + images in the generated documentation. + + QDoc will not recognize images used within HTML (or any other + markup language). If we want the images to be copied from the + directories specified by \l {imagedirs} {\c imagedirs} (the images + in question must be located in these directories) to the output + directory, we must specify the images using the \c extraimages + variable. + + The general syntax is \tt {extraimages.\e{format} = \e image}. The + file extension is optional. + + For example, in \l qt.qdocconf we use a couple of images within + the HTML.postheader variable which value is pure HTML. For that + reason, these images are specified using the \c extraimages + variable: + + \code + extraimages.HTML = qt-logo + \endcode + + See also \l images and \l imagedirs. + + \target falsehoods-variable + \section1 falsehoods + + The \c falsehoods variable defines the truth value of specified + preprocessor symbols as false. + + If this variable is not set for a preprocessor symbol, QDoc + assumes its truth value is true. The exception is '0', which value + always is false. + + QDoc will recognize, and is able to evaluate, the following + preprocessor syntax: + + \code + #ifdef NOTYET + ... + #endif + + #if defined (NOTYET) + ... + #end if + \endcode + + However, faced with unknown syntax like + + \code + #if NOTYET + ... + #endif + \endcode + + QDoc will evaluate it as true by default, \e unless the + preprocessor symbol is specified within the \c falsehoods variable + entry: + + \code + falsehoods = NOTYET + \endcode + + See also \l defines. + + \target generateindex-variable + \section1 generateindex + + The \c generateindex variable contains a boolean value that + specifies whether to generate an index file when HTML + documentation is generated. + + By default, an index file is always generated with HTML + documentation, so this variable is typically only used when + disabling this feature (by setting the value to \c false) or when + enabling index generation for the WebXML output (by setting the + value to \c true). + + \target headerdirs-variable + \section1 headerdirs + + The \c headerdirs variable specifies the directories containing + the header files associated with the \c .cpp source files used in + the documentation. + + \code + headerdirs = $QTDIR/src \ + $QTDIR/extensions/activeqt \ + $QTDIR/extensions/motif \ + $QTDIR/tools/designer/src/lib/extension \ + $QTDIR/tools/designer/src/lib/sdk \ + $QTDIR/tools/designer/src/lib/uilib + \endcode + + When executed, the first thing QDoc will do is to read through the + headers specified in the \l {headers} {\c headers} variable, and + the ones located in the directories specified in the \c headerdir + variable (including all subdirectories), building an internal + structure of the classes and their functions. + + Then it will read through the sources specified in the \l + {sources-variable} {\c sources}, and the ones located in the + directories specified in the \l {sourcedirs-variable} {\c + sourcedirs} varible (including all subdirectories), merging the + documentation with the structure it retrieved from the header + files. + + If both the \c headers and \c headerdirs variables are defined, + QDoc will read through both, first \l {headers} {\c headers} then + \c headerdirs. + + In the specified directories, QDoc will only read the files with + the fileextensions specified in the \l {headers.fileextensions} + {\c headers.fileextensions} variable. The default extensions are + *.ch, *.h, *.h++, *.hh, *.hpp and *.hxx". The files specified by + \l {headers} {\c headers} will be read independent of their + fileextensions. + + See also \l headers and \l headers.fileextensions. + + \target headers-variable + \section1 headers + + The \c headers variable allows you to specify individual header + files in addition to those located in the directories specified by + the \l {headerdirs} {\c headerdirs} variable. + + \code + headers = $QTDIR/src/gui/widgets/qlineedit.h \ + $QTDIR/src/gui/widgets/qpushbutton.h + \endcode + + When processing the \c headers variable, QDoc behaves in the same + way as it does when processing the \l {headerdirs} {\c headerdirs} + variable. For more information, see the \l {headerdirs} {\c + headerdirs} variable. + + See also \l headerdirs. + + \target headers.fileextensions-variable + \section1 headers.fileextensions + + The \c headers.fileextensions variable specify the extension used + by the headers. + + When processing the header files specified in the \l {headerdirs} + {\c headerdirs} variable, QDoc will only read the files with the + fileextensions specified in the \c headers.fileextensions + variable. In this way QDoc avoid spending time reading irrelevant + files. + + The default extensions are *.ch, *.h, *.h++, *.hh, *.hpp and + *.hxx. + + The extensions are given as standard wildcard expressions. You + can add a file extension to the filter using '+='. For example: + + \code + header.fileextensions += *.H + \endcode + + \warning The above assignment may not work as described. + + See also \l headerdirs. + + \target imagedirs-variable + \section1 imagedirs + + The \c imagedirs variable specifies the directories containing the + images used in the documentation. + + The \l {images} {\c images} and \c imagedirs variables are used by + the \l {image-command} {\\image} and \l {inlineimage-command} + {\\inlineimage} commands. If both the \l {images} {\c images} and + \c imagedirs variables are defined, QDoc will search in both, + first in \l {images} {\c images} then in \c imagedirs. + + QDoc will search through the directories in the specified order, + and accept the first matching file it finds. It will only search + in the specified directories, \e not in subdirectories. + + \code + imagedirs = $QTDIR/doc/src/images \ + $QTDIR/examples + + images = $QTDIR/doc/src/images/calculator-example.png + \endcode + + When processing + + \code + \image calculator-example.png + \endcode + + QDoc will then see if there exists a file called + calculator-example.png listed as a value in the \c images + variable. If it doesn't, it will search in the \c imagedirs + variable, and first see if there exists a file called + + \code + $QTDIR/doc/src/images/calculator-example.png + \endcode + + If it doesn't, QDoc will look for a file called + + \code + $QTDIR/examples/calculator-example.png + \endcode + + You can filter the images in an image directory using the \l + {images.fileextensions} {\c images.fileextensions} variable. The + general idea behind the \l {images.fileextensions} {\c images.fileextensions} + variable is to enable different image format for different output format. + + \warning The \l {images.fileextensions} {\c images.fileextensions} + variable's functionality is preliminay since QDoc at this point + only support HTML. + + See also \l images and \l images.fileextensions. + + \target images-variable + \section1 images + + The \c images variable allows you to specify individual image + files in addition to those located in the directories specified by + the \l {imagedirs} {\c imagedirs} variable. + + \code + images = $QTDIR/doc/src/images/calculator-example.png + \endcode + + When processing the \c images variable, QDoc behaves in the same + way as it does when processing the \l {imagedirs} {\c imagedirs} + variable. For more information, see the \l {imagedirs} {\c + imagedirs} variable. + + See also \l imagedirs and \l images.fileextensions. + + \target images.fileextensions-variable + \section1 images.fileextensions + + The images.fileextensions variable filters the files within an + image directory. + + The variable's values (the extensions) are given as standard + wildcard expressions. The general syntax is: \tt + {images.fileextensions.\e{format} = *.\e{extension}}. + + The idea is to enable different image format for different output + format. + + \code + images.fileextensions.HTML = *.png + images.fileextensions.LOUT = *.eps + \endcode + + Then, when processing the \l {image-command} {\\image} and \l + {inlineimage-command} {\\inlineimage} commands, QDoc will only + search for files with extensions specified in the output format's + associated image extension variable. + + \warning This is preliminary functionality since QDoc at this + point only support HTML. + + The default extensions for HTML are *.png, *.jpg, *.jpeg and + *.gif. + + You can add a file extension to the filter using '+='. For + example: + + \code + images.fileextensions.HTML += *.eps + \endcode + + See also \l imagedirs and \l images. + + \target language-variable + \section1 language + + The \c language variable specifies the language of the source code + that is used in the documentation. + + Currently, C++ is the only language that QDoc understands. It is + also the default language, and doesn't really need to be + specified. But for example in \l qt.qdocconf: + + \code + language = Cpp + \endcode + + identifies the language of the Qt source code as C++. + + \target macro-variable + \section1 macro + + The \c macro variable is used to create your own simple QDoc + commands. The syntax is \tt {macro.\e{command} = \e{definition}}, + where the definition is written using QDoc syntax. + + A macro variable can be restricted for use in one type of output + generation. By appending \c {.HTML} to the macro name, for + example, the macro is only used when generating HTML output. By + appending \c {.DITAXML} to the macro name, the macro is only used + when generating DITA XML. + + \code + macro.gui = "\\bold" + macro.raisedaster.HTML = "<sup>*</sup>" + \endcode + + The first macro defines the \\gui command to render its argument + using a bold font. The second macro defines the \\raisedaster + command to render a superscript asterisk, but only when generating + HTML. + + See also \l {alias-variable} {alias}. + + \target naturallanguage-variable + \section1 naturallanguage + + The \c naturallanguage variable specifies the natural language + used for the documentation generated by qdoc. + + \code + naturallanguage = zh-Hans + \endcode + + By default, the natural language is \c en for compatibility with + legacy documentation. + + qdoc will add the natural language information to the HTML it + generates, using the \c lang and \c xml:lang attributes. + + See also \l {sourceencoding-variable} {sourceencoding}, + \l {outputencoding-variable} {outputencoding}, + \l{http://www.w3.org/TR/xhtml1/#C_7} + {C.7. The lang and xml:lang Attributes} and + \l{http://www.w3.org/TR/i18n-html-tech-lang/#ri20040429.113217290} + {Best Practice 13: Using Hans and Hant codes}. + + \target outputdir-variable + \section1 outputdir + + The \c outputdir variable specifies the directory where QDoc will + put the generated documentation. + + In qt.qdocconf: + + \code + outputdir = $QTDIR/doc/html + \endcode + + locates the generated Qt reference documentation in + $QTDIR/doc/html. For example, the documentation of the QWidget + class is located in + + \code + $QTDIR/doc/html/qwidget.html + \endcode + + The associated images will be put in an \c images subdirectory. + + \warning When running QDoc multiple times using the same output + directory, all files from the previous run will be lost. + + \target outputencoding-variable + \section1 outputencoding + + The \c outputencoding variable specifies the encoding used for the + documentation generated by qdoc. + + \code + outputencoding = UTF-8 + \endcode + + By default, the output encoding is \c ISO-8859-1 (Latin1) for + compatibility with legacy documentation. When generating + documentation for some languages, particularly non-European + languages, this is not sufficient and an encoding such as UTF-8 is + required. + + qdoc will encode HTML using this encoding and generate the correct + declarations to indicate to browsers which encoding is being + used. The \l naturallanguage configuration variable should also be + specified to provide browsers with a complete set of character + encoding and language information. + + See also \l outputencoding and \l naturallanguage. + + \target outputformats-variable + \section1 outputformats + + The \c outputformats variable specifies the format of + the generated documentation. + + Currently, QDoc only supports the HTML format. It is also + the default format, and doesn't need to be specified. + + \target outputprefixes + \section1 outputprefixes + + The \c outputprefixes variable specifies a mapping between types of files + and the prefixes to prepend to the HTML file names in the generated + documentation. + + \code + outputprefixes = QML + outputprefixes.QML = qt-components- + \endcode + + By default, files containing the API documentation for QML elements + or components are prefixed with "qml-". In the above example, the + prefix "qt-components-" is used instead. + + \target qhp-variable + \section1 qhp + + The \c qhp variable is used to define the information to be + written out to Qt Help Project (\c{qhp}) files. + + See the \l{Creating Help Project Files} chapter for information + about this process. + + \target sourcedirs-variable + \section1 sourcedirs + + The \c sourcedirs variable specifies the directories containing + the \c .cpp or \c .qdoc files used in the documentation. + + For example in \l qt.qdocconf + + \code + sourcedirs = $QTDIR/src \ + $QTDIR/doc/src \ + $QTDIR/extensions/activeqt \ + $QTDIR/extensions/motif \ + $QTDIR/tools/designer/src/lib/extension \ + $QTDIR/tools/designer/src/lib/sdk \ + $QTDIR/tools/designer/src/lib/uilib + \endcode + + When executed, the first thing QDoc will do is to read through the + headers specified in the \l {header-command} {\c header} variable, + and the ones located in the directories specified in the \c + headerdir variable (including all subdirectories), building an + internal structure of the classes and their functions. + + Then it will read through the sources specified in the \l + {sources} {\c sources}, and the ones located in the directories + specified in the \l {sourcedirs} {\c sourcedirs} varible + (including all subdirectories), merging the documentation with the + structure it retrieved from the header files. + + If both the \c sources and \c sourcedirs variables are defined, + QDoc will read through both, first \l {sources} {\c sources} then + \c sourcedirs. + + In the specified directories, QDoc will only read the files with + the fileextensions specified in the \l {sources.fileextensions} + {\c sources.fileextensions} variable. The default extensions are + *.c++, *.cc, *.cpp and *.cxx. The files specified by \l {sources} + {\c sources} will be read independent of their fileextensions. + + See also \l {sources-variable} {sources} and + \l {sources.fileextensions-variable} {sources.fileextensions}. + + \target sourceencoding-variable + \section1 sourceencoding + + The \c sourceencoding variable specifies the encoding used for the + source code and documentation. + + \code + sourceencoding = UTF-8 + \endcode + + By default, the source encoding is \c ISO-8859-1 (Latin1) for + compatibility with legacy documentation. For some languages, + particularly non-European languages, this is not sufficient and an + encoding such as UTF-8 is required. + + Although qdoc will use the encoding to read source and + documentation files, limitations of C++ compilers may prevent you + from using non-ASCII characters in source code comments. In cases + like these, it is possible to write API documentation completely + in documentation files. + + See also \l {naturallanguage-variable} {naturallanguage} and + \l {outputencoding-variable} {outputencoding}. + + \target sources-variable + \section1 sources + + The \c sources variable allows you to specify individual source + files in addition to those located in the directories specified by + the \l {sourcedirs-variable} {sourcedirs} variable. + + \code + sources = $QTDIR/src/gui/widgets/qlineedit.cpp \ + $QTDIR/src/gui/widgets/qpushbutton.cpp + \endcode + + When processing the \c sources variable, QDoc behaves in the same + way as it does when processing the \l {sourcedirs-variable} + {sourcedirs} variable. For more information, see the \l + {sourcedirs-variable} {sourcedirs} variable. + + See also \l {sourcedirs-variable} {sourcedirs}. + + \target sources.fileextensions-variable + \section1 sources.fileextensions + + The \c sources.fileextensions variable filters the files within a + source directory. + + When processing the source files specified in the \l {sourcedirs} + {\c sourcedirs} variable, QDoc will only read the files with the + fileextensions specified in the \c sources.fileextensions + variable. In this way QDoc avoid spending time reading irrelevant + files. + + The default extensions are *.c++, *.cc, *.cpp and *.cxx. + + The extensions are given as standard wildcard expressions. You + can add a file extension to the filter using '+='. For example: + + \code + sources.fileextensions += *.CC + \endcode + + \warning The above assignment may not work as described. + + See also \l {sourcedirs-variable} {sourcedirs} and \l + (sources-variable} {sources}. + + + \target spurious-variable + \section1 spurious + + The \c spurious variable excludes specified QDoc warnings from the + output. The warnings are specified using standard wildcard + expressions. + + \code + spurious = "Cannot find .*" \ + "Missing .*" + \endcode + + makes sure that warnings matching either of these expressions, + will not be part of the output when running QDoc. For example + would the following warning be omitted from the output: + + \code + qt-4.0/src/opengl/qgl_mac.cpp:156: Missing parameter name + \endcode + + \target syntaxhighlighting + \section1 syntaxhighlighting + + The \c syntaxhighlighting variable specifies whether QDoc should + perform syntax highlighting on source code quoted in the + documentation it generates. + + \code + syntaxhighlighting = true + \endcode + + will enable syntax highlighting for all supported programming + languages. + + \target tabsize-variable + \section1 tabsize + + The \c tabsize variable defines the size of a tab character. + + \code + tabsize = 4 + \endcode + + will give the tab character the size of 4 spaces. The default + value of the variable is 8, and doesn't need to be specified. + + \target tagfile-variable + \section1 tagfile + + The \c tagfile variable specifies the Doxygen tag file to be + written when HTML is generated. + + \target version-variable + \section1 version + + The \c version variable specifies the version number of the + documented software. + + \code + version = 4.0.1 + \endcode + + When a version number is specified (using the \tt{\l version} or + \tt {\l versionsym} variables in a \c .qdocconf file), it is + accessible through the corresponding \\version command for use in + the documentation. + + \warning The \\version command's functionality is not fully + implemented; currently it only works within raw HTML code. + + See also \l versionsym. + + \target versionsym-variable + \section1 versionsym + + The \c versionsym variable specifies a C++ preprocessor symbol + that defines the version number of the documented software. + + For example in \l qt.qdocconf: + + \code + versionsym = QT_VERSION_STR + \endcode + + QT_VERSION_STR is defined in qglobal.h as follows + + \code + #define QT_VERSION_STR "4.0.1" + \endcode + + When a version number is specified (using the \tt{\l version} or + \tt {\l versionsym} variables in a \c .qdocconf file), it is + accessible through the corresponding \\version command for use in + the documentation. + + \warning The \\version command's functionality is not fully + implemented; currently it only works within raw HTML code. + + See also \l {version} {\\version}. +*/ + +/*! + \page 22-creating-help-project-files.html + \previouspage Generic Configuration Variables + \contentspage Table of Contents + \nextpage C++ Specific Configuration Variables + + \title Creating Help Project Files + + \section1 Overview + + Starting with Qt 4.4, Qt Assistant uses a different system for managing + Qt documentation that requires QDoc to generate inventories of files in a + format that is similar to the old style DCF format, but with additional + features. + + Instead of hard-coding information about the documentation sets for Qt, + QDoc allows configuration variables to be used to specify which pages are + to be used in each documentation set it generates. These are specified as + subvariables of the \c qch variable with each set declared using a unique + identifier as a subvariable. + + For example, the configuration file for the Qt documentation defines a + \c Qt documentation set by specifying information about the set as + subvariables with the \c{qhp.Qt} prefix: + + \code + qhp.Qt.file = qt.qhp + qhp.Qt.namespace = com.trolltech.qt.440 + qhp.Qt.virtualFolder = qdoc + qhp.Qt.indexTitle = Qt Reference Documentation + qhp.Qt.indexRoot = + qhp.Qt.extraFiles = classic.css images/qt-logo.png + qhp.Qt.filterAttributes = qt 4.4.0 qtrefdoc + qhp.Qt.customFilters.Qt.name = Qt 4.4.0 + qhp.Qt.customFilters.Qt.filterAttributes = qt 4.4.0 + qhp.Qt.subprojects = classes overviews examples + qhp.Qt.subprojects.classes.title = Classes + qhp.Qt.subprojects.classes.indexTitle = Qt's Classes + qhp.Qt.subprojects.classes.selectors = class + qhp.Qt.subprojects.overviews.title = Overviews + qhp.Qt.subprojects.overviews.indexTitle = All Overviews and HOWTOs + qhp.Qt.subprojects.overviews.selectors = fake:page,group,module + qhp.Qt.subprojects.examples.title = Tutorials and Examples + qhp.Qt.subprojects.examples.indexTitle = Qt Examples + qhp.Qt.subprojects.examples.selectors = fake:example + \endcode + + To create a table of contents for a manual, create a subproject with + a \c{type} property and set it to \c{manual}. The page in the documentation + referred to by the \c{indexTitle} property must contain a list of links + that acts as a table of contents for the whole manual. QDoc will take the + information in this list and create a table of contents for the subproject. + + For example, the configuration file for Qt Creator defines only one + subproject for its documentation, including all the documentation in a + single manual: + + \code + qhp.QtCreator.subprojects = manual + qhp.QtCreator.subprojects.manual.title = Qt Creator Manual + qhp.QtCreator.subprojects.manual.indexTitle = Qt Creator Manual + qhp.QtCreator.subprojects.manual.type = manual + \endcode + + In this example, the page entitled "Qt Creator Manual" contains a nested + list of links to pages in the documentation which is duplicated in + Qt Assistant's Contents tab. +*/ + +/*! + \page 23-qdoc-configuration-cppvariables.html + \previouspage Creating Help Project Files + \contentspage Table of Contents + \nextpage HTML Specific Configuration Variables + + \title C++ Specific Configuration Variables + + The C++ specific configuration variables are provided to avoid + erroneous documentation due to non-standard C++ constructs. + + \target Cpp.ignoredirectives-variable + \section1 Cpp.ignoredirectives + + The \c Cpp.ignoredirectives variable makes QDoc ignore the + specified non-standard constructs, within C++ source code. + + If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l + Cpp.ignoredirectives} variables, non-standard constructs + (typically macros) can result in erroneous documentation. + + In \l qt.qdocconf: + + \code + Cpp.ignoredirectives = Q_DECLARE_INTERFACE \ + Q_DECLARE_OPERATORS_FOR_FLAGS \ + Q_DECLARE_PRIVATE \ + Q_DECLARE_PUBLIC \ + Q_DISABLE_COPY \ + Q_DUMMY_COMPARISON_OPERATOR \ + Q_ENUMS \ + Q_FLAGS \ + Q_INTERFACES \ + __attribute__ + \endcode + + makes sure that when processing the code below, for example, QDoc + will simply ignore the 'Q_ENUMS' and 'Q_FLAGS' expressions: + + \code + class Q_CORE_EXPORT Qt { + Q_OBJECT + Q_ENUMS(Orientation TextFormat BackgroundMode + DateFormat ScrollBarPolicy FocusPolicy + ContextMenuPolicy CaseSensitivity + LayoutDirection ArrowType) + Q_ENUMS(ToolButtonStyle) + Q_FLAGS(Alignment) + Q_FLAGS(Orientations) + Q_FLAGS(DockWidgetAreas) + + public: + ... + }; + \endcode + + The Q_OBJECT macro, however, is an exception: QDoc recognizes this + particular non-standard construct, so there is no need specifying + it using the \tt {\l Cpp.ignoredirectives} variable. + + Regarding the Q_CORE_EXPORT macro; see the documentation of the + \tt {\l Cpp.ignoretokens} variable. + + See also \l Cpp.ignoretokens. + + \target Cpp.ignoretokens-variable + \section1 Cpp.ignoretokens + + The \c Cpp.ignoretokens variable makes QDoc ignore the specified + non-standard constructs, within C++ source code. + + If not specified by the \tt {\l Cpp.ignoretokens} or \tt {\l + Cpp.ignoredirectives} variables, non-standard constructs + (typically macros) can result in erroneous documentation. + + In \l qt.qdocconf: + + \code + Cpp.ignoretokens = QAXFACTORY_EXPORT \ + QM_EXPORT_CANVAS \ + ... + Q_COMPAT_EXPORT \ + Q_CORE_EXPORT \ + Q_EXPLICIT \ + Q_EXPORT \ + ... + Q_XML_EXPORT + \endcode + + makes sure that when processing the code below, for example, QDoc + will simply ignore the 'Q_CORE_EXPORT' expression: + + \code + class Q_CORE_EXPORT Qt { + Q_OBJECT + Q_ENUMS(Orientation TextFormat BackgroundMode + DateFormat ScrollBarPolicy FocusPolicy + ContextMenuPolicy CaseSensitivity + LayoutDirection ArrowType) + Q_ENUMS(ToolButtonStyle) + Q_FLAGS(Alignment) + Q_FLAGS(Orientations) + Q_FLAGS(DockWidgetAreas) + public: + ... + }; + \endcode + + Regarding the Q_OBJECT, Q_ENUMS and Q_FLAGS macros; see the + documentation of the \tt {\l Cpp.ignoredirectives} variable. + + See also \l Cpp.ignoredirectives. +*/ + +/*! + \page 24-qdoc-configuration-htmlvariables.html + \previouspage C++ Specific Configuration Variables + \contentspage Table of Contents + \nextpage Supporting Derived Projects + + \title HTML Specific Configuration Variables + + The HTML specific configuration variables define the generated + documentation's style, or define the contents of the + documentation's footer or postheader. The format of the variable + values are raw HTML. + + \target HTML.footer-variable + \section1 HTML.footer + + The \c HTML.footer variable defines the content of the generated + HTML documentation's footer. + + The footer is rendered at the bottom of the generated + documentation page. + + The variable's value is given as raw HTML code enclosed by + quotation marks. Note that if the value spans several lines, each + line needs to be enclosed by quotation marks. + + For example in \l qt.qdocconf: + + \code + HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + ... + "</tr></table></div></address>" + \endcode + + The complete variable entry in \l qt.qdocconf provides the + standard footer of the \l {http://qt.nokia.com/doc/4.0/index.html} + {Qt Reference Documentation}. + + \target HTML.postheader-variable + \section1 HTML.postheader + + The \c HTML.postheader variable defines the content of the + generated HTML documentation's postheader. + + The header is rendered at the top of the generated documentation + page. + + The variable's value is given as raw HTML enclosed by quotation + marks. Note that if the value spans several lines, each line needs + to be enclosed by quotation marks. + + For example in \l qt.qdocconf: + + \code + HTML.postheader = "<table border=\"0\"..." \ + ... + "<img src=\"images/qt-logo.png\" \ + "align=\"right\" width=\"203\" height=\"32\""\ + "border=\"0\" />" \ + "</td></tr>" \ + "</table>" + \endcode + + The complete variable entry in \l qt.qdocconf provides the + standard header of the \l {http://doc.qt.nokia.com/} + {Qt Reference Documentation}. + + \target HTML.style-variable + \section1 HTML.style + + The HTML.style variable defines the style for + the generated HTML documentation. + + The variable's value is given as raw HTML enclosed by quotation + marks. Note that if the value spans several lines, each line needs + to be enclosed by quotation marks. + + For example in \l qt.qdocconf: + + \code + HTML.style = "h3.fn,span.fn" \ + "{ margin-left: 1cm; text-indent: -1cm; }\n" \ + "a:link { color: #004faf; text-decoration: none }\n" \ + "a:visited" \ + "{ color: #672967; text-decoration: none }\n" \ + "td.postheader { font-family: sans-serif }\n" \ + "tr.address { font-family: sans-serif }\n" \ + "body { background: #ffffff; color: black; }" + \endcode + + provides the HTML style for the \l + {http://qt.nokia.com/doc/4.0/index.html} {Qt Reference + Documentation}. + + \target HTML.stylesheets-variable + \section1 HTML.stylesheets + + The HTML.stylesheets variable defines a list of stylesheets + to use for the generated HTML documentation. + + Using separate stylesheets for the documentation makes it easier + to customize and experiment with the style used once the contents + has been generated. Typically, it is only necessary to define a + single stylesheet for any set of documentation; for example: + + \code + HTML.stylesheets = classic.css + \endcode + + QDoc expects to find stylesheets in the directory containing the + \l qt.qdocconf file, and it will copy those specified to the output + directory alongside the HTML pages. + +*/ + +/*! + \page 25-qdoc-configuration-derivedprojects.html + \previouspage HTML Specific Configuration Variables + \contentspage Table of Contents + \nextpage Compatibility Issues + + \title Supporting Derived Projects + + Some configuration variables allow you to use QDoc to support + Qt-based projects; i.e allow your project to contain links to the + online Qt documentation. This means that QDoc will be able to + create links to the class reference documentation, without any + explicit linking command. + + \target description-variable + \section1 description + + The description variable holds a short description of the + associated project. + + See also \l project. + + \target indexes-variable + \section1 indexes + + The \c indexes variable lists the index files that will be used to + generate references. + + For example. to make a derived Qt project contain links to the Qt + Reference documentation, you need to specify the associated index + file: + + \code + indexes = $QTDIR/doc/html/qt.index + \endcode + + See also \l project and \l url. + + \target project-variable + \section1 project + + The \c project variable provides a name for the project associated + with the \c .qdocconf file. + + The project's name is used to form a file name for the associated + project's \e index file. + + \code + project = QtCreator + \endcode + + This will cause an index file called \c qtcreator.index to be + created. + + See also \l description and \l indexes. + + \target url-variable + \section1 url + + The \c url variable holds the base URL for the reference + documentation associated with the current project. + + The URL is stored in the generated index file for the + project. When we use the index on its own, QDoc will use this as + the base URL when constructing links to classes, functions, and + other things listed in the index. + + \code + project = Qt + description = Qt Reference Documentation + url = http://doc.qt.nokia.com/4.7 + + ... + \endcode + + This makes sure that whenever \c qt.index is used to generate + references to for example Qt classes, the base URL is \c + http://doc.qt.nokia.com/4.7. + + See also \l indexes. + + \target howto + \section1 How to Support Derived Projects + + This feature makes use of the comprehensive indexes generated by + QDoc when it creates the Qt reference documentation. + + For example, \l qt.qdocconf (the configuration file for Qt) + contains the following variable definitions: + + \code + project = Qt + description = Qt Reference Documentation + url = http://doc.qt.nokia.com/4.7 + + ... + \endcode + + The \l project variable name is used to form a file name for the + index file; in this case the \c qt.index file is created. The \l + url is stored in the index file. Later, when we use the index on + its own, QDoc will use this as the base URL when constructing + links to classes, functions, and other things listed in the index. + + In a mini-project, you can use an index file by defining an \l + indexes configuration variable in your \c .qdocconf file. + + For example, you can create a \c qtcreator.qdocconf file to help you + check the Qt Creator documentation: + + \code + include($QTDIR/tools/qdoc3/test/compat.qdocconf) + + project = QtCreator + description = Qt Creator Class Documentation + url = http://doc.qt.nokia.com/qtcreator-2.2 + + indexes = $QTDIR/doc/html/qt.index + + outputdir = html + + headerdirs = src + sourcedirs = src \ + examples + sources.fileextensions = "*.cpp *.qdoc *.doc" + + exampledirs = examples + \endcode + + The code above requires that you run QDoc from the directory that + contains this file. You need to include the compat.qdocconf + file for compatibility reasons; this is further explained in the + \l {Compatibility Issues} section. + + \bold {To resolve the actual links to Qt classes, the + mini-project's \c .qdocconf file needs to assign a value to the \l + indexes variable; \c $QTDIR/doc/html/qt.index makes sure that you + always use the updated index file for the Qt documentation.} + + The only disadvantages with this approach are the extra file that + QDoc has to generate and the time it takes to do so. Reading the + index back again later isn't instantaneous either, but it's + quicker than processing all the Qt classes each time you need to + write a new document. +*/ + +/*! + \page 26-qdoc-commands-compatibility.html + \previouspage Supporting Derived Projects + \contentspage Table of Contents + \nextpage qt.qdocconf + + \title Compatibility Issues + + \section1 General Description + + \target reason + + Because QDoc evolves to suit our documentation needs, there can be + some compatibility issues when converting to a new version. + + To allow you to proceed at your own speed when converting your + qdoc comments to use new qdoc commands and formats, the ability to + include a configuration file called \c {compat.qdocconf} is + provided. + + A \c {compat.qdocconf} file is a separate configuration file, + which you include in your main configuration file. It typically + contains the mappings from old qdoc commands to new ones using + \l {alias} and + \l {22-qdoc-configuration-generalvariables.html#macro-variable} + {macro} configuration variables. + + \section1 Qt Compatibility + + In Qt's documentation there still exist occurrences of old + commands, and the Qt \l {qt.qdocconf} {configuration file} needs to + include the compat.qdocconf file tailored for Qt. For more + detailed information about the commands creating compatibility + issues, see the \l {Command Comments} {command comments}. + + \section1 Qt's current compat.qdocconf file + + \quotefile files/compat.qdocconf + + \section1 Command Comments + + \table + \header + \o New Command + \o Old Command + \o Description + + \row + \o \\i \target i-versus-e + \o \\e + \o Earlier we + used the \\i command to indicate a list or table item, and + the \\e command for rendering in italic. Now we want the + \\i command to render in italic discarding the + \\e command name. + + \bold {We still need to use the \\e command to render in + italic in new documentation for \l {reason} {compatibility + reasons}}. + + \row + \o \\include \target include-versus-input + \o \\input + \o The \\include command was previously used to quote the + complete contents of a source file, now we want to use the + command to include separate documentation. + That is the functionality of the old \\input command + which name we want to discard. + + \bold {We still need to use the \\input command to include + plain text in new documentation for \l + {reason} {compatibility reasons}}. + + \row + \o \\quotefile \target quotefile-versus-include + \o \\include + \o Earlier, we have used the \\quotefile command to + quote from file, i.e. quote parts from file, and the + \\include command to quote the entire file. Since we now want + \\include to include separate documentation, we change the use of + \\quotefile to quote a complete source file. + + \bold {We still need to use the \\include command to quote + the entire contents of a source file in new documentation + for \l {reason} {compatibility reasons}}. + + \row + \o \\quotefromfile \target quotefromfile-versus-quotefile + \o \\quotefile + \o Earlier, we have used the \\quotefile command to + quote from file, i.e. quote parts from file. Since we now want + that command to quote an entire file, we introduce the new + \\quotefromfile command to quote from file. + + \bold {Use \l {quotefromfile-command} {\\quotefromfile} to quote + parts from a source file in new documentation}. + + \row + \o \\o \target o-versus-i + \o \\i + \o Earlier we used the \\i command to indicate list items + and table items. Since we now want the \\i command to render + in italic instead, we introduce the new \\o command for + this purpose. + + \bold {Use \l {o-command} {\\o} to indicate list and table items in + new documentation}. + + \row + \o \\quotation \target quotation-versus-quote + \o \\quote + \o These commands are equivalent, and represent a simple name + change. + + \bold {Use \l {quotation} {\\quotation} in new + documentation}. + + \row + \o \\image \target image-versus-img + \o \\img + \o These commands are equivalent, and represent a simple name + change. + + \bold {Use \l {image-command} {\\image} in new documentation}. + + \endtable +*/ + +/*! + \page 27-qdoc-commmands-alphabetical.html + \previouspage Introduction to QDoc + \contentspage Table of Contents + \nextpage Topic Commands + + \title Command Index + + This is a complete, alphabetized list of the QDoc commands. + + \list + + \o \l {04-qdoc-commands-textmarkup.html#a-command} {\\a} + \o \l {11-qdoc-commands-specialcontent.html#abstract-command} {\\abstract} + \o \l {12-0-qdoc-commands-miscellaneous.html#annotatedlist-command} {\\annotatedlist} \span {class="newStuff"} {(new 03/11/11)} + \o \l {23-qdoc-configuration-cppvariables.html#basedir-variable} {basedir} \span {class="newStuff"} {(experimental)} + \o \l {06-qdoc-commands-includecodeinline.html#badcode-command} {\\badcode} + \o \l {04-qdoc-commands-textmarkup.html#bold-command} {\\bold} + \o \l {11-qdoc-commands-specialcontent.html#brief-command} {\\brief} + \o \l {04-qdoc-commands-textmarkup.html#c-command} {\\c} + \o \l {09-qdoc-commands-includingimages.html#caption-command} {\\caption} + \o \l {05-qdoc-commands-documentstructure.html#chapter-command} {\\chapter} + \o \l {13-qdoc-commands-topics.html#class-command} {\\class} + \o \l {06-qdoc-commands-includecodeinline.html#code-command} {\\code} + \o \l {07-0-qdoc-commands-includingexternalcode.html#codeline-command} {\\codeline}, + \o \l {16-qdoc-commands-status.html#compat-command} {\\compat} + \o \l {15-qdoc-commands-navigation.html#contentspage-command} {\\contentspage} + \o \l {16-qdoc-commands-status.html#default-command} {\\default} + \o \l {04-qdoc-commands-textmarkup.html#div-command} {\\div} + \o \l {07-0-qdoc-commands-includingexternalcode.html#dots-command} {\\dots} + \o \l {12-0-qdoc-commands-miscellaneous.html#else-command} {\\else} + \o \l {12-0-qdoc-commands-miscellaneous.html#endif-command} {\\endif} + \o \l {13-qdoc-commands-topics.html#enum-command} {\\enum} + \o \l {13-qdoc-commands-topics.html#example-command} {\\example} + \o \l {12-0-qdoc-commands-miscellaneous.html#expire-command} {\\expire} + \o \l {13-qdoc-commands-topics.html#externalpage-command} {\\externalpage} + \o \l {13-qdoc-commands-topics.html#fn-command} {\\fn} + \o \l {11-qdoc-commands-specialcontent.html#footnote-command} {\\footnote} + \o \l {12-0-qdoc-commands-miscellaneous.html#generatelist-command} {\\generatelist} + \o \l {13-qdoc-commands-topics.html#group-command} {\\group} + \o \l {10-qdoc-commands-tablesandlists.html#header-command} {\\header} + \o \l {13-qdoc-commands-topics.html#headerfile-command} {\\headerfile} + \o \l {04-qdoc-commands-textmarkup.html#i-command} {\\i} + \o \l {12-0-qdoc-commands-miscellaneous.html#if-command} {\\if} + \o \l {09-qdoc-commands-includingimages.html#image-command} {\\image} + \o \l {12-0-qdoc-commands-miscellaneous.html#include-command} {\\include} + \o \l {15-qdoc-commands-navigation.html#indexpage-command} {\\indexpage} + \o \l {19-qdoc-commands-grouping.html#ingroup-command} {\\ingroup} + \o \l {18-qdoc-commands-relating.html#inherits-command}{\\inherits} + \o \l {19-qdoc-commands-grouping.html#inmodule-command} {\\inmodule} + \o \l {09-qdoc-commands-includingimages.html#inlineimage-command} {\\inlineimage} + \o \l {16-qdoc-commands-status.html#internal-command} {\\internal} + \o \l {08-qdoc-commands-creatinglinks.html#keyword-command} {\\keyword} + \o \l {08-qdoc-commands-creatinglinks.html#l-command} {\\l} + \o \l {11-qdoc-commands-specialcontent.html#legalese-command} {\\legalese} + \o \l {10-qdoc-commands-tablesandlists.html#list-command} {\\list} + \o \l {13-qdoc-commands-topics.html#macro-command} {\\macro} + \o \l {19-qdoc-commands-grouping.html#mainclass-command} {\\mainclass} + \o \l {12-0-qdoc-commands-miscellaneous.html#meta-command} {\\meta} + \o \l {13-qdoc-commands-topics.html#module-command} {\\module} + \o \l {13-qdoc-commands-topics.html#namespace-command} {\\namespace} + \o \l {15-qdoc-commands-navigation.html#nextpage-command} {\\nextpage} + \o \l {06-qdoc-commands-includecodeinline.html#newcode-command} {\\newcode} + \o \l {17-qdoc-commands-thread.html#nonreentrant-command} {\\nonreentrant} + \o \l {10-qdoc-commands-tablesandlists.html#o-command} {\\o} + \o \l {16-qdoc-commands-status.html#obsolete-command} {\\obsolete} + \o \l {06-qdoc-commands-includecodeinline.html#oldcode-command} {\\oldcode} + \o \l {12-0-qdoc-commands-miscellaneous.html#omit-command} {\\omit} + \o \l {10-qdoc-commands-tablesandlists.html#omitvalue-command} {\\omitvalue} + \o \l {18-qdoc-commands-relating.html#overload-command} {\\overload} + \o \l {13-qdoc-commands-topics.html#page-command} {\\page} + \o \l {05-qdoc-commands-documentstructure.html#part-command} {\\part} + \o \l {16-qdoc-commands-status.html#preliminary-command} {\\preliminary} + \o \l {15-qdoc-commands-navigation.html#previouspage-command} {\\previouspage} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printline-command} {\\printline} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printto-command} {\\printto} + \o \l {07-0-qdoc-commands-includingexternalcode.html#printuntil-command} {\\printuntil} + \o \l {13-qdoc-commands-topics.html#property-command} {\\property} + \o \l {13-qdoc-commands-topics.html#qmlattachedproperty-command} {\\qmlattachedproperty} + \o \l {13-qdoc-commands-topics.html#qmlattachedsignal-command} {\\qmlattachedsignal} + \o \l {13-qdoc-commands-topics.html#qmlbasictype-command} {\\qmlbasictype} + \o \l {13-qdoc-commands-topics.html#qmlclass-command} {\\qmlclass} + \o \l {13-qdoc-commands-topics.html#qmlmethod-command} {\\qmlmethod} + \o \l {13-qdoc-commands-topics.html#qmlproperty-command} {\\qmlproperty} + \o \l {13-qdoc-commands-topics.html#qmlsignal-command} {\\qmlsignal} + \o \l {13-qdoc-commands-topics.html#qmlmodule-command} {\\qmlmodule} + \o \l {13-qdoc-commands-topics.html#inqmlmodule-command} {\\inqmlmodule} + \o \l {11-qdoc-commands-specialcontent.html#quotation-command} {\\quotation} + \o \l {07-0-qdoc-commands-includingexternalcode.html#quotefile-command} {\\quotefile} + \o \l {07-0-qdoc-commands-includingexternalcode.html#quotefromfile-command} {\\quotefromfile} + \o \l {12-0-qdoc-commands-miscellaneous.html#raw-command} {\\raw} \span {class="newStuff"} {(avoid)} + \o \l {17-qdoc-commands-thread.html#reentrant-command} {\\reentrant} + \o \l {18-qdoc-commands-relating.html#reimp-command} {\\reimp} + \o \l {18-qdoc-commands-relating.html#relates-command} {\\relates} + \o \l {10-qdoc-commands-tablesandlists.html#row-command} {\\row} + \o \l {08-qdoc-commands-creatinglinks.html#sa-command} {\\sa} + \o \l {05-qdoc-commands-documentstructure.html#sectionOne-command} {\\section1} + \o \l {05-qdoc-commands-documentstructure.html#sectionTwo-command} {\\section2} + \o \l {05-qdoc-commands-documentstructure.html#sectionThree-command} {\\section3} + \o \l {05-qdoc-commands-documentstructure.html#sectionFour-command} {\\section4} + \o \l {13-qdoc-commands-topics.html#service-command} {\\service} + \o \l {16-qdoc-commands-status.html#since-command} {\\since} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipline-command} {\\skipline} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipto-command} {\\skipto} + \o \l {07-0-qdoc-commands-includingexternalcode.html#skipuntil-command} {\\skipuntil} + \o \l {07-0-qdoc-commands-includingexternalcode.html#snippet-command} {\\snippet}, + \o \l {04-qdoc-commands-textmarkup.html#span-command} {\\span} + \o \l {15-qdoc-commands-navigation.html#startpage-command} {\\startpage} + \o \l {04-qdoc-commands-textmarkup.html#sub-command} {\\sub} + \o \l {20-qdoc-commands-namingthings.html#subtitle-command} {\\subtitle} + \o \l {04-qdoc-commands-textmarkup.html#sup-command} {\\sup} + \o \l {10-qdoc-commands-tablesandlists.html#table-command} {\\table} + \o \l {11-qdoc-commands-specialcontent.html#tableofcontents-command} {\\tableofcontents} + \o \l {08-qdoc-commands-creatinglinks.html#target-command} {\\target} + \o \l {17-qdoc-commands-thread.html#threadsafe-command} {\\threadsafe} + \o \l {20-qdoc-commands-namingthings.html#title-command} {\\title} + \o \l {04-qdoc-commands-textmarkup.html#tt-command} {\\tt} + \o \l {13-qdoc-commands-topics.html#typedef-command} {\\typedef} + \o \l {04-qdoc-commands-textmarkup.html#underline-command} {\\underline} + \o \l {13-qdoc-commands-topics.html#variable-command} {\\variable} + \o \l {10-qdoc-commands-tablesandlists.html#value-command} {\\value} + \o \l {11-qdoc-commands-specialcontent.html#warning-command} {\\warning} + \endlist +*/ + +/*! + \externalpage http://qt.nokia.com/about + \title About Qt +*/ diff --git a/src/tools/qdoc/editdistance.cpp b/src/tools/qdoc/editdistance.cpp new file mode 100644 index 0000000000..8e14deddc1 --- /dev/null +++ b/src/tools/qdoc/editdistance.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + editdistance.cpp +*/ + +#include "editdistance.h" + +QT_BEGIN_NAMESPACE + +int editDistance( const QString& s, const QString& t ) +{ +#define D( i, j ) d[(i) * n + (j)] + int i; + int j; + int m = s.length() + 1; + int n = t.length() + 1; + int *d = new int[m * n]; + int result; + + for ( i = 0; i < m; i++ ) + D( i, 0 ) = i; + for ( j = 0; j < n; j++ ) + D( 0, j ) = j; + for ( i = 1; i < m; i++ ) { + for ( j = 1; j < n; j++ ) { + if ( s[i - 1] == t[j - 1] ) { + D( i, j ) = D( i - 1, j - 1 ); + } else { + int x = D( i - 1, j ); + int y = D( i - 1, j - 1 ); + int z = D( i, j - 1 ); + D( i, j ) = 1 + qMin( qMin(x, y), z ); + } + } + } + result = D( m - 1, n - 1 ); + delete[] d; + return result; +#undef D +} + +QString nearestName( const QString& actual, const QSet<QString>& candidates ) +{ + if (actual.isEmpty()) + return ""; + + int deltaBest = 10000; + int numBest = 0; + QString best; + + QSet<QString>::ConstIterator c = candidates.begin(); + while ( c != candidates.end() ) { + if ( (*c)[0] == actual[0] ) { + int delta = editDistance( actual, *c ); + if ( delta < deltaBest ) { + deltaBest = delta; + numBest = 1; + best = *c; + } else if ( delta == deltaBest ) { + numBest++; + } + } + ++c; + } + + if ( numBest == 1 && deltaBest <= 2 && + actual.length() + best.length() >= 5 ) { + return best; + } else { + return ""; + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/editdistance.h b/src/tools/qdoc/editdistance.h new file mode 100644 index 0000000000..d8e8fe29aa --- /dev/null +++ b/src/tools/qdoc/editdistance.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + editdistance.h +*/ + +#ifndef EDITDISTANCE_H +#define EDITDISTANCE_H + +#include <QSet> +#include <QString> + +QT_BEGIN_NAMESPACE + +int editDistance( const QString& s, const QString& t ); +QString nearestName( const QString& actual, const QSet<QString>& candidates ); + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp new file mode 100644 index 0000000000..c990413234 --- /dev/null +++ b/src/tools/qdoc/generator.cpp @@ -0,0 +1,1495 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + generator.cpp +*/ +#include <qdir.h> +#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES +#include <qdebug.h> +#endif +#include <qdebug.h> +#include "codemarker.h" +#include "config.h" +#include "doc.h" +#include "editdistance.h" +#include "generator.h" +#include "node.h" +#include "openedlist.h" +#include "quoter.h" +#include "separator.h" +#include "tokenizer.h" +#include "ditaxmlgenerator.h" + +QT_BEGIN_NAMESPACE + +QList<Generator *> Generator::generators; +QMap<QString, QMap<QString, QString> > Generator::fmtLeftMaps; +QMap<QString, QMap<QString, QString> > Generator::fmtRightMaps; +QMap<QString, QStringList> Generator::imgFileExts; +QSet<QString> Generator::outputFormats; +QStringList Generator::imageFiles; +QStringList Generator::imageDirs; +QStringList Generator::exampleDirs; +QStringList Generator::exampleImgExts; +QStringList Generator::scriptFiles; +QStringList Generator::scriptDirs; +QStringList Generator::styleFiles; +QStringList Generator::styleDirs; +QString Generator::outDir_; +QString Generator::baseDir_; +QString Generator::project; +QHash<QString, QString> Generator::outputPrefixes; + +QString Generator::sinceTitles[] = +{ + " New Namespaces", + " New Classes", + " New Member Functions", + " New Functions in Namespaces", + " New Global Functions", + " New Macros", + " New Enum Types", + " New Typedefs", + " New Properties", + " New Variables", + " New QML Elements", + " New QML Properties", + " New QML Signals", + " New QML Signal Handlers", + " New QML Methods", + "" +}; + +static void singularPlural(Text& text, const NodeList& nodes) +{ + if (nodes.count() == 1) + text << " is"; + else + text << " are"; +} + +Generator::Generator() + : amp("&"), + lt("<"), + gt(">"), + quot("""), + tag("</?@[^>]*>") +{ + generators.prepend(this); +} + +Generator::~Generator() +{ + generators.removeAll(this); +} + +void Generator::initializeGenerator(const Config & /* config */) +{ +} + +void Generator::terminateGenerator() +{ +} + +void Generator::initialize(const Config &config) +{ + outputFormats = config.getOutputFormats(); + if (!outputFormats.isEmpty()) { + outDir_ = config.getOutputDir(); + baseDir_ = config.getString(CONFIG_BASEDIR); + if (!baseDir_.isEmpty()) + config.location().warning(tr("\"basedir\" specified in config file. " + "All output will be in module directories of the output directory")); + if (outDir_.isEmpty()) + config.lastLocation().fatal(tr("No output directory specified in configuration file or on the command line")); + + QDir dirInfo; + if (dirInfo.exists(outDir_)) { + if (!Config::removeDirContents(outDir_)) + config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); + } + else { + if (!dirInfo.mkpath(outDir_)) + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_)); + } + + if (!dirInfo.mkdir(outDir_ + "/images")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/images")); + if (!dirInfo.mkdir(outDir_ + "/images/used-in-examples")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/images/used-in-examples")); + if (!dirInfo.mkdir(outDir_ + "/scripts")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/scripts")); + if (!dirInfo.mkdir(outDir_ + "/style")) + config.lastLocation().fatal(tr("Cannot create output directory '%1'") + .arg(outDir_ + "/style")); + } + + imageFiles = config.getCleanPathList(CONFIG_IMAGES); + imageDirs = config.getCleanPathList(CONFIG_IMAGEDIRS); + scriptFiles = config.getCleanPathList(CONFIG_SCRIPTS); + scriptDirs = config.getCleanPathList(CONFIG_SCRIPTDIRS); + styleFiles = config.getCleanPathList(CONFIG_STYLES); + styleDirs = config.getCleanPathList(CONFIG_STYLEDIRS); + exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS); + exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + + CONFIG_IMAGEEXTENSIONS); + + QString imagesDotFileExtensions = + CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; + QSet<QString> formats = config.subVars(imagesDotFileExtensions); + QSet<QString>::ConstIterator f = formats.begin(); + while (f != formats.end()) { + imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + + Config::dot + *f); + ++f; + } + + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if (outputFormats.contains((*g)->format())) { + (*g)->initializeGenerator(config); + QStringList extraImages = + config.getCleanPathList(CONFIG_EXTRAIMAGES+Config::dot+(*g)->format()); + QStringList::ConstIterator e = extraImages.begin(); + while (e != extraImages.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + imageFiles, + imageDirs, + *e, + imgFileExts[(*g)->format()], + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/images"); + ++e; + } + + // Documentation template handling + QString templateDir = config.getString( + (*g)->format() + Config::dot + CONFIG_TEMPLATEDIR); + + if (!templateDir.isEmpty()) { + QStringList noExts; + QStringList searchDirs = QStringList() << templateDir; + QStringList scripts = + config.getCleanPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS); + e = scripts.begin(); + while (e != scripts.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + scriptFiles, + searchDirs, + *e, + noExts, + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/scripts"); + ++e; + } + + QStringList styles = + config.getCleanPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS); + e = styles.begin(); + while (e != styles.end()) { + QString userFriendlyFilePath; + QString filePath = Config::findFile(config.lastLocation(), + styleFiles, + searchDirs, + *e, + noExts, + userFriendlyFilePath); + if (!filePath.isEmpty()) + Config::copyFile(config.lastLocation(), + filePath, + userFriendlyFilePath, + (*g)->outputDir() + + "/style"); + ++e; + } + } + } + ++g; + } + + QRegExp secondParamAndAbove("[\2-\7]"); + QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING); + QSet<QString>::ConstIterator n = formattingNames.begin(); + while (n != formattingNames.end()) { + QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; + + QSet<QString> formats = config.subVars(formattingDotName); + QSet<QString>::ConstIterator f = formats.begin(); + while (f != formats.end()) { + QString def = config.getString(formattingDotName + + Config::dot + *f); + if (!def.isEmpty()) { + int numParams = Config::numParams(def); + int numOccs = def.count("\1"); + + if (numParams != 1) { + config.lastLocation().warning(tr("Formatting '%1' must " + "have exactly one " + "parameter (found %2)") + .arg(*n).arg(numParams)); + } + else if (numOccs > 1) { + config.lastLocation().fatal(tr("Formatting '%1' must " + "contain exactly one " + "occurrence of '\\1' " + "(found %2)") + .arg(*n).arg(numOccs)); + } + else { + int paramPos = def.indexOf("\1"); + fmtLeftMaps[*f].insert(*n, def.left(paramPos)); + fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1)); + } + } + ++f; + } + ++n; + } + + project = config.getString(CONFIG_PROJECT); + + QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES); + if (!prefixes.isEmpty()) { + foreach (QString prefix, prefixes) + outputPrefixes[prefix] = config.getString( + CONFIG_OUTPUTPREFIXES + Config::dot + prefix); + } else + outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); +} + +void Generator::terminate() +{ + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if (outputFormats.contains((*g)->format())) + (*g)->terminateGenerator(); + ++g; + } + + fmtLeftMaps.clear(); + fmtRightMaps.clear(); + imgFileExts.clear(); + imageFiles.clear(); + imageDirs.clear(); + outDir_ = ""; + QmlClassNode::terminate(); + ExampleNode::terminate(); +} + +Generator *Generator::generatorForFormat(const QString& format) +{ + QList<Generator *>::ConstIterator g = generators.begin(); + while (g != generators.end()) { + if ((*g)->format() == format) + return *g; + ++g; + } + return 0; +} + +void Generator::startText(const Node * /* relative */, + CodeMarker * /* marker */) +{ +} + +void Generator::endText(const Node * /* relative */, + CodeMarker * /* marker */) +{ +} + +int Generator::generateAtom(const Atom * /* atom */, + const Node * /* relative */, + CodeMarker * /* marker */) +{ + return 0; +} + +void Generator::generateClassLikeNode(const InnerNode * /* classe */, + CodeMarker * /* marker */) +{ +} + +void Generator::generateFakeNode(const FakeNode * /* fake */, + CodeMarker * /* marker */) +{ +} + +bool Generator::generateText(const Text& text, + const Node *relative, + CodeMarker *marker) +{ + bool result = false; + if (text.firstAtom() != 0) { + int numAtoms = 0; + startText(relative, marker); + generateAtomList(text.firstAtom(), + relative, + marker, + true, + numAtoms); + endText(relative, marker); + result = true; + } + return result; +} + +#ifdef QDOC_QML +/*! + Extract sections of markup text surrounded by \e qmltext + and \e endqmltext and output them. + */ +bool Generator::generateQmlText(const Text& text, + const Node *relative, + CodeMarker *marker, + const QString& /* qmlName */ ) +{ + const Atom* atom = text.firstAtom(); + bool result = false; + + if (atom != 0) { + startText(relative, marker); + while (atom) { + if (atom->type() != Atom::QmlText) + atom = atom->next(); + else { + atom = atom->next(); + while (atom && (atom->type() != Atom::EndQmlText)) { + int n = 1 + generateAtom(atom, relative, marker); + while (n-- > 0) + atom = atom->next(); + } + } + } + endText(relative, marker); + result = true; + } + return result; +} +#endif + +void Generator::generateBody(const Node *node, CodeMarker *marker) +{ + bool quiet = false; + + if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + if (fake->subType() == Node::Example) { + generateExampleFiles(fake, marker); + } + else if ((fake->subType() == Node::File) || (fake->subType() == Node::Image)) { + quiet = true; + } + } + if (node->doc().isEmpty()) { + if (!quiet && !node->isReimp()) { // ### might be unnecessary + node->location().warning(tr("No documentation for '%1'") + .arg(marker->plainFullName(node))); + } + } + else { + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + if (func->reimplementedFrom() != 0) + generateReimplementedFrom(func, marker); + } + + if (!generateText(node->doc().body(), node, marker)) { + if (node->isReimp()) + return; + } + + if (node->type() == Node::Enum) { + const EnumNode *enume = (const EnumNode *) node; + + QSet<QString> definedItems; + QList<EnumItem>::ConstIterator it = enume->items().begin(); + while (it != enume->items().end()) { + definedItems.insert((*it).name()); + ++it; + } + + QSet<QString> documentedItems = enume->doc().enumItemNames().toSet(); + QSet<QString> allItems = definedItems + documentedItems; + if (allItems.count() > definedItems.count() || + allItems.count() > documentedItems.count()) { + QSet<QString>::ConstIterator a = allItems.begin(); + while (a != allItems.end()) { + if (!definedItems.contains(*a)) { + QString details; + QString best = nearestName(*a, definedItems); + if (!best.isEmpty() && !documentedItems.contains(best)) + details = tr("Maybe you meant '%1'?").arg(best); + + node->doc().location().warning( + tr("No such enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node)), + details); + } + else if (!documentedItems.contains(*a)) { + node->doc().location().warning( + tr("Undocumented enum item '%1' in %2").arg(*a).arg(marker->plainFullName(node))); + } + ++a; + } + } + } + else if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + QSet<QString> definedParams; + QList<Parameter>::ConstIterator p = func->parameters().begin(); + while (p != func->parameters().end()) { + if ((*p).name().isEmpty() && (*p).leftType() != QLatin1String("...") + && func->name() != QLatin1String("operator++") + && func->name() != QLatin1String("operator--")) { + node->doc().location().warning(tr("Missing parameter name")); + } + else { + definedParams.insert((*p).name()); + } + ++p; + } + + QSet<QString> documentedParams = func->doc().parameterNames(); + QSet<QString> allParams = definedParams + documentedParams; + if (allParams.count() > definedParams.count() + || allParams.count() > documentedParams.count()) { + QSet<QString>::ConstIterator a = allParams.begin(); + while (a != allParams.end()) { + if (!definedParams.contains(*a)) { + QString details; + QString best = nearestName(*a, definedParams); + if (!best.isEmpty()) + details = tr("Maybe you meant '%1'?").arg(best); + + node->doc().location().warning( + tr("No such parameter '%1' in %2").arg(*a).arg(marker->plainFullName(node)), + details); + } + else if (!(*a).isEmpty() && !documentedParams.contains(*a)) { + bool needWarning = (func->status() > Node::Obsolete); + if (func->overloadNumber() > 1) { + FunctionNode *primaryFunc = + func->parent()->findFunctionNode(func->name()); + if (primaryFunc) { + foreach (const Parameter ¶m, + primaryFunc->parameters()) { + if (param.name() == *a) { + needWarning = false; + break; + } + } + } + } + if (needWarning && !func->isReimp()) + node->doc().location().warning( + tr("Undocumented parameter '%1' in %2") + .arg(*a).arg(marker->plainFullName(node))); + } + ++a; + } + } + /* + Something like this return value check should + be implemented at some point. + */ + if (func->status() > Node::Obsolete && func->returnType() == "bool" + && func->reimplementedFrom() == 0 && !func->isOverload()) { + QString body = func->doc().body().toString(); + if (!body.contains("return", Qt::CaseInsensitive)) + node->doc().location().warning(tr("Undocumented return value")); + } + } + } + + if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + if (fake->subType() == Node::File) { + Text text; + Quoter quoter; + Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); + QString code = quoter.quoteTo(fake->location(), "", ""); + CodeMarker *codeMarker = CodeMarker::markerForFileName(fake->name()); + text << Atom(codeMarker->atomType(), code); + generateText(text, fake, codeMarker); + } + } +} + +void Generator::generateAlsoList(const Node *node, CodeMarker *marker) +{ + QList<Text> alsoList = node->doc().alsoList(); + supplementAlsoList(node, alsoList); + + if (!alsoList.isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "See also " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); + + for (int i = 0; i < alsoList.size(); ++i) + text << alsoList.at(i) << separator(i, alsoList.size()); + + text << Atom::ParaRight; + generateText(text, node, marker); + } +} + +/*! + Generate a list of maintainers in the output + */ +void Generator::generateMaintainerList(const InnerNode* node, CodeMarker* marker) +{ + QStringList sl = getMetadataElements(node,"maintainer"); + + if (!sl.isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Maintained by: " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); + + for (int i = 0; i < sl.size(); ++i) + text << sl.at(i) << separator(i, sl.size()); + + text << Atom::ParaRight; + generateText(text, node, marker); + } +} + +void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) +{ + QList<RelatedClass>::ConstIterator r; + int index; + + if (!classe->baseClasses().isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Inherits: " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); + + r = classe->baseClasses().begin(); + index = 0; + while (r != classe->baseClasses().end()) { + text << Atom(Atom::LinkNode, CodeMarker::stringForNode((*r).node)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, (*r).dataTypeWithTemplateArgs) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + if ((*r).access == Node::Protected) { + text << " (protected)"; + } + else if ((*r).access == Node::Private) { + text << " (private)"; + } + text << separator(index++, classe->baseClasses().count()); + ++r; + } + text << Atom::ParaRight; + generateText(text, classe, marker); + } +} + +#ifdef QDOC_QML +/*! + */ +void Generator::generateQmlInherits(const QmlClassNode* , CodeMarker* ) +{ + // stub. +} +#endif + +void Generator::generateInheritedBy(const ClassNode *classe, + CodeMarker *marker) +{ + if (!classe->derivedClasses().isEmpty()) { + Text text; + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Inherited by: " + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD); + + appendSortedNames(text, classe, classe->derivedClasses(), marker); + text << Atom::ParaRight; + generateText(text, classe, marker); + } +} + +/*! + This function is called when the documentation for an + example is being formatted. It outputs the list of source + files comprising the example, and the list of images used + by the example. The images are copied into a subtree of + \c{...doc/html/images/used-in-examples/...} + */ +void Generator::generateFileList(const FakeNode* fake, + CodeMarker* marker, + Node::SubType subtype, + const QString& tag) +{ + int count = 0; + Text text; + OpenedList openedList(OpenedList::Bullet); + + text << Atom::ParaLeft << tag << Atom::ParaRight + << Atom(Atom::ListLeft, openedList.styleString()); + + foreach (const Node* child, fake->childNodes()) { + if (child->subType() == subtype) { + ++count; + QString file = child->name(); + if (subtype == Node::Image) { + if (!file.isEmpty()) { + QDir dirInfo; + QString userFriendlyFilePath; + QString srcPath = Config::findFile(fake->location(), + QStringList(), + exampleDirs, + file, + exampleImgExts, + userFriendlyFilePath); + userFriendlyFilePath.truncate(userFriendlyFilePath.lastIndexOf('/')); + + QString imgOutDir = outDir_ + "/images/used-in-examples/" + userFriendlyFilePath; + if (!dirInfo.mkpath(imgOutDir)) + fake->location().fatal(tr("Cannot create output directory '%1'") + .arg(imgOutDir)); + + QString imgOutName = Config::copyFile(fake->location(), + srcPath, + file, + imgOutDir); + } + + } + + openedList.next(); + text << Atom(Atom::ListItemNumber, openedList.numberString()) + << Atom(Atom::ListItemLeft, openedList.styleString()) + << Atom::ParaLeft + << Atom(Atom::Link, file) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << file + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom::ParaRight + << Atom(Atom::ListItemRight, openedList.styleString()); + } + } + text << Atom(Atom::ListRight, openedList.styleString()); + if (count > 0) + generateText(text, fake, marker); +} + +void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) +{ + if (fake->childNodes().isEmpty()) + return; + generateFileList(fake, marker, Node::File, QString("Files:")); + generateFileList(fake, marker, Node::Image, QString("Images:")); +} + +QString Generator::indent(int level, const QString& markedCode) +{ + if (level == 0) + return markedCode; + + QString t; + int column = 0; + + int i = 0; + while (i < (int) markedCode.length()) { + if (markedCode.at(i) == QLatin1Char('\n')) { + column = 0; + } + else { + if (column == 0) { + for (int j = 0; j < level; j++) + t += QLatin1Char(' '); + } + column++; + } + t += markedCode.at(i++); + } + return t; +} + +QString Generator::plainCode(const QString& markedCode) +{ + QString t = markedCode; + t.replace(tag, QString()); + t.replace(quot, QLatin1String("\"")); + t.replace(gt, QLatin1String(">")); + t.replace(lt, QLatin1String("<")); + t.replace(amp, QLatin1String("&")); + return t; +} + +QString Generator::typeString(const Node *node) +{ + switch (node->type()) { + case Node::Namespace: + return "namespace"; + case Node::Class: + return "class"; + case Node::Fake: + { + switch (node->subType()) { + case Node::QmlClass: + return "element"; + case Node::QmlPropertyGroup: + return "property group"; + case Node::QmlBasicType: + return "type"; + default: + return "documentation"; + } + } + case Node::Enum: + return "enum"; + case Node::Typedef: + return "typedef"; + case Node::Function: + return "function"; + case Node::Property: + return "property"; + default: + return "documentation"; + } +} + +/*! + Returns a relative path name for an image. + */ +QString Generator::imageFileName(const Node *relative, const QString& fileBase) +{ + QString userFriendlyFilePath; + QString filePath = Config::findFile( + relative->doc().location(), imageFiles, imageDirs, fileBase, + imgFileExts[format()], userFriendlyFilePath); + + if (filePath.isEmpty()) + return QString(); + + QString path = Config::copyFile(relative->doc().location(), + filePath, + userFriendlyFilePath, + outputDir() + QLatin1String("/images")); + QString images = "images"; + if (path[0] != '/') + images.append(QLatin1Char('/')); + return images + path; +} + +void Generator::setImageFileExtensions(const QStringList& extensions) +{ + imgFileExts[format()] = extensions; +} + +void Generator::unknownAtom(const Atom *atom) +{ + Location::internalError(tr("unknown atom type '%1' in %2 generator") + .arg(atom->typeString()).arg(format())); +} + +bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType) +{ + return atom->next() != 0 && atom->next()->type() == expectedAtomType; +} + +void Generator::supplementAlsoList(const Node *node, QList<Text> &alsoList) +{ + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + if (func->overloadNumber() == 1) { + QString alternateName; + const FunctionNode *alternateFunc = 0; + + if (func->name().startsWith("set") && func->name().size() >= 4) { + alternateName = func->name()[3].toLower(); + alternateName += func->name().mid(4); + alternateFunc = func->parent()->findFunctionNode(alternateName); + + if (!alternateFunc) { + alternateName = "is" + func->name().mid(3); + alternateFunc = func->parent()->findFunctionNode(alternateName); + if (!alternateFunc) { + alternateName = "has" + func->name().mid(3); + alternateFunc = func->parent()->findFunctionNode(alternateName); + } + } + } + else if (!func->name().isEmpty()) { + alternateName = "set"; + alternateName += func->name()[0].toUpper(); + alternateName += func->name().mid(1); + alternateFunc = func->parent()->findFunctionNode(alternateName); + } + + if (alternateFunc && alternateFunc->access() != Node::Private) { + int i; + for (i = 0; i < alsoList.size(); ++i) { + if (alsoList.at(i).toString().contains(alternateName)) + break; + } + + if (i == alsoList.size()) { + alternateName += "()"; + + Text also; + also << Atom(Atom::Link, alternateName) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << alternateName + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + alsoList.prepend(also); + } + } + } + } +} + +QMap<QString, QString>& Generator::formattingLeftMap() +{ + return fmtLeftMaps[format()]; +} + +QMap<QString, QString>& Generator::formattingRightMap() +{ + return fmtRightMaps[format()]; +} + +/* + Trims trailimng whitespace off the \a string and returns + the trimmed string. + */ +QString Generator::trimmedTrailing(const QString& string) +{ + QString trimmed = string; + while (trimmed.length() > 0 && trimmed[trimmed.length() - 1].isSpace()) + trimmed.truncate(trimmed.length() - 1); + return trimmed; +} + +void Generator::generateStatus(const Node *node, CodeMarker *marker) +{ + Text text; + + switch (node->status()) { + case Node::Commendable: + case Node::Main: + break; + case Node::Preliminary: + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is under development and is subject to change." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom::ParaRight; + break; + case Node::Deprecated: + text << Atom::ParaLeft; + if (node->isInnerNode()) + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + text << "This " << typeString(node) << " is deprecated."; + if (node->isInnerNode()) + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD); + text << Atom::ParaRight; + break; + case Node::Obsolete: + text << Atom::ParaLeft; + if (node->isInnerNode()) + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + text << "This " << typeString(node) << " is obsolete."; + if (node->isInnerNode()) + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD); + text << " It is provided to keep old source code working. " + << "We strongly advise against " + << "using it in new code." << Atom::ParaRight; + break; + case Node::Compat: + // reimplemented in HtmlGenerator subclass + if (node->isInnerNode()) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is part of the Qt 3 compatibility layer." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << " It is provided to keep old source code working. " + << "We strongly advise against " + << "using it in new code. See " + << Atom(Atom::AutoLink, "Porting to Qt 4") + << " for more information." + << Atom::ParaRight; + } + break; + case Node::Internal: + default: + break; + } + generateText(text, node, marker); +} + +void Generator::generateThreadSafeness(const Node *node, CodeMarker *marker) +{ + Text text; + Text theStockLink; + Node::ThreadSafeness threadSafeness = node->threadSafeness(); + + Text rlink; + rlink << Atom(Atom::Link,"reentrant") + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << "reentrant" + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + Text tlink; + tlink << Atom(Atom::Link,"thread-safe") + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << "thread-safe" + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + + switch (threadSafeness) { + case Node::UnspecifiedSafeness: + break; + case Node::NonReentrant: + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Warning:" + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) + << " This " + << typeString(node) + << " is not " + << rlink + << "." + << Atom::ParaRight; + break; + case Node::Reentrant: + case Node::ThreadSafe: + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "Note:" + << Atom(Atom::FormattingRight,ATOM_FORMATTING_BOLD) + << " "; + + if (node->isInnerNode()) { + const InnerNode* innerNode = static_cast<const InnerNode*>(node); + text << "All functions in this " + << typeString(node) + << " are "; + if (threadSafeness == Node::ThreadSafe) + text << tlink; + else + text << rlink; + + bool exceptions = false; + NodeList reentrant; + NodeList threadsafe; + NodeList nonreentrant; + NodeList::ConstIterator c = innerNode->childNodes().begin(); + while (c != innerNode->childNodes().end()) { + + if ((*c)->status() != Node::Obsolete){ + switch ((*c)->threadSafeness()) { + case Node::Reentrant: + reentrant.append(*c); + if (threadSafeness == Node::ThreadSafe) + exceptions = true; + break; + case Node::ThreadSafe: + threadsafe.append(*c); + if (threadSafeness == Node::Reentrant) + exceptions = true; + break; + case Node::NonReentrant: + nonreentrant.append(*c); + exceptions = true; + break; + default: + break; + } + } + ++c; + } + if (!exceptions) + text << "."; + else if (threadSafeness == Node::Reentrant) { + if (nonreentrant.isEmpty()) { + if (!threadsafe.isEmpty()) { + text << ", but "; + appendFullNames(text,threadsafe,innerNode,marker); + singularPlural(text,threadsafe); + text << " also " << tlink << "."; + } + else + text << "."; + } + else { + text << ", except for "; + appendFullNames(text,nonreentrant,innerNode,marker); + text << ", which"; + singularPlural(text,nonreentrant); + text << " nonreentrant."; + if (!threadsafe.isEmpty()) { + text << " "; + appendFullNames(text,threadsafe,innerNode,marker); + singularPlural(text,threadsafe); + text << " " << tlink << "."; + } + } + } + else { // thread-safe + if (!nonreentrant.isEmpty() || !reentrant.isEmpty()) { + text << ", except for "; + if (!reentrant.isEmpty()) { + appendFullNames(text,reentrant,innerNode,marker); + text << ", which"; + singularPlural(text,reentrant); + text << " only " << rlink; + if (!nonreentrant.isEmpty()) + text << ", and "; + } + if (!nonreentrant.isEmpty()) { + appendFullNames(text,nonreentrant,innerNode,marker); + text << ", which"; + singularPlural(text,nonreentrant); + text << " nonreentrant."; + } + text << "."; + } + } + } + else { + text << "This " << typeString(node) << " is "; + if (threadSafeness == Node::ThreadSafe) + text << tlink; + else + text << rlink; + text << "."; + } + text << Atom::ParaRight; + } + generateText(text,node,marker); +} + +void Generator::generateSince(const Node *node, CodeMarker *marker) +{ + if (!node->since().isEmpty()) { + Text text; + text << Atom::ParaLeft + << "This " + << typeString(node); + if (node->type() == Node::Enum) + text << " was introduced or modified in "; + else + text << " was introduced in "; + + QStringList since = node->since().split(QLatin1Char(' ')); + if (since.count() == 1) { + // Handle legacy use of \since <version>. + if (project.isEmpty()) + text << "version"; + else + text << project; + text << " " << since[0]; + } else { + // Reconstruct the <project> <version> string. + text << " " << since.join(" "); + } + + text << "." << Atom::ParaRight; + generateText(text, node, marker); + } +} + +void Generator::generateReimplementedFrom(const FunctionNode *func, + CodeMarker *marker) +{ + if (func->reimplementedFrom() != 0) { + const FunctionNode *from = func->reimplementedFrom(); + if (from->access() != Node::Private && + from->parent()->access() != Node::Private) { + Text text; + text << Atom::ParaLeft << "Reimplemented from "; + QString fullName = from->parent()->name() + "::" + from->name() + "()"; + appendFullName(text, from->parent(), fullName, from); + text << "." << Atom::ParaRight; + generateText(text, func, marker); + } + } +} + +const Atom *Generator::generateAtomList(const Atom *atom, + const Node *relative, + CodeMarker *marker, + bool generate, + int &numAtoms) +{ + while (atom) { + if (atom->type() == Atom::FormatIf) { + int numAtoms0 = numAtoms; + bool rightFormat = canHandleFormat(atom->string()); + atom = generateAtomList(atom->next(), + relative, + marker, + generate && rightFormat, + numAtoms); + if (!atom) + return 0; + + if (atom->type() == Atom::FormatElse) { + ++numAtoms; + atom = generateAtomList(atom->next(), + relative, + marker, + generate && !rightFormat, + numAtoms); + if (!atom) + return 0; + } + + if (atom->type() == Atom::FormatEndif) { + if (generate && numAtoms0 == numAtoms) { + relative->location().warning(tr("Output format %1 not handled %2") + .arg(format()).arg(outFileName())); + Atom unhandledFormatAtom(Atom::UnhandledFormat, format()); + generateAtomList(&unhandledFormatAtom, + relative, + marker, + generate, + numAtoms); + } + atom = atom->next(); + } + } + else if (atom->type() == Atom::FormatElse || + atom->type() == Atom::FormatEndif) { + return atom; + } + else { + int n = 1; + if (generate) { + n += generateAtom(atom, relative, marker); + numAtoms += n; + } + while (n-- > 0) + atom = atom->next(); + } + } + return 0; +} + +void Generator::appendFullName(Text& text, + const Node *apparentNode, + const Node *relative, + CodeMarker *marker, + const Node *actualNode) +{ + if (actualNode == 0) + actualNode = apparentNode; + text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, marker->plainFullName(apparentNode, relative)) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); +} + +void Generator::appendFullName(Text& text, + const Node *apparentNode, + const QString& fullName, + const Node *actualNode) +{ + if (actualNode == 0) + actualNode = apparentNode; + text << Atom(Atom::LinkNode, CodeMarker::stringForNode(actualNode)) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, fullName) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); +} + +void Generator::appendFullNames(Text& text, + const NodeList& nodes, + const Node* relative, + CodeMarker* marker) +{ + NodeList::ConstIterator n = nodes.begin(); + int index = 0; + while (n != nodes.end()) { + appendFullName(text,*n,relative,marker); + text << comma(index++,nodes.count()); + ++n; + } +} + +void Generator::appendSortedNames(Text& text, + const ClassNode *classe, + const QList<RelatedClass> &classes, + CodeMarker *marker) +{ + QList<RelatedClass>::ConstIterator r; + QMap<QString,Text> classMap; + int index = 0; + + r = classes.begin(); + while (r != classes.end()) { + if ((*r).node->access() == Node::Public && + (*r).node->status() != Node::Internal + && !(*r).node->doc().isEmpty()) { + Text className; + appendFullName(className, (*r).node, classe, marker); + classMap[className.toString().toLower()] = className; + } + ++r; + } + + QStringList classNames = classMap.keys(); + classNames.sort(); + + foreach (const QString &className, classNames) { + text << classMap[className]; + text << separator(index++, classNames.count()); + } +} + +void Generator::appendSortedQmlNames(Text& text, + const Node* base, + const NodeList& subs, + CodeMarker *marker) +{ + QMap<QString,Text> classMap; + int index = 0; + + for (int i = 0; i < subs.size(); ++i) { + Text t; + if (!base->isQtQuickNode() || !subs[i]->isQtQuickNode() || + (base->qmlModuleIdentifier() == subs[i]->qmlModuleIdentifier())) { + appendFullName(t, subs[i], base, marker); + classMap[t.toString().toLower()] = t; + } + } + + QStringList names = classMap.keys(); + names.sort(); + + foreach (const QString &name, names) { + text << classMap[name]; + text << separator(index++, names.count()); + } +} + +int Generator::skipAtoms(const Atom *atom, Atom::Type type) const +{ + int skipAhead = 0; + atom = atom->next(); + while (atom != 0 && atom->type() != type) { + skipAhead++; + atom = atom->next(); + } + return skipAhead; +} + +QString Generator::fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const +{ + if (node->type() == Node::Fake) { + const FakeNode* fn = static_cast<const FakeNode *>(node); +#if 0 + // Removed for QTBUG-22870 + if (!fn->qmlModuleIdentifier().isEmpty()) + return fn->qmlModuleIdentifier() + QLatin1Char(' ') + fn->title(); +#endif + return fn->title(); + } + else if (node->type() == Node::Class && + !(static_cast<const ClassNode *>(node))->serviceName().isEmpty()) + return (static_cast<const ClassNode *>(node))->serviceName(); + else + return marker->plainFullName(node, relative); +} + +QString Generator::outputPrefix(const QString &nodeType) +{ + return outputPrefixes[nodeType]; +} + +/*! + Looks up the tag \a t in the map of metadata values for the + current topic in \a inner. If a value for the tag is found, + the value is returned. + + \note If \a t is found in the metadata map, it is erased. + i.e. Once you call this function for a particular \a t, + you consume \a t. + */ +QString Generator::getMetadataElement(const InnerNode* inner, const QString& t) +{ + QString s; + QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap()); + QStringMultiMap::iterator i = metaTagMap.find(t); + if (i != metaTagMap.end()) { + s = i.value(); + metaTagMap.erase(i); + } + return s; +} + +/*! + Looks up the tag \a t in the map of metadata values for the + current topic in \a inner. If values for the tag are found, + they are returned in a string list. + + \note If \a t is found in the metadata map, all the pairs + having the key \a t are erased. i.e. Once you call this + function for a particular \a t, you consume \a t. + */ +QStringList Generator::getMetadataElements(const InnerNode* inner, const QString& t) +{ + QStringList s; + QStringMultiMap& metaTagMap = const_cast<QStringMultiMap&>(inner->doc().metaTagMap()); + s = metaTagMap.values(t); + if (!s.isEmpty()) + metaTagMap.remove(t); + return s; +} + +/*! + For generating the "New Classes... in 4.6" section on the + What's New in 4.6" page. + */ +void Generator::findAllSince(const InnerNode *node) +{ + NodeList::const_iterator child = node->childNodes().constBegin(); + + // Traverse the tree, starting at the node supplied. + + while (child != node->childNodes().constEnd()) { + + QString sinceString = (*child)->since(); + + if (((*child)->access() != Node::Private) && !sinceString.isEmpty()) { + + // Insert a new entry into each map for each new since string found. + NewSinceMaps::iterator nsmap = newSinceMaps.find(sinceString); + if (nsmap == newSinceMaps.end()) + nsmap = newSinceMaps.insert(sinceString,NodeMultiMap()); + + NewClassMaps::iterator ncmap = newClassMaps.find(sinceString); + if (ncmap == newClassMaps.end()) + ncmap = newClassMaps.insert(sinceString,NodeMap()); + + NewClassMaps::iterator nqcmap = newQmlClassMaps.find(sinceString); + if (nqcmap == newQmlClassMaps.end()) + nqcmap = newQmlClassMaps.insert(sinceString,NodeMap()); + + if ((*child)->type() == Node::Function) { + // Insert functions into the general since map. + FunctionNode *func = static_cast<FunctionNode *>(*child); + if ((func->status() > Node::Obsolete) && + (func->metaness() != FunctionNode::Ctor) && + (func->metaness() != FunctionNode::Dtor)) { + nsmap.value().insert(func->name(),(*child)); + } + } + else if ((*child)->url().isEmpty()) { + if ((*child)->type() == Node::Class && !(*child)->doc().isEmpty()) { + // Insert classes into the since and class maps. + QString className = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + className = (*child)->parent()->name()+"::"+className; + + nsmap.value().insert(className,(*child)); + ncmap.value().insert(className,(*child)); + } + else if ((*child)->subType() == Node::QmlClass) { + // Insert QML elements into the since and element maps. + QString className = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + className = (*child)->parent()->name()+"::"+className; + + nsmap.value().insert(className,(*child)); + nqcmap.value().insert(className,(*child)); + } + else if ((*child)->type() == Node::QmlProperty) { + // Insert QML properties into the since map. + QString propertyName = (*child)->name(); + nsmap.value().insert(propertyName,(*child)); + } + } + else { + // Insert external documents into the general since map. + QString name = (*child)->name(); + if ((*child)->parent() && + (*child)->parent()->type() == Node::Namespace && + !(*child)->parent()->name().isEmpty()) + name = (*child)->parent()->name()+"::"+name; + + nsmap.value().insert(name,(*child)); + } + + // Find child nodes with since commands. + if ((*child)->isInnerNode()) { + findAllSince(static_cast<InnerNode *>(*child)); + } + } + ++child; + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h new file mode 100644 index 0000000000..4021a85f61 --- /dev/null +++ b/src/tools/qdoc/generator.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + generator.h +*/ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include <qlist.h> +#include <qmap.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> + +#include "node.h" +#include "text.h" + +QT_BEGIN_NAMESPACE + +typedef QMap<QString, const Node*> NodeMap; +typedef QMultiMap<QString, Node*> NodeMultiMap; +typedef QMap<QString, NodeMultiMap> NewSinceMaps; +typedef QMap<Node*, NodeMultiMap> ParentMaps; +typedef QMap<QString, NodeMap> NewClassMaps; + +class ClassNode; +class Config; +class CodeMarker; +class FakeNode; +class FunctionNode; +class InnerNode; +class Location; +class NamespaceNode; +class Node; +class Tree; + +class Generator +{ +public: + Generator(); + virtual ~Generator(); + + virtual void initializeGenerator(const Config &config); + virtual void terminateGenerator(); + virtual QString format() = 0; + virtual bool canHandleFormat(const QString &format) { return format == this->format(); } + virtual void generateTree(const Tree *tree) = 0; + + static void initialize(const Config& config); + static void terminate(); + static Generator *generatorForFormat(const QString& format); + static const QString& outputDir() { return outDir_; } + static const QString& baseDir() { return baseDir_; } + +protected: + virtual void startText(const Node *relative, CodeMarker *marker); + virtual void endText(const Node *relative, CodeMarker *marker); + virtual int generateAtom(const Atom *atom, + const Node *relative, + CodeMarker *marker); + virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker); + virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker); + + virtual bool generateText(const Text& text, + const Node *relative, + CodeMarker *marker); + virtual bool generateQmlText(const Text& text, + const Node *relative, + CodeMarker *marker, + const QString& qmlName); + virtual void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); + virtual void generateBody(const Node *node, CodeMarker *marker); + virtual void generateAlsoList(const Node *node, CodeMarker *marker); + virtual void generateMaintainerList(const InnerNode* node, CodeMarker* marker); + virtual void generateInherits(const ClassNode *classe, + CodeMarker *marker); + virtual void generateInheritedBy(const ClassNode *classe, + CodeMarker *marker); + + void generateThreadSafeness(const Node *node, CodeMarker *marker); + void generateSince(const Node *node, CodeMarker *marker); + void generateStatus(const Node *node, CodeMarker *marker); + const Atom* generateAtomList(const Atom *atom, + const Node *relative, + CodeMarker *marker, + bool generate, + int& numGeneratedAtoms); + void generateFileList(const FakeNode* fake, + CodeMarker* marker, + Node::SubType subtype, + const QString& tag); + void generateExampleFiles(const FakeNode *fake, CodeMarker *marker); + + virtual int skipAtoms(const Atom *atom, Atom::Type type) const; + virtual QString fullName(const Node *node, + const Node *relative, + CodeMarker *marker) const; + + virtual QString outFileName() { return QString(); } + + QString indent(int level, const QString& markedCode); + QString plainCode(const QString& markedCode); + virtual QString typeString(const Node *node); + virtual QString imageFileName(const Node *relative, const QString& fileBase); + void setImageFileExtensions(const QStringList& extensions); + void unknownAtom(const Atom *atom); + QMap<QString, QString> &formattingLeftMap(); + QMap<QString, QString> &formattingRightMap(); + QMap<QString, QStringList> editionModuleMap; + QMap<QString, QStringList> editionGroupMap; + + static QString trimmedTrailing(const QString &string); + static bool matchAhead(const Atom *atom, Atom::Type expectedAtomType); + static void supplementAlsoList(const Node *node, QList<Text> &alsoList); + static QString outputPrefix(const QString &nodeType); + + QString getMetadataElement(const InnerNode* inner, const QString& t); + QStringList getMetadataElements(const InnerNode* inner, const QString& t); + void findAllSince(const InnerNode *node); + +private: + void generateReimplementedFrom(const FunctionNode *func, + CodeMarker *marker); + void appendFullName(Text& text, + const Node *apparentNode, + const Node *relative, + CodeMarker *marker, + const Node *actualNode = 0); + void appendFullName(Text& text, + const Node *apparentNode, + const QString& fullName, + const Node *actualNode); + void appendFullNames(Text& text, + const NodeList& nodes, + const Node* relative, + CodeMarker* marker); + void appendSortedNames(Text& text, + const ClassNode *classe, + const QList<RelatedClass> &classes, + CodeMarker *marker); + +protected: + void appendSortedQmlNames(Text& text, + const Node* base, + const NodeList& subs, + CodeMarker *marker); + + static QString sinceTitles[]; + NewSinceMaps newSinceMaps; + NewClassMaps newClassMaps; + NewClassMaps newQmlClassMaps; + +private: + QString amp; + QString lt; + QString gt; + QString quot; + QRegExp tag; + + static QList<Generator *> generators; + static QMap<QString, QMap<QString, QString> > fmtLeftMaps; + static QMap<QString, QMap<QString, QString> > fmtRightMaps; + static QMap<QString, QStringList> imgFileExts; + static QSet<QString> outputFormats; + static QStringList imageFiles; + static QStringList imageDirs; + static QStringList exampleDirs; + static QStringList exampleImgExts; + static QStringList scriptFiles; + static QStringList scriptDirs; + static QStringList styleFiles; + static QStringList styleDirs; + static QString outDir_; + static QString baseDir_; + static QString project; + static QHash<QString, QString> outputPrefixes; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/helpprojectwriter.cpp b/src/tools/qdoc/helpprojectwriter.cpp new file mode 100644 index 0000000000..096877211d --- /dev/null +++ b/src/tools/qdoc/helpprojectwriter.cpp @@ -0,0 +1,772 @@ +/**************************************************************************** +** +** 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 <QHash> +#include <QMap> + +#include "atom.h" +#include "helpprojectwriter.h" +#include "htmlgenerator.h" +#include "config.h" +#include "node.h" +#include "tree.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +HelpProjectWriter::HelpProjectWriter(const Config &config, const QString &defaultFileName) +{ + // The output directory should already have been checked by the calling + // generator. + outputDir = config.getOutputDir(); + + QStringList names = config.getStringList(CONFIG_QHP + Config::dot + "projects"); + + foreach (const QString &projectName, names) { + HelpProject project; + project.name = projectName; + + QString prefix = CONFIG_QHP + Config::dot + projectName + Config::dot; + project.helpNamespace = config.getString(prefix + "namespace"); + project.virtualFolder = config.getString(prefix + "virtualFolder"); + project.fileName = config.getString(prefix + "file"); + if (project.fileName.isEmpty()) + project.fileName = defaultFileName; + project.extraFiles = config.getStringSet(prefix + "extraFiles"); + project.indexTitle = config.getString(prefix + "indexTitle"); + project.indexRoot = config.getString(prefix + "indexRoot"); + project.filterAttributes = config.getStringList(prefix + "filterAttributes").toSet(); + QSet<QString> customFilterNames = config.subVars(prefix + "customFilters"); + foreach (const QString &filterName, customFilterNames) { + QString name = config.getString(prefix + "customFilters" + Config::dot + filterName + Config::dot + "name"); + QSet<QString> filters = config.getStringList(prefix + "customFilters" + Config::dot + filterName + Config::dot + "filterAttributes").toSet(); + project.customFilters[name] = filters; + } + //customFilters = config.defs. + + foreach (QString name, config.getStringSet(prefix + "excluded")) + project.excluded.insert(name.replace("\\", "/")); + + foreach (const QString &name, config.getStringList(prefix + "subprojects")) { + SubProject subproject; + QString subprefix = prefix + "subprojects" + Config::dot + name + Config::dot; + subproject.title = config.getString(subprefix + "title"); + subproject.indexTitle = config.getString(subprefix + "indexTitle"); + subproject.sortPages = config.getBool(subprefix + "sortPages"); + subproject.type = config.getString(subprefix + "type"); + readSelectors(subproject, config.getStringList(subprefix + "selectors")); + project.subprojects[name] = subproject; + } + + if (project.subprojects.isEmpty()) { + SubProject subproject; + readSelectors(subproject, config.getStringList(prefix + "selectors")); + project.subprojects[""] = subproject; + } + + projects.append(project); + } +} + +void HelpProjectWriter::readSelectors(SubProject &subproject, const QStringList &selectors) +{ + QHash<QString, Node::Type> typeHash; + typeHash["namespace"] = Node::Namespace; + typeHash["class"] = Node::Class; + typeHash["fake"] = Node::Fake; + typeHash["enum"] = Node::Enum; + typeHash["typedef"] = Node::Typedef; + typeHash["function"] = Node::Function; + typeHash["property"] = Node::Property; + typeHash["variable"] = Node::Variable; + typeHash["target"] = Node::Target; +#ifdef QDOC_QML + typeHash["qmlproperty"] = Node::QmlProperty; + typeHash["qmlsignal"] = Node::QmlSignal; + typeHash["qmlsignalhandler"] = Node::QmlSignalHandler; + typeHash["qmlmethod"] = Node::QmlMethod; +#endif + + QHash<QString, Node::SubType> subTypeHash; + subTypeHash["example"] = Node::Example; + subTypeHash["headerfile"] = Node::HeaderFile; + subTypeHash["file"] = Node::File; + subTypeHash["group"] = Node::Group; + subTypeHash["module"] = Node::Module; + subTypeHash["page"] = Node::Page; + subTypeHash["externalpage"] = Node::ExternalPage; +#ifdef QDOC_QML + subTypeHash["qmlclass"] = Node::QmlClass; + subTypeHash["qmlpropertygroup"] = Node::QmlPropertyGroup; + subTypeHash["qmlbasictype"] = Node::QmlBasicType; +#endif + + QSet<Node::SubType> allSubTypes = QSet<Node::SubType>::fromList(subTypeHash.values()); + + foreach (const QString &selector, selectors) { + QStringList pieces = selector.split(QLatin1Char(':')); + if (pieces.size() == 1) { + QString lower = selector.toLower(); + if (typeHash.contains(lower)) + subproject.selectors[typeHash[lower]] = allSubTypes; + } else if (pieces.size() >= 2) { + QString lower = pieces[0].toLower(); + pieces = pieces[1].split(QLatin1Char(',')); + if (typeHash.contains(lower)) { + QSet<Node::SubType> subTypes; + for (int i = 0; i < pieces.size(); ++i) { + QString lower = pieces[i].toLower(); + if (subTypeHash.contains(lower)) + subTypes.insert(subTypeHash[lower]); + } + subproject.selectors[typeHash[lower]] = subTypes; + } + } + } +} + +void HelpProjectWriter::addExtraFile(const QString &file) +{ + for (int i = 0; i < projects.size(); ++i) + projects[i].extraFiles.insert(file); +} + +void HelpProjectWriter::addExtraFiles(const QSet<QString> &files) +{ + for (int i = 0; i < projects.size(); ++i) + projects[i].extraFiles.unite(files); +} + +/* + Returns a list of strings describing the keyword details for a given node. + + The first string is the human-readable name to be shown in Assistant. + The second string is a unique identifier. + The third string is the location of the documentation for the keyword. +*/ +QStringList HelpProjectWriter::keywordDetails(const Node *node) const +{ + QStringList details; + + if (node->type() == Node::QmlProperty) { + // "name" + details << node->name(); + // "id" + details << node->parent()->parent()->name()+"::"+node->name(); + } + else if (node->parent() && !node->parent()->name().isEmpty()) { + // "name" + if (node->type() == Node::Enum || node->type() == Node::Typedef) + details << node->parent()->name()+"::"+node->name(); + else + details << node->name(); + // "id" + details << node->parent()->name()+"::"+node->name(); + } + else if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + if (fake->subType() == Node::QmlClass) { + details << (QmlClassNode::qmlOnly ? fake->name() : fake->fullTitle()); + details << "QML." + fake->name(); + } + else { + details << fake->fullTitle(); + details << fake->fullTitle(); + } + } + else { + details << node->name(); + details << node->name(); + } + details << HtmlGenerator::fullDocumentLocation(node,true); + return details; +} + +bool HelpProjectWriter::generateSection(HelpProject &project, + QXmlStreamWriter & /* writer */, + const Node *node) +{ + if (!node->url().isEmpty()) + return false; + + if (node->access() == Node::Private || node->status() == Node::Internal) + return false; + + if (node->name().isEmpty()) + return true; + + QString docPath = node->doc().location().filePath(); + if (!docPath.isEmpty() && project.excluded.contains(docPath)) + return false; + + QString objName; + if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + objName = fake->fullTitle(); + } + else + objName = node->fullDocumentName(); + + // Only add nodes to the set for each subproject if they match a selector. + // Those that match will be listed in the table of contents. + + foreach (const QString &name, project.subprojects.keys()) { + SubProject subproject = project.subprojects[name]; + // No selectors: accept all nodes. + if (subproject.selectors.isEmpty()) { + project.subprojects[name].nodes[objName] = node; + } + else if (subproject.selectors.contains(node->type())) { + // Accept only the node types in the selectors hash. + if (node->type() != Node::Fake) + project.subprojects[name].nodes[objName] = node; + else { + // Accept only fake nodes with subtypes contained in the selector's + // mask. + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (subproject.selectors[node->type()].contains(fakeNode->subType()) && + fakeNode->subType() != Node::ExternalPage && + !fakeNode->fullTitle().isEmpty()) { + + project.subprojects[name].nodes[objName] = node; + } + } + } + } + + switch (node->type()) { + + case Node::Class: + project.keywords.append(keywordDetails(node)); + project.files.insert(HtmlGenerator::fullDocumentLocation(node,true)); + break; + + case Node::Namespace: + project.keywords.append(keywordDetails(node)); + project.files.insert(HtmlGenerator::fullDocumentLocation(node,true)); + break; + + case Node::Enum: + project.keywords.append(keywordDetails(node)); + { + const EnumNode *enumNode = static_cast<const EnumNode*>(node); + foreach (const EnumItem &item, enumNode->items()) { + QStringList details; + + if (enumNode->itemAccess(item.name()) == Node::Private) + continue; + + if (!node->parent()->name().isEmpty()) { + details << node->parent()->name()+"::"+item.name(); // "name" + details << node->parent()->name()+"::"+item.name(); // "id" + } else { + details << item.name(); // "name" + details << item.name(); // "id" + } + details << HtmlGenerator::fullDocumentLocation(node,true); + project.keywords.append(details); + } + } + break; + + case Node::Property: + case Node::QmlProperty: + case Node::QmlSignal: + case Node::QmlSignalHandler: + case Node::QmlMethod: + project.keywords.append(keywordDetails(node)); + break; + + case Node::Function: + { + const FunctionNode *funcNode = static_cast<const FunctionNode *>(node); + + // Only insert keywords for non-constructors. Constructors are covered + // by the classes themselves. + + if (funcNode->metaness() != FunctionNode::Ctor) + project.keywords.append(keywordDetails(node)); + + // Insert member status flags into the entries for the parent + // node of the function, or the node it is related to. + // Since parent nodes should have already been inserted into + // the set of files, we only need to ensure that related nodes + // are inserted. + + if (node->relates()) { + project.memberStatus[node->relates()].insert(node->status()); + project.files.insert(HtmlGenerator::fullDocumentLocation(node->relates(),true)); + } else if (node->parent()) + project.memberStatus[node->parent()].insert(node->status()); + } + break; + + case Node::Typedef: + { + const TypedefNode *typedefNode = static_cast<const TypedefNode *>(node); + QStringList typedefDetails = keywordDetails(node); + const EnumNode *enumNode = typedefNode->associatedEnum(); + // Use the location of any associated enum node in preference + // to that of the typedef. + if (enumNode) + typedefDetails[2] = HtmlGenerator::fullDocumentLocation(enumNode,true); + + project.keywords.append(typedefDetails); + } + break; + + case Node::Variable: + { + QString location = HtmlGenerator::fullDocumentLocation(node,true); + project.files.insert(location.left(location.lastIndexOf(QLatin1Char('#')))); + project.keywords.append(keywordDetails(node)); + } + break; + + // Fake nodes (such as manual pages) contain subtypes, titles and other + // attributes. + case Node::Fake: { + const FakeNode *fakeNode = static_cast<const FakeNode*>(node); + if (fakeNode->subType() != Node::ExternalPage && + !fakeNode->fullTitle().isEmpty()) { + + if (fakeNode->subType() != Node::File) { + if (fakeNode->doc().hasKeywords()) { + foreach (const Atom *keyword, fakeNode->doc().keywords()) { + if (!keyword->string().isEmpty()) { + QStringList details; + details << keyword->string() + << keyword->string() + << HtmlGenerator::fullDocumentLocation(node,true) + + QLatin1Char('#') + Doc::canonicalTitle(keyword->string()); + project.keywords.append(details); + } else + fakeNode->doc().location().warning( + tr("Bad keyword in %1").arg(HtmlGenerator::fullDocumentLocation(node,true)) + ); + } + } + project.keywords.append(keywordDetails(node)); + } + /* + if (fakeNode->doc().hasTableOfContents()) { + foreach (const Atom *item, fakeNode->doc().tableOfContents()) { + QString title = Text::sectionHeading(item).toString(); + if (!title.isEmpty()) { + QStringList details; + details << title + << title + << HtmlGenerator::fullDocumentLocation(node,true) + + QLatin1Char('#') + Doc::canonicalTitle(title); + project.keywords.append(details); + } else + fakeNode->doc().location().warning( + tr("Bad contents item in %1").arg(HtmlGenerator::fullDocumentLocation(node,true))); + } + } +*/ + project.files.insert(HtmlGenerator::fullDocumentLocation(node,true)); + } + break; + } + default: + ; + } + + // Add all images referenced in the page to the set of files to include. + const Atom *atom = node->doc().body().firstAtom(); + while (atom) { + if (atom->type() == Atom::Image || atom->type() == Atom::InlineImage) { + // Images are all placed within a single directory regardless of + // whether the source images are in a nested directory structure. + QStringList pieces = atom->string().split(QLatin1Char('/')); + project.files.insert("images/" + pieces.last()); + } + atom = atom->next(); + } + + return true; +} + +void HelpProjectWriter::generateSections(HelpProject &project, + QXmlStreamWriter &writer, const Node *node) +{ + if (!generateSection(project, writer, node)) + return; + + if (node->isInnerNode()) { + const InnerNode *inner = static_cast<const InnerNode *>(node); + + // Ensure that we don't visit nodes more than once. + QMap<QString, const Node*> childMap; + foreach (const Node *node, inner->childNodes()) { + if (node->access() == Node::Private) + continue; + if (node->type() == Node::Fake) { + /* + Don't visit QML property group nodes, + but visit their children, which are all + QML property nodes. + */ + if (node->subType() == Node::QmlPropertyGroup) { + const InnerNode* inner = static_cast<const InnerNode*>(node); + foreach (const Node* n, inner->childNodes()) { + if (n->access() == Node::Private) + continue; + childMap[n->fullDocumentName()] = n; + } + } + else + childMap[static_cast<const FakeNode *>(node)->fullTitle()] = node; + } + else { + if (node->type() == Node::Function) { + const FunctionNode *funcNode = static_cast<const FunctionNode *>(node); + if (funcNode->isOverload()) + continue; + } + childMap[node->fullDocumentName()] = node; + } + } + + foreach (const Node *child, childMap) + generateSections(project, writer, child); + } +} + +void HelpProjectWriter::generate(const Tree *tre) +{ + this->tree = tre; + for (int i = 0; i < projects.size(); ++i) + generateProject(projects[i]); +} + +void HelpProjectWriter::writeNode(HelpProject &project, QXmlStreamWriter &writer, + const Node *node) +{ + QString href = HtmlGenerator::fullDocumentLocation(node,true); + QString objName = node->name(); + + switch (node->type()) { + + case Node::Class: + writer.writeStartElement("section"); + writer.writeAttribute("ref", href); + if (node->parent() && !node->parent()->name().isEmpty()) + writer.writeAttribute("title", tr("%1::%2 Class Reference").arg(node->parent()->name()).arg(objName)); + else + writer.writeAttribute("title", tr("%1 Class Reference").arg(objName)); + + // Write subsections for all members, obsolete members and Qt 3 + // members. + if (!project.memberStatus[node].isEmpty()) { + QString membersPath = href.left(href.size()-5) + "-members.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", membersPath); + writer.writeAttribute("title", tr("List of all members")); + writer.writeEndElement(); // section + project.files.insert(membersPath); + } + if (project.memberStatus[node].contains(Node::Compat)) { + QString compatPath = href.left(href.size()-5) + "-qt3.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", compatPath); + writer.writeAttribute("title", tr("Qt 3 support members")); + writer.writeEndElement(); // section + project.files.insert(compatPath); + } + if (project.memberStatus[node].contains(Node::Obsolete)) { + QString obsoletePath = href.left(href.size()-5) + "-obsolete.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", obsoletePath); + writer.writeAttribute("title", tr("Obsolete members")); + writer.writeEndElement(); // section + project.files.insert(obsoletePath); + } + + writer.writeEndElement(); // section + break; + + case Node::Namespace: + writer.writeStartElement("section"); + writer.writeAttribute("ref", href); + writer.writeAttribute("title", objName); + writer.writeEndElement(); // section + break; + + case Node::Fake: { + // Fake nodes (such as manual pages) contain subtypes, titles and other + // attributes. + const FakeNode *fakeNode = static_cast<const FakeNode*>(node); + + writer.writeStartElement("section"); + writer.writeAttribute("ref", href); + writer.writeAttribute("title", fakeNode->fullTitle()); + + if ((fakeNode->subType() == Node::HeaderFile) || (fakeNode->subType() == Node::QmlClass)) { + // Write subsections for all members, obsolete members and Qt 3 + // members. + if (!project.memberStatus[node].isEmpty() || (fakeNode->subType() == Node::QmlClass)) { + QString membersPath = href.left(href.size()-5) + "-members.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", membersPath); + writer.writeAttribute("title", tr("List of all members")); + writer.writeEndElement(); // section + project.files.insert(membersPath); + } + if (project.memberStatus[node].contains(Node::Compat)) { + QString compatPath = href.left(href.size()-5) + "-qt3.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", compatPath); + writer.writeAttribute("title", tr("Qt 3 support members")); + writer.writeEndElement(); // section + project.files.insert(compatPath); + } + if (project.memberStatus[node].contains(Node::Obsolete)) { + QString obsoletePath = href.left(href.size()-5) + "-obsolete.html"; + writer.writeStartElement("section"); + writer.writeAttribute("ref", obsoletePath); + writer.writeAttribute("title", tr("Obsolete members")); + writer.writeEndElement(); // section + project.files.insert(obsoletePath); + } + } + + writer.writeEndElement(); // section + } + break; + default: + ; + } +} + +void HelpProjectWriter::generateProject(HelpProject &project) +{ + const Node *rootNode; + if (!project.indexRoot.isEmpty()) + rootNode = tree->findFakeNodeByTitle(project.indexRoot); + else + rootNode = tree->root(); + + if (!rootNode) + return; + + project.files.clear(); + project.keywords.clear(); + + QFile file(outputDir + QDir::separator() + project.fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("QtHelpProject"); + writer.writeAttribute("version", "1.0"); + + // Write metaData, virtualFolder and namespace elements. + writer.writeTextElement("namespace", project.helpNamespace); + writer.writeTextElement("virtualFolder", project.virtualFolder); + + // Write customFilter elements. + QHash<QString, QSet<QString> >::ConstIterator it; + for (it = project.customFilters.begin(); it != project.customFilters.end(); ++it) { + writer.writeStartElement("customFilter"); + writer.writeAttribute("name", it.key()); + foreach (const QString &filter, it.value()) + writer.writeTextElement("filterAttribute", filter); + writer.writeEndElement(); // customFilter + } + + // Start the filterSection. + writer.writeStartElement("filterSection"); + + // Write filterAttribute elements. + foreach (const QString &filterName, project.filterAttributes) + writer.writeTextElement("filterAttribute", filterName); + + writer.writeStartElement("toc"); + writer.writeStartElement("section"); + const Node* node = tree->findFakeNodeByTitle(project.indexTitle); + if (node == 0) + node = tree->findNode(QStringList("index.html")); + QString indexPath; + if (node) + indexPath = HtmlGenerator::fullDocumentLocation(node,true); + else + indexPath = "index.html"; + writer.writeAttribute("ref", indexPath); + writer.writeAttribute("title", project.indexTitle); + project.files.insert(HtmlGenerator::fullDocumentLocation(rootNode)); + + generateSections(project, writer, rootNode); + + foreach (const QString &name, project.subprojects.keys()) { + SubProject subproject = project.subprojects[name]; + + if (subproject.type == QLatin1String("manual")) { + + const FakeNode *indexPage = tree->findFakeNodeByTitle(subproject.indexTitle); + if (indexPage) { + Text indexBody = indexPage->doc().body(); + const Atom *atom = indexBody.firstAtom(); + QStack<int> sectionStack; + bool inItem = false; + + while (atom) { + switch (atom->type()) { + case Atom::ListLeft: + sectionStack.push(0); + break; + case Atom::ListRight: + if (sectionStack.pop() > 0) + writer.writeEndElement(); // section + break; + case Atom::ListItemLeft: + inItem = true; + break; + case Atom::ListItemRight: + inItem = false; + break; + case Atom::Link: + if (inItem) { + if (sectionStack.top() > 0) + writer.writeEndElement(); // section + + const FakeNode *page = tree->findFakeNodeByTitle(atom->string()); + writer.writeStartElement("section"); + QString indexPath = HtmlGenerator::fullDocumentLocation(page,true); + writer.writeAttribute("ref", indexPath); + writer.writeAttribute("title", atom->string()); + project.files.insert(indexPath); + + sectionStack.top() += 1; + } + break; + default: + ; + } + + if (atom == indexBody.lastAtom()) + break; + atom = atom->next(); + } + } else + rootNode->doc().location().warning( + tr("Failed to find index: %1").arg(subproject.indexTitle) + ); + + } else { + + if (!name.isEmpty()) { + writer.writeStartElement("section"); + QString indexPath = HtmlGenerator::fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle),true); + writer.writeAttribute("ref", indexPath); + writer.writeAttribute("title", subproject.title); + project.files.insert(indexPath); + } + if (subproject.sortPages) { + QStringList titles = subproject.nodes.keys(); + titles.sort(); + foreach (const QString &title, titles) { + writeNode(project, writer, subproject.nodes[title]); + } + } else { + // Find a contents node and navigate from there, using the NextLink values. + QSet<QString> visited; + + foreach (const Node *node, subproject.nodes) { + QString nextTitle = node->links().value(Node::NextLink).first; + if (!nextTitle.isEmpty() && + node->links().value(Node::ContentsLink).first.isEmpty()) { + + FakeNode *nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle)); + + // Write the contents node. + writeNode(project, writer, node); + + while (nextPage) { + writeNode(project, writer, nextPage); + nextTitle = nextPage->links().value(Node::NextLink).first; + if (nextTitle.isEmpty() || visited.contains(nextTitle)) + break; + nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(nextTitle)); + visited.insert(nextTitle); + } + break; + } + } + } + + if (!name.isEmpty()) + writer.writeEndElement(); // section + } + } + + writer.writeEndElement(); // section + writer.writeEndElement(); // toc + + writer.writeStartElement("keywords"); + foreach (const QStringList &details, project.keywords) { + writer.writeStartElement("keyword"); + writer.writeAttribute("name", details[0]); + writer.writeAttribute("id", details[1]); + writer.writeAttribute("ref", details[2]); + writer.writeEndElement(); //keyword + } + writer.writeEndElement(); // keywords + + writer.writeStartElement("files"); + foreach (const QString &usedFile, project.files) { + if (!usedFile.isEmpty()) + writer.writeTextElement("file", usedFile); + } + foreach (const QString &usedFile, project.extraFiles) + writer.writeTextElement("file", usedFile); + writer.writeEndElement(); // files + + writer.writeEndElement(); // filterSection + writer.writeEndElement(); // QtHelpProject + writer.writeEndDocument(); + file.close(); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/helpprojectwriter.h b/src/tools/qdoc/helpprojectwriter.h new file mode 100644 index 0000000000..8725f6275e --- /dev/null +++ b/src/tools/qdoc/helpprojectwriter.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef HELPPROJECTWRITER_H +#define HELPPROJECTWRITER_H + +#include <QString> +#include <QXmlStreamReader> +#include <QXmlStreamWriter> + +#include "config.h" +#include "node.h" + +QT_BEGIN_NAMESPACE + +class Tree; +typedef QPair<QString, const Node*> QStringNodePair; + +struct SubProject +{ + QString title; + QString indexTitle; + QHash<Node::Type, QSet<FakeNode::SubType> > selectors; + bool sortPages; + QString type; + QHash<QString, const Node *> nodes; +}; + +struct HelpProject +{ + QString name; + QString helpNamespace; + QString virtualFolder; + QString fileName; + QString indexRoot; + QString indexTitle; + QList<QStringList> keywords; + QSet<QString> files; + QSet<QString> extraFiles; + QSet<QString> filterAttributes; + QHash<QString, QSet<QString> > customFilters; + QSet<QString> excluded; + QMap<QString, SubProject> subprojects; + QHash<const Node *, QSet<Node::Status> > memberStatus; +}; + +class HelpProjectWriter +{ +public: + HelpProjectWriter(const Config &config, const QString &defaultFileName); + void addExtraFile(const QString &file); + void addExtraFiles(const QSet<QString> &files); + void generate(const Tree *tre); + +private: + void generateProject(HelpProject &project); + void generateSections(HelpProject &project, QXmlStreamWriter &writer, + const Node *node); + bool generateSection(HelpProject &project, QXmlStreamWriter &writer, + const Node *node); + QStringList keywordDetails(const Node *node) const; + void writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node); + void readSelectors(SubProject &subproject, const QStringList &selectors); + + const Tree *tree; + + QString outputDir; + QList<HelpProject> projects; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp new file mode 100644 index 0000000000..dd3a0fcbfc --- /dev/null +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -0,0 +1,5115 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + htmlgenerator.cpp +*/ + +#include "codemarker.h" +#include "codeparser.h" +#include "helpprojectwriter.h" +#include "htmlgenerator.h" +#include "node.h" +#include "separator.h" +#include "tree.h" +#include <ctype.h> +#include <qdebug.h> +#include <qlist.h> +#include <qiterator.h> +#include <qtextcodec.h> +#include <QUuid> + +QT_BEGIN_NAMESPACE + +#define COMMAND_VERSION Doc::alias("version") +int HtmlGenerator::id = 0; +bool HtmlGenerator::debugging_on = false; + +QString HtmlGenerator::divNavTop = ""; + +static bool showBrokenLinks = false; + +static QRegExp linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); +static QRegExp funcTag("(<@func target=\"([^\"]*)\">)(.*)(</@func>)"); +static QRegExp typeTag("(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)"); +static QRegExp spanTag("</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>"); +static QRegExp unknownTag("</?@[^>]*>"); + +static void addLink(const QString &linkTarget, + const QStringRef &nestedStuff, + QString *res) +{ + if (!linkTarget.isEmpty()) { + *res += "<a href=\""; + *res += linkTarget; + *res += "\">"; + *res += nestedStuff; + *res += "</a>"; + } + else { + *res += nestedStuff; + } +} + + +HtmlGenerator::HtmlGenerator() + : helpProjectWriter(0), + inLink(false), + inObsoleteLink(false), + inContents(false), + inSectionHeading(false), + inTableHeader(false), + numTableRows(0), + threeColumnEnumValueTable(true), + funcLeftParen("\\S(\\()"), + myTree(0), + obsoleteLinks(false) +{ +} + +/*! + The destructor deletes the instance of HelpProjectWriter. + */ +HtmlGenerator::~HtmlGenerator() +{ + if (helpProjectWriter) + delete helpProjectWriter; +} + +void HtmlGenerator::initializeGenerator(const Config &config) +{ + static const struct { + const char *key; + const char *left; + const char *right; + } defaults[] = { + { ATOM_FORMATTING_BOLD, "<b>", "</b>" }, + { ATOM_FORMATTING_INDEX, "<!--", "-->" }, + { ATOM_FORMATTING_ITALIC, "<i>", "</i>" }, + { ATOM_FORMATTING_PARAMETER, "<i>", "</i>" }, + { ATOM_FORMATTING_SUBSCRIPT, "<sub>", "</sub>" }, + { ATOM_FORMATTING_SUPERSCRIPT, "<sup>", "</sup>" }, + { ATOM_FORMATTING_TELETYPE, "<tt>", "</tt>" }, + { ATOM_FORMATTING_UNDERLINE, "<u>", "</u>" }, + { 0, 0, 0 } + }; + + Generator::initializeGenerator(config); + obsoleteLinks = config.getBool(QLatin1String(CONFIG_OBSOLETELINKS)); + setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif"); + int i = 0; + while (defaults[i].key) { + formattingLeftMap().insert(defaults[i].key, defaults[i].left); + formattingRightMap().insert(defaults[i].key, defaults[i].right); + i++; + } + + style = config.getString(HtmlGenerator::format() + + Config::dot + + CONFIG_STYLE); + endHeader = config.getString(HtmlGenerator::format() + + Config::dot + + CONFIG_ENDHEADER); + postHeader = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_POSTHEADER); + postPostHeader = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_POSTPOSTHEADER); + footer = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_FOOTER); + address = config.getString(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_ADDRESS); + pleaseGenerateMacRef = config.getBool(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_GENERATEMACREFS); + noBreadCrumbs = config.getBool(HtmlGenerator::format() + + Config::dot + + HTMLGENERATOR_NOBREADCRUMBS); + + 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"); + + QSet<QString> editionNames = config.subVars(CONFIG_EDITION); + QSet<QString>::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; + } + + codeIndent = config.getInt(CONFIG_CODEINDENT); + + helpProjectWriter = new HelpProjectWriter(config, + project.toLower() + + ".qhp"); + + // Documentation template handling + headerScripts = config.getString(HtmlGenerator::format() + Config::dot + + CONFIG_HEADERSCRIPTS); + headerStyles = config.getString(HtmlGenerator::format() + + Config::dot + + CONFIG_HEADERSTYLES); + + QString prefix = CONFIG_QHP + Config::dot + "Qt" + Config::dot; + manifestDir = "qthelp://" + config.getString(prefix + "namespace"); + manifestDir += QLatin1Char('/') + config.getString(prefix + "virtualFolder") + QLatin1Char('/'); + + /* + If the output files will be in subdirectores in the output + directory, change the references to files in the style and + scripts subdirectories that appear in the headerscipts and + headerstyles string so that they link to the correct files, + whic means prepending "../" to each + */ + if (!baseDir().isEmpty()) { + headerScripts = headerScripts.replace("style/","../style/"); + headerScripts = headerScripts.replace("scripts/","../scripts/"); + headerStyles = headerStyles.replace("style/","../style/"); + headerStyles = headerStyles.replace("scripts/","../scripts/"); + } +} + +void HtmlGenerator::terminateGenerator() +{ + Generator::terminateGenerator(); +} + +QString HtmlGenerator::format() +{ + return "HTML"; +} + +/*! + This is where the HTML files are written. + \note The HTML file generation is done in the base class, + PageGenerator::generateTree(). + */ +void HtmlGenerator::generateTree(const Tree *tree) +{ + myTree = 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); + reportOrphans(tree->root()); + generateDisambiguationPages(); + + QString fileBase = project.toLower().simplified().replace(" ", "-"); + generateIndex(fileBase, projectUrl, projectDescription); + generatePageIndex(outputDir() + QLatin1Char('/') + fileBase + ".pageindex"); + + helpProjectWriter->generate(myTree); + generateManifestFiles(); +} + +void HtmlGenerator::startText(const Node * /* relative */, + CodeMarker * /* marker */) +{ + inLink = false; + inContents = false; + inSectionHeading = false; + inTableHeader = false; + numTableRows = 0; + threeColumnEnumValueTable = true; + link.clear(); + sectionNumber.clear(); +} + +/*! + Generate html from an instance of Atom. + */ +int HtmlGenerator::generateAtom(const Atom *atom, + const Node *relative, + CodeMarker *marker) +{ + int skipAhead = 0; + static bool in_para = false; + + switch (atom->type()) { + case Atom::AbstractLeft: + if (relative) + relative->doc().location().warning(tr("\abstract is not implemented.")); + else + Location::information(tr("\abstract is not implemented.")); + break; + case Atom::AbstractRight: + break; + case Atom::AutoLink: + if (!inLink && !inContents && !inSectionHeading) { + const Node *node = 0; + QString link = getLink(atom, relative, marker, &node); + if (!link.isEmpty()) { + beginLink(link, node, relative, marker); + generateLink(atom, relative, marker); + endLink(); + } + else { + out() << protectEnc(atom->string()); + } + } + else { + out() << protectEnc(atom->string()); + } + break; + case Atom::BaseName: + break; + case Atom::BriefLeft: + if (relative->type() == Node::Fake) { + if (relative->subType() != Node::Example) { + skipAhead = skipAtoms(atom, Atom::BriefRight); + break; + } + } + + out() << "<p>"; + if (relative->type() == Node::Property || + relative->type() == Node::Variable) { + QString str; + 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); + out() << "This "; + if (relative->type() == Node::Property) + out() << "property"; + else + out() << "variable"; + QStringList words = str.split(QLatin1Char(' ')); + if (!(words.first() == "contains" || words.first() == "specifies" + || words.first() == "describes" || words.first() == "defines" + || words.first() == "holds" || words.first() == "determines")) + out() << " holds "; + else + out() << ' '; + out() << str << '.'; + } + break; + case Atom::BriefRight: + if (relative->type() != Node::Fake) + out() << "</p>\n"; + break; + case Atom::C: + // This may at one time have been used to mark up C++ code but it is + // now widely used to write teletype text. As a result, text marked + // with the \c command is not passed to a code marker. + out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE]; + if (inLink) { + out() << protectEnc(plainCode(atom->string())); + } + else { + out() << protectEnc(plainCode(atom->string())); + } + out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE]; + break; + case Atom::CaptionLeft: + out() << "<p class=\"figCaption\">"; + in_para = true; + break; + case Atom::CaptionRight: + endLink(); + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + break; + case Atom::Code: + out() << "<pre class=\"cpp\">" + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) + << "</pre>\n"; + break; +#ifdef QDOC_QML + case Atom::Qml: + out() << "<pre class=\"qml\">" + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) + << "</pre>\n"; + break; + case Atom::JavaScript: + out() << "<pre class=\"js\">" + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) + << "</pre>\n"; + break; +#endif + case Atom::CodeNew: + out() << "<p>you can rewrite it as</p>\n" + << "<pre class=\"cpp\">" + << trimmedTrailing(highlightedCode(indent(codeIndent,atom->string()), + marker,relative)) + << "</pre>\n"; + break; + case Atom::CodeOld: + out() << "<p>For example, if you have code like</p>\n"; + // fallthrough + case Atom::CodeBad: + out() << "<pre class=\"cpp\">" + << trimmedTrailing(protectEnc(plainCode(indent(codeIndent,atom->string())))) + << "</pre>\n"; + break; + case Atom::DivLeft: + out() << "<div"; + if (!atom->string().isEmpty()) + out() << ' ' << atom->string(); + out() << '>'; + break; + case Atom::DivRight: + out() << "</div>"; + break; + case Atom::FootnoteLeft: + // ### For now + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + out() << "<!-- "; + break; + case Atom::FootnoteRight: + // ### For now + out() << "-->"; + break; + case Atom::FormatElse: + case Atom::FormatEndif: + case Atom::FormatIf: + break; + case Atom::FormattingLeft: + if (atom->string().startsWith("span ")) { + out() << '<' + atom->string() << '>'; + } + else + out() << formattingLeftMap()[atom->string()]; + 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())) { + out() << subscriptRegExp.cap(1) << "<sub>" + << subscriptRegExp.cap(2) << "</sub>"; + skipAhead = 1; + } + } + } + break; + case Atom::FormattingRight: + if (atom->string() == ATOM_FORMATTING_LINK) { + endLink(); + } + else if (atom->string().startsWith("span ")) { + out() << "</span>"; + } + else { + out() << formattingRightMap()[atom->string()]; + } + break; + case Atom::AnnotatedList: + { + QList<Node*> values = myTree->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 <QString, Node *> groups = myTree->groups(); + foreach (const QString &groupName, editionGroupMap[editionName]) { + QList<Node *> 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<const FakeNode *>(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); + } + } + else if (atom->string() == "relatedinline") { + const FakeNode *fake = static_cast<const FakeNode *>(relative); + if (fake && !fake->groupMembers().isEmpty()) { + // Reverse the list into the original scan order. + // Should be sorted. But on what? It may not be a + // regular class or page definition. + QList<const Node *> list; + foreach (const Node *node, fake->groupMembers()) + list.prepend(node); + foreach (const Node *node, list) + generateBody(node, marker); + } + } + 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<Section> sections; + QList<Section>::ConstIterator s; + + for (int i=0; i<LastSinceType; ++i) + sections.append(Section(sinceTitle(i),QString(),QString(),QString())); + + NodeMultiMap::const_iterator n = nsmap.value().constBegin(); + + while (n != nsmap.value().constEnd()) { + + const Node* node = n.value(); + switch (node->type()) { + 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<const FunctionNode*>(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. + */ + out() << "<ul>\n"; + s = sections.constBegin(); + while (s != sections.constEnd()) { + if (!(*s).members.isEmpty()) { + + out() << "<li>" + << "<a href=\"#" + << Doc::canonicalTitle((*s).name) + << "\">" + << (*s).name + << "</a></li>\n"; + } + ++s; + } + out() << "</ul>\n"; + + int idx = 0; + s = sections.constBegin(); + while (s != sections.constEnd()) { + if (!(*s).members.isEmpty()) { + out() << "<a name=\"" + << Doc::canonicalTitle((*s).name) + << "\"></a>\n"; + out() << "<h3>" << protectEnc((*s).name) << "</h3>\n"; + 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(); + out() << "<p>Class "; + + out() << "<a href=\"" + << linkForNode(pmap.key(), 0) + << "\">"; + QStringList pieces = fullName(pmap.key(), 0, marker).split("::"); + out() << protectEnc(pieces.last()); + out() << "</a>" << ":</p>\n"; + + generateSection(nlist, 0, marker, CodeMarker::Summary); + out() << "<br/>"; + ++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 (atom->type() == Atom::Image) + out() << "<p class=\"centerAlign\">"; + if (fileName.isEmpty()) { + out() << "<font color=\"red\">[Missing image " + << protectEnc(atom->string()) << "]</font>"; + } + else { + QString prefix = ""; + if (!baseDir().isEmpty()) + prefix = "../"; + out() << "<img src=\"" << protectEnc(prefix + fileName) << '"'; + if (!text.isEmpty()) + out() << " alt=\"" << protectEnc(text) << '"'; + else + out() << " alt=\"\""; + out() << " />"; + helpProjectWriter->addExtraFile(fileName); + if ((relative->type() == Node::Fake) && + (relative->subType() == Node::Example)) { + const ExampleNode* cen = static_cast<const ExampleNode*>(relative); + if (cen->imageFileName().isEmpty()) { + ExampleNode* en = const_cast<ExampleNode*>(cen); + en->setImageFileName(fileName); + } + } + } + if (atom->type() == Atom::Image) + out() << "</p>"; + } + break; + case Atom::ImageText: + break; + case Atom::ImportantLeft: + out() << "<p>"; + out() << formattingLeftMap()[ATOM_FORMATTING_BOLD]; + out() << "Important: "; + out() << formattingRightMap()[ATOM_FORMATTING_BOLD]; + break; + case Atom::ImportantRight: + out() << "</p>"; + break; + case Atom::NoteLeft: + out() << "<p>"; + out() << formattingLeftMap()[ATOM_FORMATTING_BOLD]; + out() << "Note: "; + out() << formattingRightMap()[ATOM_FORMATTING_BOLD]; + break; + case Atom::NoteRight: + out() << "</p>"; + break; + case Atom::LegaleseLeft: + out() << "<div class=\"LegaleseLeft\">"; + break; + case Atom::LegaleseRight: + out() << "</div>"; + break; + case Atom::LineBreak: + out() << "<br/>"; + break; + case Atom::Link: + { + const Node *node = 0; + QString myLink = getLink(atom, relative, marker, &node); + if (myLink.isEmpty()) { + myLink = getDisambiguationLink(atom, marker); + if (myLink.isEmpty()) { + relative->doc().location().warning(tr("Can't create link to '%1'") + .arg(atom->string())); + } + else + node = 0; + } + beginLink(myLink, node, relative, marker); + skipAhead = 1; + } + break; + case Atom::LinkNode: + { + const Node *node = CodeMarker::nodeForString(atom->string()); + beginLink(linkForNode(node, relative), node, relative, marker); + skipAhead = 1; + } + break; + case Atom::ListLeft: + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + if (atom->string() == ATOM_LIST_BULLET) { + out() << "<ul>\n"; + } + else if (atom->string() == ATOM_LIST_TAG) { + out() << "<dl>\n"; + } + else if (atom->string() == ATOM_LIST_VALUE) { + threeColumnEnumValueTable = isThreeColumnEnumValueTable(atom); + if (threeColumnEnumValueTable) { + out() << "<table class=\"valuelist\">"; + if (++numTableRows % 2 == 1) + out() << "<tr valign=\"top\" class=\"odd\">"; + else + out() << "<tr valign=\"top\" class=\"even\">"; + + out() << "<th class=\"tblConst\">Constant</th>" + << "<th class=\"tblval\">Value</th>" + << "<th class=\"tbldscr\">Description</th></tr>\n"; + } + else { + out() << "<table class=\"valuelist\">" + << "<tr><th class=\"tblConst\">Constant</th><th class=\"tblVal\">Value</th></tr>\n"; + } + } + else { + out() << "<ol class="; + if (atom->string() == ATOM_LIST_UPPERALPHA) { + out() << "\"A\""; + } /* why type? changed to */ + else if (atom->string() == ATOM_LIST_LOWERALPHA) { + out() << "\"a\""; + } + else if (atom->string() == ATOM_LIST_UPPERROMAN) { + out() << "\"I\""; + } + else if (atom->string() == ATOM_LIST_LOWERROMAN) { + out() << "\"i\""; + } + else { // (atom->string() == ATOM_LIST_NUMERIC) + out() << "\"1\""; + } + if (atom->next() != 0 && atom->next()->string().toInt() != 1) + out() << " start=\"" << atom->next()->string() << '"'; + out() << ">\n"; + } + break; + case Atom::ListItemNumber: + break; + case Atom::ListTagLeft: + if (atom->string() == ATOM_LIST_TAG) { + out() << "<dt>"; + } + else { // (atom->string() == ATOM_LIST_VALUE) + // ### Trenton + + out() << "<tr><td class=\"topAlign\"><tt>" + << protectEnc(plainCode(marker->markedUpEnumValue(atom->next()->string(), + relative))) + << "</tt></td><td class=\"topAlign\">"; + + QString itemValue; + if (relative->type() == Node::Enum) { + const EnumNode *enume = static_cast<const EnumNode *>(relative); + itemValue = enume->itemValue(atom->next()->string()); + } + + if (itemValue.isEmpty()) + out() << '?'; + else + out() << "<tt>" << protectEnc(itemValue) << "</tt>"; + + skipAhead = 1; + } + break; + case Atom::ListTagRight: + if (atom->string() == ATOM_LIST_TAG) + out() << "</dt>\n"; + break; + case Atom::ListItemLeft: + if (atom->string() == ATOM_LIST_TAG) { + out() << "<dd>"; + } + else if (atom->string() == ATOM_LIST_VALUE) { + if (threeColumnEnumValueTable) { + out() << "</td><td class=\"topAlign\">"; + if (matchAhead(atom, Atom::ListItemRight)) + out() << " "; + } + } + else { + out() << "<li>"; + } + if (matchAhead(atom, Atom::ParaLeft)) + skipAhead = 1; + break; + case Atom::ListItemRight: + if (atom->string() == ATOM_LIST_TAG) { + out() << "</dd>\n"; + } + else if (atom->string() == ATOM_LIST_VALUE) { + out() << "</td></tr>\n"; + } + else { + out() << "</li>\n"; + } + break; + case Atom::ListRight: + if (atom->string() == ATOM_LIST_BULLET) { + out() << "</ul>\n"; + } + else if (atom->string() == ATOM_LIST_TAG) { + out() << "</dl>\n"; + } + else if (atom->string() == ATOM_LIST_VALUE) { + out() << "</table>\n"; + } + else { + out() << "</ol>\n"; + } + break; + case Atom::Nop: + break; + case Atom::ParaLeft: + out() << "<p>"; + in_para = true; + break; + case Atom::ParaRight: + endLink(); + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + //if (!matchAhead(atom, Atom::ListItemRight) && !matchAhead(atom, Atom::TableItemRight)) + // out() << "</p>\n"; + break; + case Atom::QuotationLeft: + out() << "<blockquote>"; + break; + case Atom::QuotationRight: + out() << "</blockquote>\n"; + break; + case Atom::RawString: + out() << atom->string(); + break; + case Atom::SectionLeft: + out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) + << "\"></a>" << divNavTop << '\n'; + break; + case Atom::SectionRight: + break; + case Atom::SectionHeadingLeft: + out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + QLatin1Char('>'); + inSectionHeading = true; + break; + case Atom::SectionHeadingRight: + out() << "</h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">\n"; + inSectionHeading = false; + break; + case Atom::SidebarLeft: + break; + case Atom::SidebarRight: + break; + case Atom::String: + if (inLink && !inContents && !inSectionHeading) { + generateLink(atom, relative, marker); + } + else { + out() << protectEnc(atom->string()); + } + break; + case Atom::TableLeft: + { + QString p1, p2; + QString attr = "generic"; + QString width; + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + if (atom->count() > 0) { + p1 = atom->string(0); + if (atom->count() > 1) + p2 = atom->string(1); + } + if (!p1.isEmpty()) { + if (p1 == "borderless") + attr = p1; + else if (p1.contains("%")) + width = p1; + } + if (!p2.isEmpty()) { + if (p2 == "borderless") + attr = p2; + else if (p2.contains("%")) + width = p2; + } + out() << "<table class=\"" << attr << "\""; + if (!width.isEmpty()) + out() << " width=\"" << width << "\">"; + out() << "\n "; + numTableRows = 0; + } + break; + case Atom::TableRight: + out() << "</table>\n"; + break; + case Atom::TableHeaderLeft: + out() << "<thead><tr class=\"qt-style\">"; + inTableHeader = true; + break; + case Atom::TableHeaderRight: + out() << "</tr>"; + if (matchAhead(atom, Atom::TableHeaderLeft)) { + skipAhead = 1; + out() << "\n<tr class=\"qt-style\">"; + } + else { + out() << "</thead>\n"; + inTableHeader = false; + } + break; + case Atom::TableRowLeft: + if (!atom->string().isEmpty()) + out() << "<tr " << atom->string() << '>'; + else if (++numTableRows % 2 == 1) + out() << "<tr valign=\"top\" class=\"odd\">"; + else + out() << "<tr valign=\"top\" class=\"even\">"; + break; + case Atom::TableRowRight: + out() << "</tr>\n"; + break; + case Atom::TableItemLeft: + { + if (inTableHeader) + out() << "<th "; + else + out() << "<td "; + + for (int i=0; i<atom->count(); ++i) { + if (i > 0) + out() << ' '; + QString p = atom->string(i); + if (p.contains('=')) { + out() << p; + } + else { + QStringList spans = p.split(","); + if (spans.size() == 2) { + if (spans.at(0) != "1") + out() << " colspan=\"" << spans.at(0) << '"'; + if (spans.at(1) != "1") + out() << " rowspan=\"" << spans.at(1) << '"'; + } + } + } + if (inTableHeader) + out() << '>'; + else { + out() << '>'; + //out() << "><p>"; + } + if (matchAhead(atom, Atom::ParaLeft)) + skipAhead = 1; + } + break; + case Atom::TableItemRight: + if (inTableHeader) + out() << "</th>"; + else { + out() << "</td>"; + //out() << "</p></td>"; + } + if (matchAhead(atom, Atom::ParaLeft)) + skipAhead = 1; + break; + case Atom::TableOfContents: + break; + case Atom::Target: + out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>"; + break; + case Atom::UnhandledFormat: + out() << "<b class=\"redFont\"><Missing HTML></b>"; + break; + case Atom::UnknownCommand: + out() << "<b class=\"redFont\"><code>\\" << protectEnc(atom->string()) + << "</code></b>"; + break; +#ifdef QDOC_QML + case Atom::QmlText: + case Atom::EndQmlText: + // don't do anything with these. They are just tags. + break; +#endif + default: + unknownAtom(atom); + } + return skipAhead; +} + +/*! + Generate a reference page for a C++ class. + */ +void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, + CodeMarker *marker) +{ + QList<Section> sections; + QList<Section>::ConstIterator s; + + const ClassNode *classe = 0; + + QString title; + QString rawTitle; + QString fullTitle; + if (inner->type() == Node::Namespace) { + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Namespace"; + } + else if (inner->type() == Node::Class) { + classe = static_cast<const ClassNode *>(inner); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Class"; + } + + Text subtitleText; + if (rawTitle != fullTitle) + subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" + << Atom(Atom::LineBreak); + + generateHeader(title, inner, marker); + sections = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); + generateTableOfContents(inner,marker,§ions); + generateTitle(title, subtitleText, SmallSubTitle, inner, marker); + generateBrief(inner, marker); + generateIncludes(inner, marker); + generateStatus(inner, marker); + if (classe) { + generateInherits(classe, marker); + generateInheritedBy(classe, marker); + if (classe->qmlElement() != 0) + generateInstantiatedBy(classe,marker); + } + generateThreadSafeness(inner, marker); + generateSince(inner, marker); + + out() << "<ul>\n"; + + QString membersLink = generateListOfAllMemberFile(inner, marker); + if (!membersLink.isEmpty()) + out() << "<li><a href=\"" << membersLink << "\">" + << "List of all members, including inherited members</a></li>\n"; + + QString obsoleteLink = generateLowStatusMemberFile(inner, + marker, + CodeMarker::Obsolete); + if (!obsoleteLink.isEmpty()) + out() << "<li><a href=\"" << obsoleteLink << "\">" + << "Obsolete members</a></li>\n"; + + QString compatLink = generateLowStatusMemberFile(inner, + marker, + CodeMarker::Compat); + if (!compatLink.isEmpty()) + out() << "<li><a href=\"" << compatLink << "\">" + << "Qt 3 support members</a></li>\n"; + + out() << "</ul>\n"; + + bool needOtherSection = false; + + /* + sections is built above for the call to generateTableOfContents(). + */ + s = sections.begin(); + while (s != sections.end()) { + if (s->members.isEmpty() && s->reimpMembers.isEmpty()) { + if (!s->inherited.isEmpty()) + needOtherSection = true; + } + else { + if (!s->members.isEmpty()) { + // out() << "<hr />\n"; + out() << "<a name=\"" + << registerRef((*s).name.toLower()) + << "\"></a>" << divNavTop << "\n"; + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + generateSection(s->members, inner, marker, CodeMarker::Summary); + } + if (!s->reimpMembers.isEmpty()) { + QString name = QString("Reimplemented ") + (*s).name; + // out() << "<hr />\n"; + out() << "<a name=\"" + << registerRef(name.toLower()) + << "\"></a>" << divNavTop << "\n"; + out() << "<h2>" << protectEnc(name) << "</h2>\n"; + generateSection(s->reimpMembers, inner, marker, CodeMarker::Summary); + } + + if (!s->inherited.isEmpty()) { + out() << "<ul>\n"; + generateSectionInheritedList(*s, inner, marker); + out() << "</ul>\n"; + } + } + ++s; + } + + if (needOtherSection) { + out() << "<h3>Additional Inherited Members</h3>\n" + "<ul>\n"; + + s = sections.begin(); + while (s != sections.end()) { + if (s->members.isEmpty() && !s->inherited.isEmpty()) + generateSectionInheritedList(*s, inner, marker); + ++s; + } + out() << "</ul>\n"; + } + + out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; + + if (!inner->doc().isEmpty()) { + generateExtractionMark(inner, DetailedDescriptionMark); + //out() << "<hr />\n" + out() << "<div class=\"descr\">\n" // QTBUG-9504 + << "<h2>" << "Detailed Description" << "</h2>\n"; + generateBody(inner, marker); + out() << "</div>\n"; // QTBUG-9504 + generateAlsoList(inner, marker); + generateMaintainerList(inner, marker); + generateExtractionMark(inner, EndMark); + } + + sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + //out() << "<hr />\n"; + if (!(*s).divClass.isEmpty()) + out() << "<div class=\"" << (*s).divClass << "\">\n"; // QTBUG-9504 + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + if ((*m)->access() != Node::Private) { // ### check necessary? + if ((*m)->type() != Node::Class) + generateDetailedMember(*m, inner, marker); + else { + out() << "<h3> class "; + generateFullName(*m, inner, marker); + out() << "</h3>"; + generateBrief(*m, marker, inner); + } + + QStringList names; + names << (*m)->name(); + if ((*m)->type() == Node::Function) { + const FunctionNode *func = reinterpret_cast<const FunctionNode *>(*m); + if (func->metaness() == FunctionNode::Ctor || + func->metaness() == FunctionNode::Dtor || + func->overloadNumber() != 1) + names.clear(); + } + else if ((*m)->type() == Node::Property) { + const PropertyNode *prop = reinterpret_cast<const PropertyNode *>(*m); + if (!prop->getters().isEmpty() && + !names.contains(prop->getters().first()->name())) + names << prop->getters().first()->name(); + if (!prop->setters().isEmpty()) + names << prop->setters().first()->name(); + if (!prop->resetters().isEmpty()) + names << prop->resetters().first()->name(); + } + else if ((*m)->type() == Node::Enum) { + const EnumNode *enume = reinterpret_cast<const EnumNode*>(*m); + if (enume->flagsType()) + names << enume->flagsType()->name(); + + foreach (const QString &enumName, + enume->doc().enumItemNames().toSet() - + enume->doc().omitEnumItemNames().toSet()) + names << plainCode(marker->markedUpEnumValue(enumName, + enume)); + } + } + ++m; + } + if (!(*s).divClass.isEmpty()) + out() << "</div>\n"; // QTBUG-9504 + ++s; + } + generateFooter(inner); +} + +/*! + We delayed generation of the disambiguation pages until now, after + all the other pages have been generated. We do this because we might + encounter a link command that tries to link to a target on a QML + component page, but the link doesn't specify the module identifer + for the component, and the component name without a module + identifier is ambiguous. When such a link is found, qdoc can't find + the target, so it appends the target to the NameCollisionNode. After + the tree has been traversed and all these ambiguous links have been + added to the name collision nodes, this function is called. The list + of collision nodes is traversed here, and the disambiguation page for + each collision is generated. The disambiguation page will not only + disambiguate links to the component pages, but it will also disambiguate + links to properties, section headers, etc. + */ +void HtmlGenerator::generateDisambiguationPages() +{ + if (collisionNodes.isEmpty()) + return; + for (int i=0; i<collisionNodes.size(); ++i) { + NameCollisionNode* ncn = collisionNodes.at(i); + ncn->clearCurrentChild(); + beginSubPage(ncn, PageGenerator::fileName(ncn)); + QString fullTitle = "Name Collision: " + ncn->fullTitle(); + QString htmlTitle = fullTitle; + CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath()); + if (ncn->isQmlNode()) { + // Replace the marker with a QML code marker. + if (ncn->isQmlNode()) + marker = CodeMarker::markerForLanguage(QLatin1String("QML")); + } + + generateHeader(htmlTitle, ncn, marker); + if (!fullTitle.isEmpty()) + out() << "<h1 class=\"title\">" << protectEnc(fullTitle) << "</h1>\n"; + const NodeList& nl = ncn->childNodes(); + NodeMap nm; + NodeList::ConstIterator it = nl.begin(); + while (it != nl.end()) { + QString t = (*it)->qmlModuleIdentifier() + " " + protectEnc(fullTitle); + nm.insertMulti(t,(*it)); + ++it; + } + generateAnnotatedList(ncn, marker, nm, true); + + const QMap<QString,QString>& targets = ncn->linkTargets(); + if (!targets.isEmpty()) { + QMap<QString,QString>::ConstIterator t = targets.begin(); + while (t != targets.end()) { + out() << "<a name=\"" << Doc::canonicalTitle(t.key()) << "\"></a>"; + out() << "<h2 class=\"title\">" << protectEnc(t.key()) << "</h2>\n"; + out() << "<ul>\n"; + it = nl.begin(); + while (it != nl.end()) { + InnerNode* n = static_cast<InnerNode*>(*it); + Node* p = n->findNode(t.key()); + if (p) { + QString link = linkForNode(p,0); + QString label = n->qmlModuleIdentifier() + "::" + n->name() + "::" + p->name(); + out() << "<li>"; + out() << "<a href=\"" << link << "\">"; + out() << protectEnc(label) << "</a>"; + out() << "</li>\n"; + } + ++it; + } + out() << "</ul>\n"; + ++t; + } + } + + generateFooter(ncn); + endSubPage(); + } +} + +/*! + Generate the HTML page for an entity that doesn't map + to any underlying parsable C++ class or QML component. + */ +void HtmlGenerator::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<const DitaMapNode*>(fake); + writeDitaMap(dmn); + return; + } + + SubTitleSize subTitleSize = LargeSubTitle; + QList<Section> sections; + QList<Section>::const_iterator s; + QString fullTitle = fake->fullTitle(); + QString htmlTitle = fullTitle; + + if (fake->subType() == Node::File && !fake->subTitle().isEmpty()) { + subTitleSize = SmallSubTitle; + htmlTitle += " (" + fake->subTitle() + ")"; + } + else if (fake->subType() == Node::QmlBasicType) { + fullTitle = "QML Basic Type: " + fullTitle; + htmlTitle = fullTitle; + + // Replace the marker with a QML code marker. + marker = CodeMarker::markerForLanguage(QLatin1String("QML")); + } + + generateHeader(htmlTitle, fake, marker); + /* + Generate the TOC for the new doc format. + Don't generate a TOC for the home page. + */ + const QmlClassNode* qml_cn = 0; + if (fake->subType() == Node::QmlClass) { + qml_cn = static_cast<const QmlClassNode*>(fake); + sections = marker->qmlSections(qml_cn,CodeMarker::Summary); + generateTableOfContents(fake,marker,§ions); + + // Replace the marker with a QML code marker. + marker = CodeMarker::markerForLanguage(QLatin1String("QML")); + } + else if (fake->subType() != Node::Collision && fake->name() != QString("index.html")) + generateTableOfContents(fake,marker,0); + + generateTitle(fullTitle, + Text() << fake->subTitle(), + subTitleSize, + fake, + marker); + + if (fake->subType() == Node::Module) { + // Generate brief text and status for modules. + generateBrief(fake, marker); + generateStatus(fake, marker); + generateSince(fake, marker); + + if (moduleNamespaceMap.contains(fake->name())) { + out() << "<a name=\"" << registerRef("namespaces") << "\"></a>" << divNavTop << '\n'; + out() << "<h2>Namespaces</h2>\n"; + generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]); + } + if (moduleClassMap.contains(fake->name())) { + out() << "<a name=\"" << registerRef("classes") << "\"></a>" << divNavTop << '\n'; + out() << "<h2>Classes</h2>\n"; + generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]); + } + } + else if (fake->subType() == Node::HeaderFile) { + // Generate brief text and status for modules. + generateBrief(fake, marker); + generateStatus(fake, marker); + generateSince(fake, marker); + + out() << "<ul>\n"; + + QString membersLink = generateListOfAllMemberFile(fake, marker); + if (!membersLink.isEmpty()) + out() << "<li><a href=\"" << membersLink << "\">" + << "List of all members, including inherited members</a></li>\n"; + + QString obsoleteLink = generateLowStatusMemberFile(fake, + marker, + CodeMarker::Obsolete); + if (!obsoleteLink.isEmpty()) + out() << "<li><a href=\"" << obsoleteLink << "\">" + << "Obsolete members</a></li>\n"; + + QString compatLink = generateLowStatusMemberFile(fake, + marker, + CodeMarker::Compat); + if (!compatLink.isEmpty()) + out() << "<li><a href=\"" << compatLink << "\">" + << "Qt 3 support members</a></li>\n"; + + out() << "</ul>\n"; + } + else if (fake->subType() == Node::QmlClass) { + const_cast<FakeNode*>(fake)->setCurrentChild(); + const ClassNode* cn = qml_cn->classNode(); + generateBrief(qml_cn, marker); + generateQmlInherits(qml_cn, marker); + generateQmlInheritedBy(qml_cn, marker); + generateQmlInstantiates(qml_cn, marker); + generateSince(qml_cn, marker); + + QString allQmlMembersLink = generateAllQmlMembersFile(qml_cn, marker); + if (!allQmlMembersLink.isEmpty()) { + out() << "<ul>\n"; + out() << "<li><a href=\"" << allQmlMembersLink << "\">" + << "List of all members, including inherited members</a></li>\n"; + out() << "</ul>\n"; + } + + s = sections.begin(); + while (s != sections.end()) { + out() << "<a name=\"" << registerRef((*s).name.toLower()) + << "\"></a>" << divNavTop << "\n"; + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + generateQmlSummary(*s,fake,marker); + ++s; + } + + generateExtractionMark(fake, DetailedDescriptionMark); + out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; + out() << "<h2>" << "Detailed Description" << "</h2>\n"; + generateBody(fake, marker); + if (cn) + generateQmlText(cn->doc().body(), cn, marker, fake->name()); + generateAlsoList(fake, marker); + generateExtractionMark(fake, EndMark); + //out() << "<hr />\n"; + + sections = marker->qmlSections(qml_cn,CodeMarker::Detailed); + s = sections.begin(); + while (s != sections.end()) { + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + generateDetailedQmlMember(*m, fake, marker); + out() << "<br/>\n"; + ++m; + } + ++s; + } + generateFooter(fake); + const_cast<FakeNode*>(fake)->clearCurrentChild(); + return; + } + + sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + out() << "<a name=\"" << registerRef((*s).name) << "\"></a>" << divNavTop << '\n'; + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + generateSectionList(*s, fake, marker, CodeMarker::Summary); + ++s; + } + + Text brief = fake->doc().briefText(); + if (fake->subType() == Node::Module && !brief.isEmpty()) { + generateExtractionMark(fake, DetailedDescriptionMark); + out() << "<a name=\"" << registerRef("details") << "\"></a>" << divNavTop << '\n'; + out() << "<div class=\"descr\">\n"; // QTBUG-9504 + out() << "<h2>" << "Detailed Description" << "</h2>\n"; + } + else { + generateExtractionMark(fake, DetailedDescriptionMark); + out() << "<div class=\"descr\"> <a name=\"" << registerRef("details") << "\"></a>\n"; // QTBUG-9504 + } + + generateBody(fake, marker); + out() << "</div>\n"; // QTBUG-9504 + generateAlsoList(fake, marker); + generateExtractionMark(fake, EndMark); + + if ((fake->subType() == Node::Group) && !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); + } + else 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); + } + + sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + //out() << "<hr />\n"; + out() << "<h2>" << protectEnc((*s).name) << "</h2>\n"; + + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + generateDetailedMember(*m, fake, marker); + ++m; + } + ++s; + } + generateFooter(fake); +} + +/*! + Returns "html" for this subclass of Generator. + */ +QString HtmlGenerator::fileExtension(const Node * /* node */) const +{ + return "html"; +} + +/*! + Output breadcrumb list in the html file. + */ +void HtmlGenerator::generateBreadCrumbs(const QString &title, + const Node *node, + CodeMarker *marker) +{ + if (noBreadCrumbs) + return; + + Text breadcrumbs; + if (node->type() == Node::Class) { + const ClassNode *cn = static_cast<const ClassNode *>(node); + QString name = node->moduleName(); + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("All Modules")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Modules")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + if (!name.isEmpty()) + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::AutoLink, name) + << Atom(Atom::ListItemRight); + if (!cn->name().isEmpty()) + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(cn->name())) + << Atom(Atom::ListItemRight); + } + else if (node->type() == Node::Fake) { + const FakeNode* fn = static_cast<const FakeNode*>(node); + if (node->subType() == Node::Module) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("All Modules")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Modules")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + QString name = node->name(); + if (!name.isEmpty()) + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(name)) + << Atom(Atom::ListItemRight); + } + else if (node->subType() == Node::Group) { + if (fn->name() == QString("modules")) + breadcrumbs << Atom(Atom::String, QLatin1String("Modules")); + else + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + else if (node->subType() == Node::Page) { + if (fn->name() == QString("qdeclarativeexamples.html")) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("Qt Examples")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Examples")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos")) + << Atom(Atom::ListItemRight); + } + else if (fn->name().startsWith("examples-")) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("Qt Examples")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Examples")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + else if (fn->name() == QString("namespaces.html")) + breadcrumbs << Atom(Atom::String, QLatin1String("Namespaces")); + else + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + else if (node->subType() == Node::QmlClass) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::AutoLink, QLatin1String("Basic QML Types")) + << Atom(Atom::ListItemRight); + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + else if (node->subType() == Node::Example) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("Qt Examples")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Examples")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + QStringList sl = fn->name().split('/'); + if (sl.contains("declarative")) + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::AutoLink, QLatin1String("QML Examples & Demos")) + << Atom(Atom::ListItemRight); + else { + QString name = protectEnc("examples-" + sl.at(0) + ".html"); // this generates an empty link + QString t = CodeParser::titleFromName(name); + } + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + } + else if (node->type() == Node::Namespace) { + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::Link, QLatin1String("All Namespaces")) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, QLatin1String("Namespaces")) + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom(Atom::ListItemRight); + breadcrumbs << Atom(Atom::ListItemLeft) + << Atom(Atom::String, protectEnc(title)) + << Atom(Atom::ListItemRight); + } + + generateText(breadcrumbs, node, marker); +} + +void HtmlGenerator::generateHeader(const QString& title, + const Node *node, + CodeMarker *marker) +{ + out() << QString("<?xml version=\"1.0\" encoding=\"%1\"?>\n").arg(outputEncoding); + out() << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"; + out() << QString("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"%1\" lang=\"%1\">\n").arg(naturalLanguage); + out() << "<head>\n"; + out() << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"; + if (node && !node->doc().location().isEmpty()) + out() << "<!-- " << node->doc().location().fileName() << " -->\n"; + + QString shortVersion = myTree->version(); + if (shortVersion.count(QChar('.')) == 2) + shortVersion.truncate(shortVersion.lastIndexOf(QChar('.'))); + if (!project.isEmpty()) + shortVersion = project + QLatin1String(" ") + shortVersion + QLatin1String(": "); + else + shortVersion = QLatin1String("Qt ") + shortVersion + QLatin1String(": "); + + // Generating page title + out() << " <title>" << shortVersion << protectEnc(title) << "</title>\n"; + + // Include style sheet and script links. + out() << headerStyles; + out() << headerScripts; + out() << endHeader; + +#ifdef GENERATE_MAC_REFS + if (mainPage) + generateMacRef(node, marker); +#endif + + out() << QString(postHeader).replace("\\" + COMMAND_VERSION, myTree->version()); + generateBreadCrumbs(title,node,marker); + out() << QString(postPostHeader).replace("\\" + COMMAND_VERSION, myTree->version()); + + navigationLinks.clear(); + + if (node && !node->links().empty()) { + QPair<QString,QString> linkPair; + QPair<QString,QString> anchorPair; + const Node *linkNode; + + if (node->links().contains(Node::PreviousLink)) { + linkPair = node->links()[Node::PreviousLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + if (!linkNode || linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + + out() << " <link rel=\"prev\" href=\"" + << anchorPair.first << "\" />\n"; + + navigationLinks += "[Previous: <a href=\"" + anchorPair.first + "\">"; + if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) + navigationLinks += protect(anchorPair.second); + else + navigationLinks += protect(linkPair.second); + navigationLinks += "</a>]\n"; + } + if (node->links().contains(Node::NextLink)) { + linkPair = node->links()[Node::NextLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + if (!linkNode || linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + + out() << " <link rel=\"next\" href=\"" + << anchorPair.first << "\" />\n"; + + navigationLinks += "[Next: <a href=\"" + anchorPair.first + "\">"; + if (linkPair.first == linkPair.second && !anchorPair.second.isEmpty()) + navigationLinks += protect(anchorPair.second); + else + navigationLinks += protect(linkPair.second); + navigationLinks += "</a>]\n"; + } + if (node->links().contains(Node::StartLink)) { + linkPair = node->links()[Node::StartLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + if (!linkNode || linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + out() << " <link rel=\"start\" href=\"" + << anchorPair.first << "\" />\n"; + } + } + + if (node && !node->links().empty()) + out() << "<p class=\"naviNextPrevious headerNavi\">\n" << navigationLinks << "</p><p/>\n"; +} + +void HtmlGenerator::generateTitle(const QString& title, + const Text &subTitle, + SubTitleSize subTitleSize, + const Node *relative, + CodeMarker *marker) +{ + if (!title.isEmpty()) + out() << "<h1 class=\"title\">" << protectEnc(title) << "</h1>\n"; + if (!subTitle.isEmpty()) { + out() << "<span"; + if (subTitleSize == SmallSubTitle) + out() << " class=\"small-subtitle\">"; + else + out() << " class=\"subtitle\">"; + generateText(subTitle, relative, marker); + out() << "</span>\n"; + } +} + +void HtmlGenerator::generateFooter(const Node *node) +{ + if (node && !node->links().empty()) + out() << "<p class=\"naviNextPrevious footerNavi\">\n" << navigationLinks << "</p>\n"; + + out() << QString(footer).replace("\\" + COMMAND_VERSION, myTree->version()) + << QString(address).replace("\\" + COMMAND_VERSION, myTree->version()); + + out() << "</body>\n"; + out() << "</html>\n"; +} + +void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, + const Node *relative) +{ + Text brief = node->doc().briefText(); + if (!brief.isEmpty()) { + generateExtractionMark(node, BriefMark); + out() << "<p>"; + generateText(brief, node, marker); + + if (!relative || node == relative) + out() << " <a href=\"#"; + else + out() << " <a href=\"" << linkForNode(node, relative) << '#'; + out() << registerRef("details") << "\">More...</a></p>\n"; + + + generateExtractionMark(node, EndMark); + } +} + +void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker) +{ + if (!inner->includes().isEmpty()) { + out() << "<pre class=\"cpp\">" + << trimmedTrailing(highlightedCode(indent(codeIndent, + marker->markedUpIncludes(inner->includes())), + marker,inner)) + << "</pre>"; + } +} + +/*! + Revised for the new doc format. + Generates a table of contents beginning at \a node. + */ +void HtmlGenerator::generateTableOfContents(const Node *node, + CodeMarker *marker, + QList<Section>* sections) +{ + QList<Atom*> toc; + if (node->doc().hasTableOfContents()) + toc = node->doc().tableOfContents(); + if (toc.isEmpty() && !sections && (node->subType() != Node::Module)) + return; + + QStringList sectionNumber; + int detailsBase = 0; + + // disable nested links in table of contents + inContents = true; + inLink = true; + + out() << "<div class=\"toc\">\n"; + out() << "<h3><a name=\"toc\">Contents</a></h3>\n"; + sectionNumber.append("1"); + out() << "<ul>\n"; + + if (node->subType() == Node::Module) { + if (moduleNamespaceMap.contains(node->name())) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><a href=\"#" + << registerRef("namespaces") + << "\">Namespaces</a></li>\n"; + } + if (moduleClassMap.contains(node->name())) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><a href=\"#" + << registerRef("classes") + << "\">Classes</a></li>\n"; + } + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><a href=\"#" + << registerRef("details") + << "\">Detailed Description</a></li>\n"; + for (int i = 0; i < toc.size(); ++i) { + if (toc.at(i)->string().toInt() == 1) { + detailsBase = 1; + break; + } + } + } + else if (sections && ((node->type() == Node::Class) || + (node->type() == Node::Namespace) || + (node->subType() == Node::QmlClass))) { + QList<Section>::ConstIterator s = sections->begin(); + while (s != sections->end()) { + if (!s->members.isEmpty() || !s->reimpMembers.isEmpty()) { + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><a href=\"#" + << registerRef((*s).pluralMember) + << "\">" << (*s).name + << "</a></li>\n"; + } + ++s; + } + out() << "<li class=\"level" + << sectionNumber.size() + << "\"><a href=\"#" + << registerRef("details") + << "\">Detailed Description</a></li>\n"; + for (int i = 0; i < toc.size(); ++i) { + if (toc.at(i)->string().toInt() == 1) { + detailsBase = 1; + break; + } + } + } + + for (int i = 0; i < toc.size(); ++i) { + Atom *atom = toc.at(i); + int nextLevel = atom->string().toInt() + detailsBase; + if (nextLevel >= 0) { + if (sectionNumber.size() < nextLevel) { + do { + sectionNumber.append("1"); + } while (sectionNumber.size() < nextLevel); + } + else { + while (sectionNumber.size() > nextLevel) { + sectionNumber.removeLast(); + } + sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); + } + } + int numAtoms; + Text headingText = Text::sectionHeading(atom); + QString s = headingText.toString(); + out() << "<li class=\"level" + << sectionNumber.size() + << "\">"; + out() << "<a href=\"" + << '#' + << Doc::canonicalTitle(s) + << "\">"; + generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); + out() << "</a></li>\n"; + } + while (!sectionNumber.isEmpty()) { + sectionNumber.removeLast(); + } + out() << "</ul>\n"; + out() << "</div>\n"; + inContents = false; + inLink = false; +} + +QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, + CodeMarker *marker) +{ + QList<Section> sections; + QList<Section>::ConstIterator s; + + sections = marker->sections(inner, + CodeMarker::Subpage, + CodeMarker::Okay); + if (sections.isEmpty()) + return QString(); + + QString fileName = fileBase(inner) + "-members." + fileExtension(inner); + beginSubPage(inner, fileName); + QString title = "List of All Members for " + inner->name(); + generateHeader(title, inner, marker); + generateTitle(title, Text(), SmallSubTitle, inner, marker); + out() << "<p>This is the complete list of members for "; + generateFullName(inner, 0, marker); + out() << ", including inherited members.</p>\n"; + + Section section = sections.first(); + generateSectionList(section, 0, marker, CodeMarker::Subpage); + + generateFooter(); + endSubPage(); + return fileName; +} + +/*! + This function creates an html page on which are listed all + the members of QML class \a qml_cn, including the inherited + members. The \a marker is used for formatting stuff. + */ +QString HtmlGenerator::generateAllQmlMembersFile(const QmlClassNode* qml_cn, + CodeMarker* marker) +{ + QList<Section> sections; + QList<Section>::ConstIterator s; + + sections = marker->qmlSections(qml_cn,CodeMarker::Subpage); + if (sections.isEmpty()) + return QString(); + + QString fileName = fileBase(qml_cn) + "-members." + fileExtension(qml_cn); + beginSubPage(qml_cn, fileName); + QString title = "List of All Members for " + qml_cn->name(); + generateHeader(title, qml_cn, marker); + generateTitle(title, Text(), SmallSubTitle, qml_cn, marker); + out() << "<p>This is the complete list of members for "; + generateFullName(qml_cn, 0, marker); + out() << ", including inherited members.</p>\n"; + + Section section = sections.first(); + generateSectionList(section, 0, marker, CodeMarker::Subpage); + + generateFooter(); + endSubPage(); + return fileName; +} + +QString HtmlGenerator::generateLowStatusMemberFile(const InnerNode *inner, + CodeMarker *marker, + CodeMarker::Status status) +{ + QList<Section> sections = marker->sections(inner, + CodeMarker::Summary, + status); + QMutableListIterator<Section> j(sections); + while (j.hasNext()) { + if (j.next().members.size() == 0) + j.remove(); + } + if (sections.isEmpty()) + return QString(); + + int i; + + QString title; + QString fileName; + + if (status == CodeMarker::Compat) { + title = "Qt 3 Support Members for " + inner->name(); + fileName = fileBase(inner) + "-qt3." + fileExtension(inner); + } + else { + title = "Obsolete Members for " + inner->name(); + fileName = fileBase(inner) + "-obsolete." + fileExtension(inner); + } + + beginSubPage(inner, fileName); + generateHeader(title, inner, marker); + generateTitle(title, Text(), SmallSubTitle, inner, marker); + + if (status == CodeMarker::Compat) { + out() << "<p><b>The following class members are part of the " + "<a href=\"qt3support.html\">Qt 3 support layer</a>.</b> " + "They are provided to help you port old code to Qt 4. We advise against " + "using them in new code.</p>\n"; + } + else { + out() << "<p><b>The following class members are obsolete.</b> " + << "They are provided to keep old source code working. " + << "We strongly advise against using them in new code.</p>\n"; + } + + out() << "<p><ul><li><a href=\"" + << linkForNode(inner, 0) << "\">" + << protectEnc(inner->name()) + << " class reference</a></li></ul></p>\n"; + + for (i = 0; i < sections.size(); ++i) { + out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n"; + generateSectionList(sections.at(i), inner, marker, CodeMarker::Summary); + } + + sections = marker->sections(inner, CodeMarker::Detailed, status); + for (i = 0; i < sections.size(); ++i) { + //out() << "<hr />\n"; + out() << "<h2>" << protectEnc(sections.at(i).name) << "</h2>\n"; + + NodeList::ConstIterator m = sections.at(i).members.begin(); + while (m != sections.at(i).members.end()) { + if ((*m)->access() != Node::Private) + generateDetailedMember(*m, inner, marker); + ++m; + } + } + + generateFooter(); + endSubPage(); + return fileName; +} + +void HtmlGenerator::generateClassHierarchy(const Node *relative, + CodeMarker *marker, + const QMap<QString,const Node*> &classMap) +{ + if (classMap.isEmpty()) + return; + + NodeMap topLevel; + NodeMap::ConstIterator c = classMap.begin(); + while (c != classMap.end()) { + const ClassNode *classe = static_cast<const ClassNode *>(*c); + if (classe->baseClasses().isEmpty()) + topLevel.insert(classe->name(), classe); + ++c; + } + + QStack<NodeMap > stack; + stack.push(topLevel); + + out() << "<ul>\n"; + while (!stack.isEmpty()) { + if (stack.top().isEmpty()) { + stack.pop(); + out() << "</ul>\n"; + } + else { + const ClassNode *child = + static_cast<const ClassNode *>(*stack.top().begin()); + out() << "<li>"; + generateFullName(child, relative, marker); + out() << "</li>\n"; + stack.top().erase(stack.top().begin()); + + NodeMap newTop; + foreach (const RelatedClass &d, child->derivedClasses()) { + if (d.access != Node::Private && !d.node->doc().isEmpty()) + newTop.insert(d.node->name(), d.node); + } + if (!newTop.isEmpty()) { + stack.push(newTop); + out() << "<ul>\n"; + } + } + } +} + +void HtmlGenerator::generateAnnotatedList(const Node *relative, + CodeMarker *marker, + const NodeMap &nodeMap, + bool allOdd) +{ + out() << "<table class=\"annotated\">\n"; + + int row = 0; + foreach (const QString &name, nodeMap.keys()) { + const Node *node = nodeMap[name]; + + if (node->status() == Node::Obsolete) + continue; + + if (allOdd || (++row % 2 == 1)) + out() << "<tr class=\"odd topAlign\">"; + else + out() << "<tr class=\"even topAlign\">"; + out() << "<td class=\"tblName\"><p>"; + generateFullName(node, relative, marker); + out() << "</p></td>"; + + if (!(node->type() == Node::Fake)) { + Text brief = node->doc().trimmedBriefText(name); + if (!brief.isEmpty()) { + out() << "<td class=\"tblDescr\"><p>"; + generateText(brief, node, marker); + out() << "</p></td>"; + } + } + else { + out() << "<td class=\"tblDescr\"><p>"; + out() << protectEnc(node->doc().briefText().toString()); + out() << "</p></td>"; + } + out() << "</tr>\n"; + } + out() << "</table>\n"; +} + +/*! + This function finds the common prefix of the names of all + the classes in \a classMap and then generates a compact + list of the class names alphabetized on the part of the + name not including the common prefix. You can tell the + function to use \a comonPrefix as the common prefix, but + normally you let it figure it out itself by looking at + the name of the first and last classes in \a classMap. + */ +void HtmlGenerator::generateCompactList(const Node *relative, + CodeMarker *marker, + const NodeMap &classMap, + bool includeAlphabet, + QString commonPrefix) +{ + const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' + + if (classMap.isEmpty()) + return; + + /* + If commonPrefix is not empty, then the caller knows what + the common prefix is and has passed it in, so just use that + one. But if the commonPrefix is empty (it normally is), then + compute a common prefix using this simple algorithm. Note we + assume the prefix length is 1, i.e. we will have a single + character as the common prefix. + */ + int commonPrefixLen = commonPrefix.length(); + if (commonPrefixLen == 0) { + QVector<int> count(26); + for (int i=0; i<26; ++i) + count[i] = 0; + + NodeMap::const_iterator iter = classMap.begin(); + while (iter != classMap.end()) { + if (!iter.key().contains("::")) { + QChar c = iter.key()[0]; + if ((c >= 'A') && (c <= 'Z')) { + int idx = c.unicode() - QChar('A').unicode(); + ++count[idx]; + } + } + ++iter; + } + int highest = 0; + int idx = -1; + for (int i=0; i<26; ++i) { + if (count[i] > highest) { + highest = count[i]; + idx = i; + } + } + idx += QChar('A').unicode(); + QChar common(idx); + commonPrefix = common; + commonPrefixLen = 1; + +#if 0 + /* + The algorithm below eventually failed, so it was replaced + with the simple (perhaps too simple) algorithm above. + + The caller didn't pass in a common prefix, so get the common + prefix by looking at the class names of the first and last + classes in the class map. Discard any namespace names and + just use the bare class names. For Qt, the prefix is "Q". + + Note that the algorithm used here to derive the common prefix + from the first and last classes in alphabetical order (QAccel + and QXtWidget in Qt 2.1), fails if either class name does not + begin with Q. + */ + QString first; + QString last; + NodeMap::const_iterator iter = classMap.begin(); + while (iter != classMap.end()) { + if (!iter.key().contains("::")) { + first = iter.key(); + break; + } + ++iter; + } + + if (first.isEmpty()) + first = classMap.begin().key(); + + iter = classMap.end(); + while (iter != classMap.begin()) { + --iter; + if (!iter.key().contains("::")) { + last = iter.key(); + break; + } + } + + if (last.isEmpty()) + last = classMap.begin().key(); + + if (classMap.size() > 1) { + while (commonPrefixLen < first.length() + 1 && + commonPrefixLen < last.length() + 1 && + first[commonPrefixLen] == last[commonPrefixLen]) + ++commonPrefixLen; + } + + commonPrefix = first.left(commonPrefixLen); +#endif + } + + /* + Divide the data into 37 paragraphs: 0, ..., 9, A, ..., Z, + underscore (_). QAccel will fall in paragraph 10 (A) and + QXtWidget in paragraph 33 (X). This is the only place where we + assume that NumParagraphs is 37. Each paragraph is a NodeMap. + */ + NodeMap paragraph[NumParagraphs+1]; + QString paragraphName[NumParagraphs+1]; + QSet<char> usedParagraphNames; + + NodeMap::ConstIterator c = classMap.begin(); + while (c != classMap.end()) { + QStringList pieces = c.key().split("::"); + QString key; + int idx = commonPrefixLen; + if (!pieces.last().startsWith(commonPrefix)) + idx = 0; + if (pieces.size() == 1) + key = pieces.last().mid(idx).toLower(); + else + key = pieces.last().toLower(); + + int paragraphNr = NumParagraphs - 1; + + if (key[0].digitValue() != -1) { + paragraphNr = key[0].digitValue(); + } + else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { + paragraphNr = 10 + key[0].unicode() - 'a'; + } + + paragraphName[paragraphNr] = key[0].toUpper(); + usedParagraphNames.insert(key[0].toLower().cell()); + paragraph[paragraphNr].insert(key, c.value()); + ++c; + } + + /* + Each paragraph j has a size: paragraph[j].count(). In the + discussion, we will assume paragraphs 0 to 5 will have sizes + 3, 1, 4, 1, 5, 9. + + We now want to compute the paragraph offset. Paragraphs 0 to 6 + start at offsets 0, 3, 4, 8, 9, 14, 23. + */ + int paragraphOffset[NumParagraphs + 1]; // 37 + 1 + paragraphOffset[0] = 0; + for (int i=0; i<NumParagraphs; i++) // i = 0..36 + paragraphOffset[i+1] = paragraphOffset[i] + paragraph[i].count(); + + /* + Output the alphabet as a row of links. + */ + if (includeAlphabet) { + out() << "<p class=\"centerAlign functionIndex\"><b>"; + for (int i = 0; i < 26; i++) { + QChar ch('a' + i); + if (usedParagraphNames.contains(char('a' + i))) + out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper()); + } + out() << "</b></p>\n"; + } + + /* + Output a <div> element to contain all the <dl> elements. + */ + out() << "<div class=\"flowListDiv\">\n"; + numTableRows = 0; + + int curParNr = 0; + int curParOffset = 0; + + for (int i=0; i<classMap.count(); i++) { + while ((curParNr < NumParagraphs) && + (curParOffset == paragraph[curParNr].count())) { + ++curParNr; + curParOffset = 0; + } + + /* + Starting a new paragraph means starting a new <dl>. + */ + if (curParOffset == 0) { + if (i > 0) + out() << "</dl>\n"; + if (++numTableRows % 2 == 1) + out() << "<dl class=\"flowList odd\">"; + else + out() << "<dl class=\"flowList even\">"; + out() << "<dt class=\"alphaChar\">"; + if (includeAlphabet) { + QChar c = paragraphName[curParNr][0].toLower(); + out() << QString("<a name=\"%1\"></a>").arg(c); + } + out() << "<b>" + << paragraphName[curParNr] + << "</b>"; + out() << "</dt>\n"; + } + + /* + Output a <dd> for the current offset in the current paragraph. + */ + out() << "<dd>"; + if ((curParNr < NumParagraphs) && + !paragraphName[curParNr].isEmpty()) { + NodeMap::Iterator it; + it = paragraph[curParNr].begin(); + for (int i=0; i<curParOffset; i++) + ++it; + + /* + Previously, we used generateFullName() for this, but we + require some special formatting. + */ + out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">"; + + QStringList pieces; + if (it.value()->subType() == Node::QmlClass) + pieces << it.value()->name(); + else + pieces = fullName(it.value(), relative, marker).split("::"); + out() << protectEnc(pieces.last()); + out() << "</a>"; + if (pieces.size() > 1) { + out() << " ("; + generateFullName(it.value()->parent(), relative, marker); + out() << ')'; + } + } + out() << "</dd>\n"; + curParOffset++; + } + if (classMap.count() > 0) + out() << "</dl>\n"; + + out() << "</div>\n"; +} + +void HtmlGenerator::generateFunctionIndex(const Node *relative, + CodeMarker *marker) +{ + out() << "<p class=\"centerAlign functionIndex\"><b>"; + for (int i = 0; i < 26; i++) { + QChar ch('a' + i); + out() << QString("<a href=\"#%1\">%2</a> ").arg(ch).arg(ch.toUpper()); + } + out() << "</b></p>\n"; + + char nextLetter = 'a'; + char currentLetter; + +#if 1 + out() << "<ul>\n"; +#endif + QMap<QString, NodeMap >::ConstIterator f = funcIndex.begin(); + while (f != funcIndex.end()) { +#if 1 + out() << "<li>"; +#else + out() << "<p>"; +#endif + out() << protectEnc(f.key()) << ':'; + + currentLetter = f.key()[0].unicode(); + while (islower(currentLetter) && currentLetter >= nextLetter) { + out() << QString("<a name=\"%1\"></a>").arg(nextLetter); + nextLetter++; + } + + NodeMap::ConstIterator s = (*f).begin(); + while (s != (*f).end()) { + out() << ' '; + generateFullName((*s)->parent(), relative, marker, *s); + ++s; + } +#if 1 + out() << "</li>"; +#else + out() << "</p>"; +#endif + out() << '\n'; + ++f; + } +#if 1 + out() << "</ul>\n"; +#endif +} + +void HtmlGenerator::generateLegaleseList(const Node *relative, + CodeMarker *marker) +{ + QMap<Text, const Node *>::ConstIterator it = legaleseTexts.begin(); + while (it != legaleseTexts.end()) { + Text text = it.key(); + //out() << "<hr />\n"; + generateText(text, relative, marker); + out() << "<ul>\n"; + do { + out() << "<li>"; + generateFullName(it.value(), relative, marker); + out() << "</li>\n"; + ++it; + } while (it != legaleseTexts.end() && it.key() == text); + out() << "</ul>\n"; + } +} + +void HtmlGenerator::generateQmlItem(const Node *node, + const Node *relative, + CodeMarker *marker, + bool summary) +{ + QString marked = marker->markedUpQmlItem(node,summary); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protectEnc(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i>"); + marked.replace("</@param>", "</i>"); + + if (summary) + marked.replace("@name>", "b>"); + + marked.replace("<@extra>", "<tt>"); + marked.replace("</@extra>", "</tt>"); + + if (summary) { + marked.remove("<@type>"); + marked.remove("</@type>"); + } + out() << highlightedCode(marked, marker, relative, false, node); +} + +void HtmlGenerator::generateOverviewList(const Node *relative, CodeMarker * /* marker */) +{ + QMap<const FakeNode *, QMap<QString, FakeNode *> > fakeNodeMap; + QMap<QString, const FakeNode *> groupTitlesMap; + QMap<QString, FakeNode *> uncategorizedNodeMap; + QRegExp singleDigit("\\b([0-9])\\b"); + + const NodeList children = myTree->root()->childNodes(); + foreach (Node *child, children) { + if (child->type() == Node::Fake && child != relative) { + FakeNode *fakeNode = static_cast<FakeNode *>(child); + + // Check whether the page is part of a group or is the group + // definition page. + QString group; + bool isGroupPage = false; + if (fakeNode->doc().metaCommandsUsed().contains("group")) { + group = fakeNode->doc().metaCommandArgs("group")[0]; + isGroupPage = true; + } + + // there are too many examples; they would clutter the list + if (fakeNode->subType() == Node::Example) + continue; + + // not interested either in individual (Qt Designer etc.) manual chapters + if (fakeNode->links().contains(Node::ContentsLink)) + continue; + + // Discard external nodes. + if (fakeNode->subType() == Node::ExternalPage) + continue; + + QString sortKey = fakeNode->fullTitle().toLower(); + if (sortKey.startsWith("the ")) + sortKey.remove(0, 4); + sortKey.replace(singleDigit, "0\\1"); + + if (!group.isEmpty()) { + if (isGroupPage) { + // If we encounter a group definition page, we add all + // the pages in that group to the list for that group. + foreach (Node *member, fakeNode->groupMembers()) { + if (member->type() != Node::Fake) + continue; + FakeNode *page = static_cast<FakeNode *>(member); + if (page) { + QString sortKey = page->fullTitle().toLower(); + if (sortKey.startsWith("the ")) + sortKey.remove(0, 4); + sortKey.replace(singleDigit, "0\\1"); + fakeNodeMap[const_cast<const FakeNode *>(fakeNode)].insert(sortKey, page); + groupTitlesMap[fakeNode->fullTitle()] = const_cast<const FakeNode *>(fakeNode); + } + } + } + else if (!isGroupPage) { + // If we encounter a page that belongs to a group then + // we add that page to the list for that group. + const FakeNode *groupNode = static_cast<const FakeNode *>(myTree->root()->findNode(group, Node::Fake)); + if (groupNode) + fakeNodeMap[groupNode].insert(sortKey, fakeNode); + //else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + }// else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + }// else + // uncategorizedNodeMap.insert(sortKey, fakeNode); + } + } + + // We now list all the pages found that belong to groups. + // If only certain pages were found for a group, but the definition page + // for that group wasn't listed, the list of pages will be intentionally + // incomplete. However, if the group definition page was listed, all the + // pages in that group are listed for completeness. + + if (!fakeNodeMap.isEmpty()) { + foreach (const QString &groupTitle, groupTitlesMap.keys()) { + const FakeNode *groupNode = groupTitlesMap[groupTitle]; + out() << QString("<h3><a href=\"%1\">%2</a></h3>\n").arg( + linkForNode(groupNode, relative)).arg( + protectEnc(groupNode->fullTitle())); + + if (fakeNodeMap[groupNode].count() == 0) + continue; + + out() << "<ul>\n"; + + foreach (const FakeNode *fakeNode, fakeNodeMap[groupNode]) { + QString title = fakeNode->fullTitle(); + if (title.startsWith("The ")) + title.remove(0, 4); + out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">" + << protectEnc(title) << "</a></li>\n"; + } + out() << "</ul>\n"; + } + } + + if (!uncategorizedNodeMap.isEmpty()) { + out() << QString("<h3>Miscellaneous</h3>\n"); + out() << "<ul>\n"; + foreach (const FakeNode *fakeNode, uncategorizedNodeMap) { + QString title = fakeNode->fullTitle(); + if (title.startsWith("The ")) + title.remove(0, 4); + out() << "<li><a href=\"" << linkForNode(fakeNode, relative) << "\">" + << protectEnc(title) << "</a></li>\n"; + } + out() << "</ul>\n"; + } +} + +void HtmlGenerator::generateSection(const NodeList& nl, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style) +{ + bool alignNames = true; + if (!nl.isEmpty()) { + bool twoColumn = false; + if (style == CodeMarker::Subpage) { + alignNames = false; + twoColumn = (nl.count() >= 16); + } + else if (nl.first()->type() == Node::Property) { + twoColumn = (nl.count() >= 5); + alignNames = false; + } + if (alignNames) { + out() << "<table class=\"alignedsummary\">\n"; + } + else { + if (twoColumn) + out() << "<table class=\"propsummary\">\n" + << "<tr><td class=\"topAlign\">"; + out() << "<ul>\n"; + } + + int i = 0; + NodeList::ConstIterator m = nl.begin(); + while (m != nl.end()) { + if ((*m)->access() == Node::Private) { + ++m; + continue; + } + + if (alignNames) { + out() << "<tr><td class=\"memItemLeft rightAlign topAlign\"> "; + } + else { + if (twoColumn && i == (int) (nl.count() + 1) / 2) + out() << "</ul></td><td class=\"topAlign\"><ul>\n"; + out() << "<li class=\"fn\">"; + } + + generateSynopsis(*m, relative, marker, style, alignNames); + if (alignNames) + out() << "</td></tr>\n"; + else + out() << "</li>\n"; + i++; + ++m; + } + if (alignNames) + out() << "</table>\n"; + else { + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table>\n"; + } + } +} + +void HtmlGenerator::generateSectionList(const Section& section, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style) +{ + bool alignNames = true; + if (!section.members.isEmpty()) { + bool twoColumn = false; + if (style == CodeMarker::Subpage) { + alignNames = false; + twoColumn = (section.members.count() >= 16); + } + else if (section.members.first()->type() == Node::Property) { + twoColumn = (section.members.count() >= 5); + alignNames = false; + } + if (alignNames) { + out() << "<table class=\"alignedsummary\">\n"; + } + else { + if (twoColumn) + out() << "<table class=\"propsummary\">\n" + << "<tr><td class=\"topAlign\">"; + out() << "<ul>\n"; + } + + int i = 0; + NodeList::ConstIterator m = section.members.begin(); + while (m != section.members.end()) { + if ((*m)->access() == Node::Private) { + ++m; + continue; + } + + if (alignNames) { + out() << "<tr><td class=\"memItemLeft topAlign rightAlign\"> "; + } + else { + if (twoColumn && i == (int) (section.members.count() + 1) / 2) + out() << "</ul></td><td class=\"topAlign\"><ul>\n"; + out() << "<li class=\"fn\">"; + } + + QString prefix; + if (!section.keys.isEmpty()) { + prefix = section.keys.at(i).mid(1); + prefix = prefix.left(section.keys.at(i).indexOf("::")+1); + } + generateSynopsis(*m, relative, marker, style, alignNames, &prefix); + if (alignNames) + out() << "</td></tr>\n"; + else + out() << "</li>\n"; + i++; + ++m; + } + if (alignNames) + out() << "</table>\n"; + else { + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table>\n"; + } + } + + if (style == CodeMarker::Summary && !section.inherited.isEmpty()) { + out() << "<ul>\n"; + generateSectionInheritedList(section, relative, marker); + out() << "</ul>\n"; + } +} + +void HtmlGenerator::generateSectionInheritedList(const Section& section, + const Node *relative, + CodeMarker *marker) +{ + QList<QPair<InnerNode *, int> >::ConstIterator p = section.inherited.begin(); + while (p != section.inherited.end()) { + out() << "<li class=\"fn\">"; + out() << (*p).second << ' '; + if ((*p).second == 1) { + out() << section.singularMember; + } + else { + out() << section.pluralMember; + } + out() << " inherited from <a href=\"" << fileName((*p).first) + << '#' << HtmlGenerator::cleanRef(section.name.toLower()) << "\">" + << protectEnc(marker->plainFullName((*p).first, relative)) + << "</a></li>\n"; + ++p; + } +} + +void HtmlGenerator::generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style, + bool alignNames, + const QString* prefix) +{ + QString marked = marker->markedUpSynopsis(node, relative, style); + + if (prefix) + marked.prepend(*prefix); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protectEnc(marked.mid(templateTag.pos(1), + templateTag.cap(1).length())); + marked.replace(templateTag.pos(1), templateTag.cap(1).length(), + contents); + } + marked.replace(QRegExp("<@param>([a-z]+)_([1-9n])</@param>"), + "<i>\\1<sub>\\2</sub></i>"); + marked.replace("<@param>", "<i> "); + marked.replace("</@param>", "</i>"); + + if (style == CodeMarker::Summary) { + marked.remove("<@name>"); // was "<b>" + marked.remove("</@name>"); // was "</b>" + } + + if (style == CodeMarker::Subpage) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.remove(extraRegExp); + } else { + marked.replace("<@extra>", "<tt>"); + marked.replace("</@extra>", "</tt>"); + } + + if (style != CodeMarker::Detailed) { + marked.remove("<@type>"); + marked.remove("</@type>"); + } + + out() << highlightedCode(marked, marker, relative, alignNames); +} + +QString HtmlGenerator::highlightedCode(const QString& markedCode, + CodeMarker* marker, + const Node* relative, + bool alignNames, + const Node* self) +{ + QString src = markedCode; + QString html; + QStringRef arg; + QStringRef par1; + + const QChar charLangle = '<'; + const QChar charAt = '@'; + + static const QString typeTag("type"); + static const QString headerTag("headerfile"); + static const QString funcTag("func"); + static const QString linkTag("link"); + + // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" + bool done = false; + for (int i = 0, srcSize = src.size(); i < srcSize;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + if (alignNames && !done) { + html += "</td><td class=\"memItemRight bottomAlign\">"; + done = true; + } + i += 2; + if (parseArg(src, linkTag, &i, srcSize, &arg, &par1)) { + html += "<b>"; + const Node* n = CodeMarker::nodeForString(par1.toString()); + QString link = linkForNode(n, relative); + addLink(link, arg, &html); + html += "</b>"; + } + else { + html += charLangle; + html += charAt; + } + } + else { + html += src.at(i++); + } + } + + // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" + src = html; + html = QString(); + for (int i = 0, srcSize = src.size(); i < srcSize;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { + + const Node* n = marker->resolveTarget(par1.toString(), + myTree, + relative); + QString link = linkForNode(n, relative); + addLink(link, arg, &html); + par1 = QStringRef(); + } + else { + html += charLangle; + html += charAt; + } + } + else { + html += src.at(i++); + } + } + + // replace all "(<@(type|headerfile|func)(?: +[^>]*)?>)(.*)(</@\\2>)" tags + src = html; + html = QString(); + + for (int i=0, srcSize=src.size(); i<srcSize;) { + if (src.at(i) == charLangle && src.at(i+1) == charAt) { + i += 2; + bool handled = false; + if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { + par1 = QStringRef(); + const Node* n = marker->resolveTarget(arg.toString(), myTree, relative, self); + html += QLatin1String("<span class=\"type\">"); + if (n && n->subType() == Node::QmlBasicType) { + if (relative && relative->subType() == Node::QmlClass) + addLink(linkForNode(n,relative), arg, &html); + else + html += arg.toString(); + } + else + addLink(linkForNode(n,relative), arg, &html); + html += QLatin1String("</span>"); + handled = true; + } + else if (parseArg(src, headerTag, &i, srcSize, &arg, &par1)) { + par1 = QStringRef(); + const Node* n = marker->resolveTarget(arg.toString(), myTree, relative); + addLink(linkForNode(n,relative), arg, &html); + handled = true; + } + else if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { + par1 = QStringRef(); + const Node* n = marker->resolveTarget(arg.toString(), myTree, relative); + addLink(linkForNode(n,relative), arg, &html); + handled = true; + } + + if (!handled) { + html += charLangle; + html += charAt; + } + } + else { + html += src.at(i++); + } + } + + // replace all + // "<@comment>" -> "<span class=\"comment\">"; + // "<@preprocessor>" -> "<span class=\"preprocessor\">"; + // "<@string>" -> "<span class=\"string\">"; + // "<@char>" -> "<span class=\"char\">"; + // "<@number>" -> "<span class=\"number\">"; + // "<@op>" -> "<span class=\"operator\">"; + // "<@type>" -> "<span class=\"type\">"; + // "<@name>" -> "<span class=\"name\">"; + // "<@keyword>" -> "<span class=\"keyword\">"; + // "</@(?:comment|preprocessor|string|char|number|op|type|name|keyword)>" -> "</span>" + src = html; + html = QString(); + static const QString spanTags[] = { + "<@comment>", "<span class=\"comment\">", + "<@preprocessor>", "<span class=\"preprocessor\">", + "<@string>", "<span class=\"string\">", + "<@char>", "<span class=\"char\">", + "<@number>", "<span class=\"number\">", + "<@op>", "<span class=\"operator\">", + "<@type>", "<span class=\"type\">", + "<@name>", "<span class=\"name\">", + "<@keyword>", "<span class=\"keyword\">", + "</@comment>", "</span>", + "</@preprocessor>", "</span>", + "</@string>", "</span>", + "</@char>", "</span>", + "</@number>", "</span>", + "</@op>", "</span>", + "</@type>", "</span>", + "</@name>", "</span>", + "</@keyword>", "</span>", + }; + // Update the upper bound of k in the following code to match the length + // of the above array. + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle) { + bool handled = false; + for (int k = 0; k != 18; ++k) { + const QString & tag = spanTags[2 * k]; + if (tag == QStringRef(&src, i, tag.length())) { + html += spanTags[2 * k + 1]; + i += tag.length(); + handled = true; + break; + } + } + if (!handled) { + ++i; + if (src.at(i) == charAt || + (src.at(i) == QLatin1Char('/') && src.at(i + 1) == charAt)) { + // drop 'our' unknown tags (the ones still containing '@') + while (i < n && src.at(i) != QLatin1Char('>')) + ++i; + ++i; + } + else { + // retain all others + html += charLangle; + } + } + } + else { + html += src.at(i); + ++i; + } + } + return html; +} + +void HtmlGenerator::generateLink(const Atom* atom, + const Node* /* relative */, + CodeMarker* marker) +{ + static QRegExp camelCase("[A-Z][A-Z][a-z]|[a-z][A-Z0-9]|_"); + + if (funcLeftParen.indexIn(atom->string()) != -1 && marker->recognizeLanguage("Cpp")) { + // hack for C++: move () outside of link + int k = funcLeftParen.pos(1); + out() << protectEnc(atom->string().left(k)); + if (link.isEmpty()) { + if (showBrokenLinks) + out() << "</i>"; + } else { + out() << "</a>"; + } + inLink = false; + out() << protectEnc(atom->string().mid(k)); + } else { + out() << protectEnc(atom->string()); + } +} + +QString HtmlGenerator::cleanRef(const QString& ref) +{ + QString clean; + + if (ref.isEmpty()) + return clean; + + clean.reserve(ref.size() + 20); + const QChar c = ref[0]; + const uint u = c.unicode(); + + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) { + clean += c; + } else if (u == '~') { + clean += "dtor."; + } else if (u == '_') { + clean += "underscore."; + } else { + clean += "A"; + } + + for (int i = 1; i < (int) ref.length(); i++) { + const QChar c = ref[i]; + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9') || u == '-' || + u == '_' || u == ':' || u == '.') { + clean += c; + } else if (c.isSpace()) { + clean += "-"; + } else if (u == '!') { + clean += "-not"; + } else if (u == '&') { + clean += "-and"; + } else if (u == '<') { + clean += "-lt"; + } else if (u == '=') { + clean += "-eq"; + } else if (u == '>') { + clean += "-gt"; + } else if (u == '#') { + clean += QLatin1Char('#'); + } else { + clean += "-"; + clean += QString::number((int)u, 16); + } + } + return clean; +} + +QString HtmlGenerator::registerRef(const QString& ref) +{ + QString clean = HtmlGenerator::cleanRef(ref); + + for (;;) { + QString& prevRef = refMap[clean.toLower()]; + if (prevRef.isEmpty()) { + prevRef = ref; + break; + } else if (prevRef == ref) { + break; + } + clean += "x"; + } + return clean; +} + +QString HtmlGenerator::protectEnc(const QString &string) +{ + return protect(string, outputEncoding); +} + +QString HtmlGenerator::protect(const QString &string, const QString &outputEncoding) +{ +#define APPEND(x) \ + if (html.isEmpty()) { \ + html = string; \ + html.truncate(i); \ +} \ + html += (x); + + QString html; + int n = string.length(); + + for (int i = 0; i < n; ++i) { + QChar ch = string.at(i); + + if (ch == QLatin1Char('&')) { + APPEND("&"); + } else if (ch == QLatin1Char('<')) { + APPEND("<"); + } else if (ch == QLatin1Char('>')) { + APPEND(">"); + } else if (ch == QLatin1Char('"')) { + APPEND("""); + } else if ((outputEncoding == "ISO-8859-1" && ch.unicode() > 0x007F) + || (ch == QLatin1Char('*') && i + 1 < n && string.at(i) == QLatin1Char('/')) + || (ch == QLatin1Char('.') && i > 2 && string.at(i - 2) == QLatin1Char('.'))) { + // we escape '*/' and the last dot in 'e.g.' and 'i.e.' for the Javadoc generator + APPEND("&#x"); + html += QString::number(ch.unicode(), 16); + html += QLatin1Char(';'); + } else { + if (!html.isEmpty()) + html += ch; + } + } + + if (!html.isEmpty()) + return html; + return string; + +#undef APPEND +} + +QString HtmlGenerator::fileBase(const Node *node) const +{ + QString result; + + result = PageGenerator::fileBase(node); + + if (!node->isInnerNode()) { + switch (node->status()) { + case Node::Compat: + result += "-qt3"; + break; + case Node::Obsolete: + result += "-obsolete"; + break; + default: + ; + } + } + return result; +} + +QString HtmlGenerator::fileName(const Node *node) +{ + if (node->type() == Node::Fake) { + if (static_cast<const FakeNode *>(node)->subType() == Node::ExternalPage) + return node->name(); + if (static_cast<const FakeNode *>(node)->subType() == Node::Image) + return node->name(); + } + return PageGenerator::fileName(node); +} + +QString HtmlGenerator::refForNode(const Node *node) +{ + const FunctionNode *func; + const TypedefNode *typedeffe; + QString ref; + + switch (node->type()) { + case Node::Namespace: + case Node::Class: + default: + break; + case Node::Enum: + ref = node->name() + "-enum"; + break; + case Node::Typedef: + typedeffe = static_cast<const TypedefNode *>(node); + if (typedeffe->associatedEnum()) { + return refForNode(typedeffe->associatedEnum()); + } + else { + ref = node->name() + "-typedef"; + } + break; + case Node::Function: + func = static_cast<const FunctionNode *>(node); + if (func->associatedProperty()) { + return refForNode(func->associatedProperty()); + } + else { + ref = func->name(); + if (func->overloadNumber() != 1) + ref += "-" + QString::number(func->overloadNumber()); + } + break; + case Node::Fake: + if (node->subType() != Node::QmlPropertyGroup) + break; + case Node::QmlProperty: + case Node::Property: + ref = node->name() + "-prop"; + break; + case Node::QmlSignal: + ref = node->name() + "-signal"; + break; + case Node::QmlSignalHandler: + ref = node->name() + "-signal-handler"; + break; + case Node::QmlMethod: + ref = node->name() + "-method"; + break; + case Node::Variable: + ref = node->name() + "-var"; + break; + case Node::Target: + return protectEnc(node->name()); + } + return registerRef(ref); +} + +#define DEBUG_ABSTRACT 0 + +/*! + Construct the link string for the \a node and return it. + The \a relative node is use to decide the link we are + generating is in the same file as the target. Note the + relative node can be 0, which pretty much guarantees + that the link and the target aren't in the same file. + */ +QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) +{ + if (node == 0 || node == relative) + return QString(); + if (!node->url().isEmpty()) + return node->url(); + if (fileBase(node).isEmpty()) + return QString(); + if (node->access() == Node::Private) + return QString(); + + QString fn = fileName(node); + if (node && relative && node->parent() != relative) { + if (node->parent()->subType() == Node::QmlClass && relative->subType() == Node::QmlClass) { + if (node->parent()->isAbstract()) { + /* + This is a bit of a hack. What we discover with + the three 'if' statements immediately above, + is that node's parent is marked \qmlabstract + but the link appears in a qdoc comment for a + subclass of the node's parent. This means the + link should refer to the file for the relative + node, not the file for node. + */ + fn = fileName(relative); +#if DEBUG_ABSTRACT + qDebug() << "ABSTRACT:" << node->parent()->name() + << node->name() << relative->name() + << node->parent()->type() << node->parent()->subType() + << relative->type() << relative->subType() << outFileName(); +#endif + } + } + } + QString link = fn; + + if (!node->isInnerNode() || node->subType() == Node::QmlPropertyGroup) { + QString ref = refForNode(node); + if (relative && fn == fileName(relative) && ref == refForNode(relative)) + return QString(); + + link += QLatin1Char('#'); + link += ref; + } + /* + If the output is going to subdirectories, then if the + two nodes will be output to different directories, then + the link must go up to the parent directory and then + back down into the other subdirectory. + */ + if (node && relative && (node != relative)) { + if (node->outputSubdirectory() != relative->outputSubdirectory()) + link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/'))); + } + return link; +} + +QString HtmlGenerator::refForAtom(Atom *atom, const Node * /* node */) +{ + if (atom->type() == Atom::SectionLeft) { + return Doc::canonicalTitle(Text::sectionHeading(atom).toString()); + } + else if (atom->type() == Atom::Target) { + return Doc::canonicalTitle(atom->string()); + } + else { + return QString(); + } +} + +void HtmlGenerator::generateFullName(const Node *apparentNode, + const Node *relative, + CodeMarker *marker, + const Node *actualNode) +{ + if (actualNode == 0) + actualNode = apparentNode; + out() << "<a href=\"" << linkForNode(actualNode, relative); + if (true || relative == 0 || relative->status() != actualNode->status()) { + switch (actualNode->status()) { + case Node::Obsolete: + out() << "\" class=\"obsolete"; + break; + case Node::Compat: + out() << "\" class=\"compat"; + break; + default: + ; + } + } + out() << "\">"; + out() << protectEnc(fullName(apparentNode, relative, marker)); + out() << "</a>"; +} + +void HtmlGenerator::generateDetailedMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker) +{ + const EnumNode *enume; + +#ifdef GENERATE_MAC_REFS + generateMacRef(node, marker); +#endif + generateExtractionMark(node, MemberMark); + if (node->type() == Node::Enum + && (enume = static_cast<const EnumNode *>(node))->flagsType()) { +#ifdef GENERATE_MAC_REFS + generateMacRef(enume->flagsType(), marker); +#endif + out() << "<h3 class=\"flags\">"; + out() << "<a name=\"" + refForNode(node) + "\"></a>"; + generateSynopsis(enume, relative, marker, CodeMarker::Detailed); + out() << "<br/>"; + generateSynopsis(enume->flagsType(), + relative, + marker, + CodeMarker::Detailed); + out() << "</h3>\n"; + } + else { + out() << "<h3 class=\"fn\">"; + out() << "<a name=\"" + refForNode(node) + "\"></a>"; + generateSynopsis(node, relative, marker, CodeMarker::Detailed); + out() << "</h3>" << divNavTop << '\n'; + } + + generateStatus(node, marker); + generateBody(node, marker); + generateThreadSafeness(node, marker); + generateSince(node, marker); + + if (node->type() == Node::Property) { + const PropertyNode *property = static_cast<const PropertyNode *>(node); + Section section; + + section.members += property->getters(); + section.members += property->setters(); + section.members += property->resetters(); + + if (!section.members.isEmpty()) { + out() << "<p><b>Access functions:</b></p>\n"; + generateSectionList(section, node, marker, CodeMarker::Accessors); + } + + Section notifiers; + notifiers.members += property->notifiers(); + + if (!notifiers.members.isEmpty()) { + out() << "<p><b>Notifier signal:</b></p>\n"; + //out() << "<p>This signal is emitted when the property value is changed.</p>\n"; + generateSectionList(notifiers, node, marker, CodeMarker::Accessors); + } + } + else if (node->type() == Node::Enum) { + const EnumNode *enume = static_cast<const EnumNode *>(node); + if (enume->flagsType()) { + out() << "<p>The " << protectEnc(enume->flagsType()->name()) + << " type is a typedef for " + << "<a href=\"qflags.html\">QFlags</a><" + << protectEnc(enume->name()) + << ">. It stores an OR combination of " + << protectEnc(enume->name()) + << " values.</p>\n"; + } + } + generateAlsoList(node, marker); + generateExtractionMark(node, EndMark); +} + +void HtmlGenerator::findAllClasses(const InnerNode *node) +{ + NodeList::const_iterator c = node->childNodes().constBegin(); + while (c != node->childNodes().constEnd()) { + if ((*c)->access() != Node::Private && (*c)->url().isEmpty()) { + if ((*c)->type() == Node::Class && !(*c)->doc().isEmpty()) { + QString className = (*c)->name(); + if ((*c)->parent() && + (*c)->parent()->type() == Node::Namespace && + !(*c)->parent()->name().isEmpty()) + className = (*c)->parent()->name()+"::"+className; + + if (!(static_cast<const ClassNode *>(*c))->hideFromMainList()) { + if ((*c)->status() == Node::Compat) { + compatClasses.insert(className, *c); + } + else if ((*c)->status() == Node::Obsolete) { + obsoleteClasses.insert(className, *c); + } + else { + nonCompatClasses.insert(className, *c); + if ((*c)->status() == Node::Main) + mainClasses.insert(className, *c); + } + } + + QString moduleName = (*c)->moduleName(); + if (moduleName == "Qt3SupportLight") { + moduleClassMap[moduleName].insert((*c)->name(), *c); + moduleName = "Qt3Support"; + } + if (!moduleName.isEmpty()) + moduleClassMap[moduleName].insert((*c)->name(), *c); + + QString serviceName = + (static_cast<const ClassNode *>(*c))->serviceName(); + if (!serviceName.isEmpty()) + serviceClasses.insert(serviceName, *c); + } + else if ((*c)->type() == Node::Fake && + (*c)->subType() == Node::QmlClass && + !(*c)->doc().isEmpty()) { + QString qmlClassName = (*c)->name(); + /* + Remove the "QML:" prefix, if present. + It shouldn't be present anymore. + */ + if (qmlClassName.startsWith(QLatin1String("QML:"))) + qmlClasses.insert(qmlClassName.mid(4),*c); + else + qmlClasses.insert(qmlClassName,*c); + } + else if ((*c)->isInnerNode()) { + findAllClasses(static_cast<InnerNode *>(*c)); + } + } + ++c; + } +} + +void HtmlGenerator::findAllFunctions(const InnerNode *node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { + findAllFunctions(static_cast<const InnerNode *>(*c)); + } + else if ((*c)->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(*c); + if ((func->status() > Node::Obsolete) && + !func->isInternal() && + (func->metaness() != FunctionNode::Ctor) && + (func->metaness() != FunctionNode::Dtor)) { + funcIndex[(*c)->name()].insert((*c)->parent()->fullDocumentName(), *c); + } + } + } + ++c; + } +} + +void HtmlGenerator::findAllLegaleseTexts(const InnerNode *node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if (!(*c)->doc().legaleseText().isEmpty()) + legaleseTexts.insertMulti((*c)->doc().legaleseText(), *c); + if ((*c)->isInnerNode()) + findAllLegaleseTexts(static_cast<const InnerNode *>(*c)); + } + ++c; + } +} + +void HtmlGenerator::findAllNamespaces(const InnerNode *node) +{ + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->access() != Node::Private) { + if ((*c)->isInnerNode() && (*c)->url().isEmpty()) { + findAllNamespaces(static_cast<const InnerNode *>(*c)); + if ((*c)->type() == Node::Namespace) { + const NamespaceNode *nspace = static_cast<const NamespaceNode *>(*c); + // Ensure that the namespace's name is not empty (the root + // namespace has no name). + if (!nspace->name().isEmpty()) { + namespaceIndex.insert(nspace->name(), *c); + QString moduleName = (*c)->moduleName(); + if (moduleName == "Qt3SupportLight") { + moduleNamespaceMap[moduleName].insert((*c)->name(), *c); + moduleName = "Qt3Support"; + } + if (!moduleName.isEmpty()) + moduleNamespaceMap[moduleName].insert((*c)->name(), *c); + } + } + } + } + ++c; + } +} + +int HtmlGenerator::hOffset(const Node *node) +{ + switch (node->type()) { + case Node::Namespace: + case Node::Class: + return 2; + case Node::Fake: + return 1; + case Node::Enum: + case Node::Typedef: + case Node::Function: + case Node::Property: + default: + return 3; + } +} + +bool HtmlGenerator::isThreeColumnEnumValueTable(const Atom *atom) +{ + while (atom != 0 && !(atom->type() == Atom::ListRight && atom->string() == ATOM_LIST_VALUE)) { + if (atom->type() == Atom::ListItemLeft && !matchAhead(atom, Atom::ListItemRight)) + return true; + atom = atom->next(); + } + return false; +} + +const Node *HtmlGenerator::findNodeForTarget(const QString &target, + const Node *relative, + CodeMarker *marker, + const Atom *atom) +{ + const Node *node = 0; + + if (target.isEmpty()) { + node = relative; + } + else if (target.endsWith(".html")) { + node = myTree->root()->findNode(target, Node::Fake); + } + else if (marker) { + node = marker->resolveTarget(target, myTree, relative); + if (!node) { + node = myTree->findFakeNodeByTitle(target, relative); + } + if (!node && atom) { + node = myTree->findUnambiguousTarget(target, *const_cast<Atom**>(&atom), relative); + } + } + + if (!node) + relative->doc().location().warning(tr("Cannot link to '%1'").arg(target)); + return node; +} + +const QPair<QString,QString> HtmlGenerator::anchorForNode(const Node *node) +{ + QPair<QString,QString> anchorPair; + + anchorPair.first = PageGenerator::fileName(node); + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode*>(node); + anchorPair.second = fakeNode->title(); + } + + return anchorPair; +} + +QString HtmlGenerator::getLink(const Atom *atom, + const Node *relative, + CodeMarker *marker, + const Node** node) +{ + QString link; + *node = 0; + inObsoleteLink = false; + + if (atom->string().contains(":") && + (atom->string().startsWith("file:") + || atom->string().startsWith("http:") + || atom->string().startsWith("https:") + || atom->string().startsWith("ftp:") + || atom->string().startsWith("mailto:"))) { + + link = atom->string(); + } + else { + QStringList path; + if (atom->string().contains('#')) { + path = atom->string().split('#'); + } + else { + path.append(atom->string()); + } + + Atom *targetAtom = 0; + QString first = path.first().trimmed(); + if (first.isEmpty()) { + *node = relative; + } + else if (first.endsWith(".html")) { + *node = myTree->root()->findNode(first, Node::Fake); + } + else { + *node = marker->resolveTarget(first, myTree, relative); + if (!*node) { + *node = myTree->findFakeNodeByTitle(first, relative); + } + if (!*node) { + *node = myTree->findUnambiguousTarget(first, targetAtom, relative); + } + } + if (*node) { + if (!(*node)->url().isEmpty()) { + return (*node)->url(); + } + else { + path.removeFirst(); + } + } + else { + *node = relative; + } + + if (*node) { + if ((*node)->status() == Node::Obsolete) { + if (relative) { + if (relative->parent() != *node) { + if (relative->status() != Node::Obsolete) { + bool porting = false; + if (relative->type() == Node::Fake) { + const FakeNode* fake = static_cast<const FakeNode*>(relative); + if (fake->title().startsWith("Porting")) + porting = true; + } + QString name = marker->plainFullName(relative); + if (!porting && !name.startsWith("Q3")) { + if (obsoleteLinks) { + relative->doc().location().warning(tr("Link to obsolete item '%1' in %2") + .arg(atom->string()) + .arg(name)); + } + inObsoleteLink = true; + } + } + } + } + else { + qDebug() << "Link to Obsolete entity" + << (*node)->name() << "no relative"; + } + } + } + + /* + This loop really only makes sense if *node is not 0. + In that case, The node *node points to represents a + qdoc page, so the link will ultimately point to some + target on that page. This loop finds that target on + the page that *node represents. targetAtom is that + target. + */ + while (!path.isEmpty()) { + targetAtom = myTree->findTarget(path.first(), *node); + if (targetAtom == 0) + break; + path.removeFirst(); + } + + /* + Given that *node is not null, we now cconstruct a link + to the page that *node represents, and then if there is + a target on that page, we connect the target to the link + with '#'. + */ + if (path.isEmpty()) { + link = linkForNode(*node, relative); + if (*node && (*node)->subType() == Node::Image) + link = "images/used-in-examples/" + link; + if (targetAtom) + link += QLatin1Char('#') + refForAtom(targetAtom, *node); + } + /* + If the output is going to subdirectories, then if the + two nodes will be output to different directories, then + the link must go up to the parent directory and then + back down into the other subdirectory. + */ + if (link.startsWith("images/")) { + link.prepend(QString("../")); + } + else if (*node && relative && (*node != relative)) { + if ((*node)->outputSubdirectory() != relative->outputSubdirectory()) { + link.prepend(QString("../" + (*node)->outputSubdirectory() + QLatin1Char('/'))); + } + } + } + return link; +} + +/*! + This function can be called if getLink() returns an empty + string. It tests the \a atom string to see if it is a link + of the form <element> :: <name>, where <element> is a QML + element or component without a module qualifier. If so, it + constructs a link to the <name> clause on the disambiguation + page for <element> and returns that link string. It also + adds the <name> as a target in the NameCollisionNode for + <element>. These clauses are then constructed when the + disambiguation page is actually generated. + */ +QString HtmlGenerator::getDisambiguationLink(const Atom *atom, CodeMarker *) +{ + QString link; + if (!atom->string().contains("::")) + return link; + QStringList path = atom->string().split("::"); + NameCollisionNode* ncn = myTree->findCollisionNode(path[0]); + if (ncn) { + QString label; + if (atom->next() && atom->next()->next()) { + if (atom->next()->type() == Atom::FormattingLeft && + atom->next()->next()->type() == Atom::String) + label = atom->next()->next()->string(); + } + ncn->addLinkTarget(path[1],label); + link = fileName(ncn); + link += QLatin1Char('#'); + link += Doc::canonicalTitle(path[1]); + } + return link; +} + +void HtmlGenerator::generateIndex(const QString &fileBase, + const QString &url, + const QString &title) +{ + myTree->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index", url, title); +} + +void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) +{ + Text text; + + switch (node->status()) { + case Node::Obsolete: + if (node->isInnerNode()) + Generator::generateStatus(node, marker); + break; + case Node::Compat: + if (node->isInnerNode()) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) + << "This " + << typeString(node) + << " is part of the Qt 3 support library." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << " It is provided to keep old source code working. " + << "We strongly advise against " + << "using it in new code. See "; + + const FakeNode *fakeNode = myTree->findFakeNodeByTitle("Porting To Qt 4"); + Atom *targetAtom = 0; + if (fakeNode && node->type() == Node::Class) { + QString oldName(node->name()); + oldName.remove(QLatin1Char('3')); + targetAtom = myTree->findTarget(oldName, + fakeNode); + } + + if (targetAtom) { + text << Atom(Atom::Link, linkForNode(fakeNode, node) + QLatin1Char('#') + + refForAtom(targetAtom, fakeNode)); + } + else + text << Atom(Atom::Link, "Porting to Qt 4"); + + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << Atom(Atom::String, "Porting to Qt 4") + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << " for more information." + << Atom::ParaRight; + } + generateText(text, node, marker); + break; + default: + Generator::generateStatus(node, marker); + } +} + +#ifdef GENERATE_MAC_REFS +/* + No longer valid. + */ +void HtmlGenerator::generateMacRef(const Node *node, CodeMarker *marker) +{ + if (!pleaseGenerateMacRef || marker == 0) + return; + + QStringList macRefs = marker->macRefsForNode(node); + foreach (const QString &macRef, macRefs) + out() << "<a name=\"" << "//apple_ref/" << macRef << "\"></a>\n"; +} +#endif + +void HtmlGenerator::beginLink(const QString &link, + const Node *node, + const Node *relative, + CodeMarker *marker) +{ + Q_UNUSED(marker) + Q_UNUSED(relative) + + this->link = link; + if (link.isEmpty()) { + if (showBrokenLinks) + out() << "<i>"; + } + else if (node == 0 || + (relative != 0 && node->status() == relative->status())) { + out() << "<a href=\"" << link << "\">"; + } + else { + switch (node->status()) { + case Node::Obsolete: + out() << "<a href=\"" << link << "\" class=\"obsolete\">"; + break; + case Node::Compat: + out() << "<a href=\"" << link << "\" class=\"compat\">"; + break; + default: + out() << "<a href=\"" << link << "\">"; + } + } + inLink = true; +} + +void HtmlGenerator::endLink() +{ + if (inLink) { + if (link.isEmpty()) { + if (showBrokenLinks) + out() << "</i>"; + } + else { + if (inObsoleteLink) { + out() << "<sup>(obsolete)</sup>"; + } + out() << "</a>"; + } + } + inLink = false; + inObsoleteLink = false; +} + +/*! + Generates the summary for the \a section. Only used for + sections of QML element documentation. + + Currently handles only the QML property group. + */ +void HtmlGenerator::generateQmlSummary(const Section& section, + const Node *relative, + CodeMarker *marker) +{ + if (!section.members.isEmpty()) { + out() << "<ul>\n"; + NodeList::ConstIterator m; + m = section.members.begin(); + while (m != section.members.end()) { + out() << "<li class=\"fn\">"; + generateQmlItem(*m,relative,marker,true); + out() << "</li>\n"; + ++m; + } + out() << "</ul>\n"; + } +} + +/*! + Outputs the html detailed documentation for a section + on a QML element reference page. + */ +void HtmlGenerator::generateDetailedQmlMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker) +{ + const QmlPropertyNode* qpn = 0; +#ifdef GENERATE_MAC_REFS + generateMacRef(node, marker); +#endif + generateExtractionMark(node, MemberMark); + out() << "<div class=\"qmlitem\">"; + if (node->subType() == Node::QmlPropertyGroup) { + const QmlPropGroupNode* qpgn = static_cast<const QmlPropGroupNode*>(node); + NodeList::ConstIterator p = qpgn->childNodes().begin(); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + while (p != qpgn->childNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + qpn = static_cast<const QmlPropertyNode*>(*p); + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlPropNode\"><p>"; + out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; + + int ro = qpn->getReadOnly(); + if (ro < 0) { + if (!qpn->isWritable(myTree)) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + } + else if (ro > 0) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + if (qpgn->isDefault()) + out() << "<span class=\"qmldefault\">default</span>"; + generateQmlItem(qpn, relative, marker, false); + out() << "</p></td></tr>"; + } + ++p; + } + out() << "</table>"; + out() << "</div>"; + } + else if (node->type() == Node::QmlProperty) { + qpn = static_cast<const QmlPropertyNode*>(node); + /* + If the QML property node has a single subproperty, + override, replace qpn with that override node and + proceed as normal. + */ + if (qpn->qmlPropNodes().size() == 1) { + Node* n = qpn->qmlPropNodes().at(0); + if (n->type() == Node::QmlProperty) + qpn = static_cast<const QmlPropertyNode*>(n); + } + /* + Now qpn either has no overrides, or it has more + than 1. If it has none, proceed to output as nortmal. + */ + if (qpn->qmlPropNodes().isEmpty()) { + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlPropNode\"><p>"; + out() << "<a name=\"" + refForNode(qpn) + "\"></a>"; + int ro = qpn->getReadOnly(); + if (ro < 0) { + const ClassNode* cn = qpn->declarativeCppNode(); + if (cn && !qpn->isWritable(myTree)) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + } + else if (ro > 0) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + if (qpn->isDefault()) + out() << "<span class=\"qmldefault\">default</span>"; + generateQmlItem(qpn, relative, marker, false); + out() << "</p></td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + else { + /* + The QML property node has multiple override nodes. + Process the whole list as we would for a QML property + group. + */ + NodeList::ConstIterator p = qpn->qmlPropNodes().begin(); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + while (p != qpn->qmlPropNodes().end()) { + if ((*p)->type() == Node::QmlProperty) { + QmlPropertyNode* q = static_cast<QmlPropertyNode*>(*p); + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlPropNode\"><p>"; + out() << "<a name=\"" + refForNode(q) + "\"></a>"; + + int ro = qpn->getReadOnly(); + if (ro < 0) { + if (!qpn->isWritable(myTree)) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + } + else if (ro > 0) { + out() << "<span class=\"qmlreadonly\">read-only</span>"; + } + if (qpn->isDefault()) + out() << "<span class=\"qmldefault\">default</span>"; + generateQmlItem(q, relative, marker, false); + out() << "</p></td></tr>"; + } + ++p; + } + out() << "</table>"; + out() << "</div>"; + } + } + else if (node->type() == Node::QmlSignal) { + const FunctionNode* qsn = static_cast<const FunctionNode*>(node); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlFuncNode\"><p>"; + out() << "<a name=\"" + refForNode(qsn) + "\"></a>"; + generateSynopsis(qsn,relative,marker,CodeMarker::Detailed,false); + out() << "</p></td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + else if (node->type() == Node::QmlSignalHandler) { + const FunctionNode* qshn = static_cast<const FunctionNode*>(node); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlFuncNode\"><p>"; + out() << "<a name=\"" + refForNode(qshn) + "\"></a>"; + generateSynopsis(qshn,relative,marker,CodeMarker::Detailed,false); + out() << "</p></td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + else if (node->type() == Node::QmlMethod) { + const FunctionNode* qmn = static_cast<const FunctionNode*>(node); + out() << "<div class=\"qmlproto\">"; + out() << "<table class=\"qmlname\">"; + out() << "<tr valign=\"top\" class=\"odd\">"; + out() << "<td class=\"tblQmlFuncNode\"><p>"; + out() << "<a name=\"" + refForNode(qmn) + "\"></a>"; + generateSynopsis(qmn,relative,marker,CodeMarker::Detailed,false); + out() << "</p></td></tr>"; + out() << "</table>"; + out() << "</div>"; + } + out() << "<div class=\"qmldoc\">"; + generateStatus(node, marker); + generateBody(node, marker); + generateThreadSafeness(node, marker); + generateSince(node, marker); + generateAlsoList(node, marker); + out() << "</div>"; + out() << "</div>"; + generateExtractionMark(node, EndMark); +} + +/*! + Output the "Inherits" line for the QML element, + if there should be one. + */ +void HtmlGenerator::generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker) +{ + if (!qcn) + return; + const FakeNode* base = qcn->qmlBase(); + if (base) { + Text text; + text << Atom::ParaLeft << "Inherits "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(base)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, base->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << Atom::ParaRight; + generateText(text, qcn, marker); + } +} + +/*! + Output the "Inherit by" list for the QML element, + if it is inherited by any other elements. + */ +void HtmlGenerator::generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker) +{ + if (qcn) { + NodeList subs; + QmlClassNode::subclasses(qcn->name(),subs); + if (!subs.isEmpty()) { + Text text; + text << Atom::ParaLeft << "Inherited by "; + appendSortedQmlNames(text,qcn,subs,marker); + text << Atom::ParaRight; + generateText(text, qcn, marker); + } + } +} + +/*! + Output the "[Xxx instantiates the C++ class QmlGraphicsXxx]" + line for the QML element, if there should be one. + + If there is no class node, or if the class node status + is set to Node::Internal, do nothing. + */ +void HtmlGenerator::generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker) +{ + const ClassNode* cn = qcn->classNode(); + if (cn && (cn->status() != Node::Internal)) { + Text text; + text << Atom::ParaLeft; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + QString name = qcn->name(); + /* + Remove the "QML:" prefix, if present. + It shouldn't be present anymore. + */ + if (name.startsWith(QLatin1String("QML:"))) + name = name.mid(4); // remove the "QML:" prefix + text << Atom(Atom::String, name); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " instantiates the C++ class "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << Atom::ParaRight; + generateText(text, qcn, marker); + } +} + +/*! + Output the "[QmlGraphicsXxx is instantiated by QML element Xxx]" + line for the class, if there should be one. + + If there is no QML element, or if the class node status + is set to Node::Internal, do nothing. + */ +void HtmlGenerator::generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker) +{ + if (cn && cn->status() != Node::Internal && cn->qmlElement() != 0) { + const QmlClassNode* qcn = cn->qmlElement(); + Text text; + text << Atom::ParaLeft; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(cn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, cn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << " is instantiated by QML element "; + text << Atom(Atom::LinkNode,CodeMarker::stringForNode(qcn)); + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + text << Atom(Atom::String, qcn->name()); + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); + text << Atom::ParaRight; + generateText(text, cn, marker); + } +} + +/*! + Generate the <page> element for the given \a node using the \a writer. + Return true if a <page> element was written; otherwise return false. + */ +bool HtmlGenerator::generatePageElement(QXmlStreamWriter& writer, + const Node* node, + CodeMarker* marker) const +{ + if (node->pageType() == Node::NoPageType) + return false; + if (node->name().isEmpty()) + return true; + if (node->access() == Node::Private) + return false; + + QString guid = QUuid::createUuid().toString(); + QString title; + QString rawTitle; + QString fullTitle; + QStringList pageWords; + QXmlStreamAttributes attributes; + + QString url = node->outputSubdirectory(); + if (!url.isEmpty()) + url.append(QLatin1Char('/')); + url.append(PageGenerator::fileName(node)); + + writer.writeStartElement("page"); + + if (node->isInnerNode()) { + const InnerNode* inner = static_cast<const InnerNode*>(node); + if (!inner->pageKeywords().isEmpty()) + pageWords << inner->pageKeywords(); + + switch (node->type()) { + case Node::Fake: + { + const FakeNode* fake = static_cast<const FakeNode*>(node); + title = fake->fullTitle(); + pageWords << title; + break; + } + case Node::Class: + { + title = node->name() + " Class"; + pageWords << node->name() << "class" << "reference"; + break; + } + case Node::Namespace: + { + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Namespace"; + pageWords << rawTitle << "namespace" << "reference"; + break; + } + default: + title = node->name(); + pageWords << title; + break; + } + } + else { + switch (node->type()) { + case Node::Enum: + { + title = node->name() + " Enum"; + pageWords << node->name() << "enum" << "type"; + url += QLatin1Char('#') + node->name() + "-enum"; + break; + } + case Node::Function: + { + title = node->name() + " Function"; + pageWords << node->name() << "function"; + url += QLatin1Char('#') + node->name(); + break; + } + case Node::Property: + { + title = node->name() + " Property"; + pageWords << node->name() << "property"; + url += QLatin1Char('#') + node->name() + "-prop"; + break; + } + case Node::Typedef: + { + title = node->name() + " Type"; + pageWords << node->name() << "typedef" << "type"; + url += QLatin1Char('#') + node->name(); + break; + } + default: + title = node->name(); + pageWords << title; + break; + } + + Node* parent = node->parent(); + if (parent && ((parent->type() == Node::Class) || + (parent->type() == Node::Namespace))) { + pageWords << parent->name(); + } + } + + writer.writeAttribute("id",guid); + writer.writeStartElement("pageWords"); + writer.writeCharacters(pageWords.join(" ")); + + writer.writeEndElement(); + writer.writeStartElement("pageTitle"); + writer.writeCharacters(title); + writer.writeEndElement(); + writer.writeStartElement("pageUrl"); + writer.writeCharacters(url); + writer.writeEndElement(); + writer.writeStartElement("pageType"); + QString ptype = "Article"; + switch (node->pageType()) { + case Node::ApiPage: + ptype = "APIPage"; + break; + case Node::ArticlePage: + ptype = "Article"; + break; + case Node::ExamplePage: + ptype = "Example"; + break; + case Node::HowToPage: + ptype = "HowTo"; + break; + case Node::OverviewPage: + ptype = "Overview"; + break; + case Node::TutorialPage: + ptype = "Tutorial"; + break; + case Node::FAQPage: + ptype = "FAQ"; + break; + default: + break; + } + writer.writeCharacters(ptype); + writer.writeEndElement(); + writer.writeEndElement(); + + if (node->type() == Node::Fake && node->doc().hasTableOfContents()) { + QList<Atom*> toc = node->doc().tableOfContents(); + if (!toc.isEmpty()) { + for (int i = 0; i < toc.size(); ++i) { + Text headingText = Text::sectionHeading(toc.at(i)); + QString s = headingText.toString(); + writer.writeStartElement("page"); + guid = QUuid::createUuid().toString(); + QString internalUrl = url + QLatin1Char('#') + Doc::canonicalTitle(s); + writer.writeAttribute("id",guid); + writer.writeStartElement("pageWords"); + writer.writeCharacters(pageWords.join(" ")); + writer.writeCharacters(" "); + writer.writeCharacters(s); + writer.writeEndElement(); + writer.writeStartElement("pageTitle"); + writer.writeCharacters(s); + writer.writeEndElement(); + writer.writeStartElement("pageUrl"); + writer.writeCharacters(internalUrl); + writer.writeEndElement(); + writer.writeStartElement("pageType"); + writer.writeCharacters("Article"); + writer.writeEndElement(); + writer.writeEndElement(); + } + } + } + return true; +} + +/*! + Traverse the tree recursively and generate the <keyword> + elements. + */ +void HtmlGenerator::generatePageElements(QXmlStreamWriter& writer, const Node* node, CodeMarker* marker) const +{ + if (generatePageElement(writer, node, marker)) { + + if (node->isInnerNode()) { + const InnerNode *inner = static_cast<const InnerNode *>(node); + + // Recurse to write an element for this child node and all its children. + foreach (const Node *child, inner->childNodes()) + generatePageElements(writer, child, marker); + } + } +} + +/*! + Outputs the file containing the index used for searching the html docs. + */ +void HtmlGenerator::generatePageIndex(const QString& fileName) const +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + CodeMarker *marker = CodeMarker::markerForFileName(fileName); + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("qtPageIndex"); + + generatePageElements(writer, myTree->root(), marker); + + writer.writeEndElement(); // qtPageIndex + writer.writeEndDocument(); + file.close(); +} + +void HtmlGenerator::generateExtractionMark(const Node *node, ExtractionMarkType markType) +{ + if (markType != EndMark) { + out() << "<!-- $$$" + node->name(); + if (markType == MemberMark) { + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + if (!func->associatedProperty()) { + if (func->overloadNumber() == 1) + out() << "[overload1]"; + out() << "$$$" + func->name() + func->rawParameters().remove(' '); + } + } else if (node->type() == Node::Property) { + out() << "-prop"; + const PropertyNode *prop = static_cast<const PropertyNode *>(node); + const NodeList &list = prop->functions(); + foreach (const Node *propFuncNode, list) { + if (propFuncNode->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(propFuncNode); + out() << "$$$" + func->name() + func->rawParameters().remove(' '); + } + } + } else if (node->type() == Node::Enum) { + const EnumNode *enumNode = static_cast<const EnumNode *>(node); + foreach (const EnumItem &item, enumNode->items()) + out() << "$$$" + item.name(); + } + } else if (markType == BriefMark) { + out() << "-brief"; + } else if (markType == DetailedDescriptionMark) { + out() << "-description"; + } + out() << " -->\n"; + } else { + out() << "<!-- @@@" + node->name() + " -->\n"; + } +} + +/*! + Returns the full document location for HTML-based documentation. + */ +QString HtmlGenerator::fullDocumentLocation(const Node *node, bool subdir) +{ + if (!node) + return ""; + if (!node->url().isEmpty()) + return node->url(); + + QString parentName; + QString anchorRef; + QString fdl = ""; + + /* + If the output is being sent to subdirectories of the + output directory, and if the subdir parameter is set, + prepend the subdirectory name + '/' to the result. + */ + if (subdir) { + fdl = node->outputSubdirectory(); + if (!fdl.isEmpty()) + fdl.append(QLatin1Char('/')); + } + if (node->type() == Node::Namespace) { + + // The root namespace has no name - check for this before creating + // an attribute containing the location of any documentation. + + if (!node->fileBase().isEmpty()) + parentName = node->fileBase() + ".html"; + else + return ""; + } + else if (node->type() == Node::Fake) { + if ((node->subType() == Node::QmlClass) || + (node->subType() == Node::QmlBasicType)) { + QString fb = node->fileBase(); + if (fb.startsWith(Generator::outputPrefix(QLatin1String("QML")))) + return fb + ".html"; + else { + QString mq = ""; + if (!node->qmlModuleName().isEmpty()) { + mq = node->qmlModuleIdentifier().replace(QChar('.'),QChar('-')); + mq = mq.toLower() + "-"; + } + return fdl+ Generator::outputPrefix(QLatin1String("QML")) + mq + + node->fileBase() + QLatin1String(".html"); + } + } + else + parentName = node->fileBase() + ".html"; + } + else if (node->fileBase().isEmpty()) + return ""; + + Node *parentNode = 0; + + if ((parentNode = node->relates())) { + parentName = fullDocumentLocation(node->relates()); + } + else if ((parentNode = node->parent())) { + if (parentNode->subType() == Node::QmlPropertyGroup) { + parentNode = parentNode->parent(); + parentName = fullDocumentLocation(parentNode); + } + else + parentName = fullDocumentLocation(node->parent()); + } + + switch (node->type()) { + case Node::Class: + case Node::Namespace: + if (parentNode && !parentNode->name().isEmpty()) { + parentName.remove(".html"); + parentName += QLatin1Char('-') + + node->fileBase().toLower() + ".html"; + } else { + parentName = node->fileBase() + ".html"; + } + break; + case Node::Function: + { + /* + Functions can be destructors, overloaded, or + have associated properties. + */ + const FunctionNode *functionNode = + static_cast<const FunctionNode *>(node); + + if (functionNode->metaness() == FunctionNode::Dtor) + anchorRef = "#dtor." + functionNode->name().mid(1); + + else if (functionNode->associatedProperty()) + return fullDocumentLocation(functionNode->associatedProperty()); + + else if (functionNode->overloadNumber() > 1) + anchorRef = QLatin1Char('#') + functionNode->name() + + "-" + QString::number(functionNode->overloadNumber()); + else + anchorRef = QLatin1Char('#') + functionNode->name(); + } + + /* + Use node->name() instead of node->fileBase() as + the latter returns the name in lower-case. For + HTML anchors, we need to preserve the case. + */ + break; + case Node::Enum: + anchorRef = QLatin1Char('#') + node->name() + "-enum"; + break; + case Node::Typedef: + anchorRef = QLatin1Char('#') + node->name() + "-typedef"; + break; + case Node::Property: + anchorRef = QLatin1Char('#') + node->name() + "-prop"; + break; + case Node::QmlProperty: + anchorRef = QLatin1Char('#') + node->name() + "-prop"; + break; + case Node::QmlSignal: + anchorRef = QLatin1Char('#') + node->name() + "-signal"; + break; + case Node::QmlSignalHandler: + anchorRef = QLatin1Char('#') + node->name() + "-signal-handler"; + break; + case Node::QmlMethod: + anchorRef = QLatin1Char('#') + node->name() + "-method"; + break; + case Node::Variable: + anchorRef = QLatin1Char('#') + node->name() + "-var"; + break; + case Node::Target: + anchorRef = QLatin1Char('#') + Doc::canonicalTitle(node->name()); + break; + case Node::Fake: + { + /* + Use node->fileBase() for fake nodes because they are represented + by pages whose file names are lower-case. + */ + parentName = node->fileBase(); + parentName.replace(QLatin1Char('/'), "-").replace(".", "-"); + parentName += ".html"; + } + break; + default: + break; + } + + // Various objects can be compat (deprecated) or obsolete. + if (node->type() != Node::Class && node->type() != Node::Namespace) { + switch (node->status()) { + case Node::Compat: + parentName.replace(".html", "-qt3.html"); + break; + case Node::Obsolete: + parentName.replace(".html", "-obsolete.html"); + break; + default: + ; + } + } + + return fdl + parentName.toLower() + anchorRef; +} + +/*! + This function outputs one or more manifest files in XML. + They are used by Creator. + */ +void HtmlGenerator::generateManifestFiles() +{ + generateManifestFile("examples", "example"); + generateManifestFile("demos", "demo"); + ExampleNode::exampleNodeMap.clear(); +} + +/*! + This function is called by generaqteManiferstFile(), once + for each manifest file to be generated. \a manifest is the + type of manifest file. + */ +void HtmlGenerator::generateManifestFile(QString manifest, QString element) +{ + if (ExampleNode::exampleNodeMap.isEmpty()) + return; + QString fileName = manifest +"-manifest.xml"; + QFile file(outputDir() + QLatin1Char('/') + fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + bool demos = false; + if (manifest == "demos") + demos = true; + + bool proceed = false; + ExampleNodeMap::Iterator i = ExampleNode::exampleNodeMap.begin(); + while (i != ExampleNode::exampleNodeMap.end()) { + const ExampleNode* en = i.value(); + if (demos) { + if (en->name().startsWith("demos")) { + proceed = true; + break; + } + } + else if (!en->name().startsWith("demos")) { + proceed = true; + break; + } + ++i; + } + if (!proceed) + return; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("instructionals"); + writer.writeAttribute("module", project); + writer.writeStartElement(manifest); + + i = ExampleNode::exampleNodeMap.begin(); + while (i != ExampleNode::exampleNodeMap.end()) { + const ExampleNode* en = i.value(); + if (demos) { + if (!en->name().startsWith("demos")) { + ++i; + continue; + } + } + else if (en->name().startsWith("demos")) { + ++i; + continue; + } + writer.writeStartElement(element); + writer.writeAttribute("name", en->title()); + QString docUrl = manifestDir + en->fileBase() + ".html"; + writer.writeAttribute("docUrl", docUrl); + foreach (const Node* child, en->childNodes()) { + if (child->subType() == Node::File) { + QString file = child->name(); + if (file.endsWith(".pro") || file.endsWith(".qmlproject")) { + if (file.startsWith("demos/")) + file = file.mid(6); + writer.writeAttribute("projectPath", file); + break; + } + } + } + if (!en->imageFileName().isEmpty()) + writer.writeAttribute("imageUrl", manifestDir + en->imageFileName()); + writer.writeStartElement("description"); + Text brief = en->doc().briefText(); + if (!brief.isEmpty()) + writer.writeCDATA(brief.toString()); + else + writer.writeCDATA(QString("No description available")); + writer.writeEndElement(); // description + QStringList tags = en->title().toLower().split(QLatin1Char(' ')); + if (!tags.isEmpty()) { + writer.writeStartElement("tags"); + bool wrote_one = false; + for (int n=0; n<tags.size(); ++n) { + QString tag = tags.at(n); + if (tag.at(0).isDigit()) + continue; + if (tag.at(0) == '-') + continue; + if (tag.startsWith("example")) + continue; + if (tag.startsWith("chapter")) + continue; + if (tag.endsWith(QLatin1Char(':'))) + tag.chop(1); + if (n>0 && wrote_one) + writer.writeCharacters(","); + writer.writeCharacters(tag); + wrote_one = true; + } + writer.writeEndElement(); // tags + } + + QString ename = en->name().mid(en->name().lastIndexOf('/')+1); + QSet<QString> usedNames; + foreach (const Node* child, en->childNodes()) { + if (child->subType() == Node::File) { + QString file = child->name(); + QString fileName = file.mid(file.lastIndexOf('/')+1); + QString baseName = fileName; + if ((fileName.count(QChar('.')) > 0) && + (fileName.endsWith(".cpp") || + fileName.endsWith(".h") || + fileName.endsWith(".qml"))) + baseName.truncate(baseName.lastIndexOf(QChar('.'))); + if (baseName.toLower() == ename) { + if (!usedNames.contains(fileName)) { + writer.writeStartElement("fileToOpen"); + if (file.startsWith("demos/")) + file = file.mid(6); + writer.writeCharacters(file); + writer.writeEndElement(); // fileToOpen + usedNames.insert(fileName); + } + } + else if (fileName.toLower().endsWith("main.cpp") || + fileName.toLower().endsWith("main.qml")) { + if (!usedNames.contains(fileName)) { + writer.writeStartElement("fileToOpen"); + if (file.startsWith("demos/")) + file = file.mid(6); + writer.writeCharacters(file); + writer.writeEndElement(); // fileToOpen + usedNames.insert(fileName); + } + } + } + } + writer.writeEndElement(); // example + ++i; + } + + writer.writeEndElement(); // examples + writer.writeEndElement(); // instructionals + writer.writeEndDocument(); + file.close(); +} + +/*! + Find global entities that have documentation but no + \e{relates} comand. Report these as errors if they + are not also marked \e {internal}. + + type: Class + type: Namespace + + subtype: Example + subtype: External page + subtype: Group + subtype: Header file + subtype: Module + subtype: Page + subtype: QML basic type + subtype: QML class + subtype: QML module + */ +void HtmlGenerator::reportOrphans(const InnerNode* parent) +{ + const NodeList& children = parent->childNodes(); + if (children.size() == 0) + return; + + bool related; + QString message; + for (int i=0; i<children.size(); ++i) { + Node* child = children[i]; + if (!child || child->isInternal() || child->doc().isEmpty()) + continue; + if (child->relates()) { + related = true; + message = child->relates()->name(); + } + else { + related = false; + message = "has documentation but no \\relates command"; + } + switch (child->type()) { + case Node::Namespace: + break; + case Node::Class: + break; + case Node::Fake: + switch (child->subType()) { + case Node::Example: + break; + case Node::HeaderFile: + break; + case Node::File: + break; + case Node::Image: + break; + case Node::Group: + break; + case Node::Module: + break; + case Node::Page: + break; + case Node::ExternalPage: + break; + case Node::QmlClass: + break; + case Node::QmlPropertyGroup: + break; + case Node::QmlBasicType: + break; + case Node::QmlModule: + break; + case Node::Collision: + break; + default: + break; + } + break; + case Node::Enum: + if (!related) + child->location().warning(tr("Global enum, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Typedef: + if (!related) + child->location().warning(tr("Global typedef, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Function: + if (!related) { + const FunctionNode* fn = static_cast<const FunctionNode*>(child); + if (fn->isMacro()) + child->location().warning(tr("Global macro, %1, %2").arg(child->name()).arg(message)); + else + child->location().warning(tr("Global function, %1(), %2").arg(child->name()).arg(message)); + } + break; + case Node::Property: + break; + case Node::Variable: + if (!related) + child->location().warning(tr("Global variable, %1, %2").arg(child->name()).arg(message)); + break; + case Node::Target: + break; + case Node::QmlProperty: + if (!related) + child->location().warning(tr("Global QML property, %1, %2").arg(child->name()).arg(message)); + break; + case Node::QmlSignal: + if (!related) + child->location().warning(tr("Global QML, signal, %1 %2").arg(child->name()).arg(message)); + break; + case Node::QmlSignalHandler: + if (!related) + child->location().warning(tr("Global QML signal handler, %1, %2").arg(child->name()).arg(message)); + break; + case Node::QmlMethod: + if (!related) + child->location().warning(tr("Global QML method, %1, %2").arg(child->name()).arg(message)); + break; + default: + break; + } + } +} + +/*! + Returns a reference to the XML stream writer currently in use. + There is one XML stream writer open for each XML file being + written, and they are kept on a stack. The one on top of the + stack is the one being written to at the moment. In the HTML + output generator, it is perhaps impossible for there to ever + be more than one writer open. + */ +QXmlStreamWriter& HtmlGenerator::xmlWriter() +{ + return *xmlWriterStack.top(); +} + +/*! + This function is only called for writing ditamaps. + + Calls beginSubPage() in the base class to open the file. + Then creates a new XML stream writer using the IO device + from opened file and pushes the XML writer onto a stackj. + Creates the file named \a fileName in the output directory. + Attaches a QTextStream to the created file, which is written + to all over the place using out(). Finally, it sets some + parameters in the XML writer and calls writeStartDocument(). + + It also ensures that a GUID map is created for the output file. + */ +void HtmlGenerator::beginDitamapPage(const InnerNode* node, const QString& fileName) +{ + PageGenerator::beginSubPage(node,fileName); + QXmlStreamWriter* writer = new QXmlStreamWriter(out().device()); + xmlWriterStack.push(writer); + writer->setAutoFormatting(true); + writer->setAutoFormattingIndent(4); + writer->writeStartDocument(); +} + +/*! + This function is only called for writing ditamaps. + + Calls writeEndDocument() and then pops the XML stream writer + off the stack and deletes it. Then it calls endSubPage() in + the base class to close the device. + */ +void HtmlGenerator::endDitamapPage() +{ + xmlWriter().writeEndDocument(); + delete xmlWriterStack.pop(); + PageGenerator::endSubPage(); +} + +/*! + This function is only called for writing ditamaps. + + Creates the DITA map from the topicrefs in \a node, + which is a DitaMapNode. + */ +void HtmlGenerator::writeDitaMap(const DitaMapNode* node) +{ + beginDitamapPage(node,node->name()); + + QString doctype = "<!DOCTYPE map PUBLIC \"-//OASIS//DTD DITA Map//EN\" \"map.dtd\">"; + + xmlWriter().writeDTD(doctype); + xmlWriter().writeStartElement("map"); + xmlWriter().writeStartElement("topicmeta"); + xmlWriter().writeStartElement("shortdesc"); + xmlWriter().writeCharacters(node->title()); + xmlWriter().writeEndElement(); // </shortdesc> + xmlWriter().writeEndElement(); // </topicmeta> + DitaRefList map = node->map(); + writeDitaRefs(map); + endDitamapPage(); +} + +/*! + Write the \a ditarefs to the current output file. + */ +void HtmlGenerator::writeDitaRefs(const DitaRefList& ditarefs) +{ + foreach (DitaRef* t, ditarefs) { + if (t->isMapRef()) + xmlWriter().writeStartElement("mapref"); + else + xmlWriter().writeStartElement("topicref"); + xmlWriter().writeAttribute("navtitle",t->navtitle()); + if (t->href().isEmpty()) { + const FakeNode* fn = myTree->findFakeNodeByTitle(t->navtitle()); + if (fn) + xmlWriter().writeAttribute("href",fileName(fn)); + } + else + xmlWriter().writeAttribute("href",t->href()); + if (t->subrefs() && !t->subrefs()->isEmpty()) + writeDitaRefs(*(t->subrefs())); + xmlWriter().writeEndElement(); // </topicref> or </mapref> + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h new file mode 100644 index 0000000000..24c5fb7179 --- /dev/null +++ b/src/tools/qdoc/htmlgenerator.h @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + htmlgenerator.h +*/ + +#ifndef HTMLGENERATOR_H +#define HTMLGENERATOR_H + +#include <qmap.h> +#include <qregexp.h> +#include <QXmlStreamWriter> +#include "codemarker.h" +#include "config.h" +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +class HelpProjectWriter; + +class HtmlGenerator : public PageGenerator +{ +public: + enum SinceType { + Namespace, + Class, + MemberFunction, + NamespaceFunction, + GlobalFunction, + Macro, + Enum, + Typedef, + Property, + Variable, + QmlClass, + QmlProperty, + QmlSignal, + QmlSignalHandler, + QmlMethod, + LastSinceType + }; + +public: + HtmlGenerator(); + ~HtmlGenerator(); + + virtual void initializeGenerator(const Config& config); + virtual void terminateGenerator(); + virtual QString format(); + virtual void generateTree(const Tree *tree); + virtual void generateDisambiguationPages(); + void generateManifestFiles(); + + QString protectEnc(const QString &string); + static QString protect(const QString &string, const QString &encoding = "ISO-8859-1"); + static QString cleanRef(const QString& ref); + static QString sinceTitle(int i) { return sinceTitles[i]; } + static QString fullDocumentLocation(const Node *node, bool subdir = false); + +protected: + virtual void startText(const Node *relative, CodeMarker *marker); + virtual int generateAtom(const Atom *atom, + const Node *relative, + CodeMarker *marker); + virtual void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker); + virtual void generateFakeNode(const FakeNode *fake, CodeMarker *marker); + virtual QString fileExtension(const Node *node) const; + virtual QString refForNode(const Node *node); + virtual QString linkForNode(const Node *node, const Node *relative); + virtual QString refForAtom(Atom *atom, const Node *node); + + void generateManifestFile(QString manifest, QString element); + +private: + enum SubTitleSize { SmallSubTitle, LargeSubTitle }; + enum ExtractionMarkType { + BriefMark, + DetailedDescriptionMark, + MemberMark, + EndMark + }; + + const QPair<QString,QString> anchorForNode(const Node *node); + const Node *findNodeForTarget(const QString &target, + const Node *relative, + CodeMarker *marker, + const Atom *atom = 0); + void generateBreadCrumbs(const QString& title, + const Node *node, + CodeMarker *marker); + void generateHeader(const QString& title, + const Node *node = 0, + CodeMarker *marker = 0); + void generateTitle(const QString& title, + const Text &subTitle, + SubTitleSize subTitleSize, + const Node *relative, + CodeMarker *marker); + void generateFooter(const Node *node = 0); + void generateBrief(const Node *node, + CodeMarker *marker, + const Node *relative = 0); + void generateIncludes(const InnerNode *inner, CodeMarker *marker); + void generateTableOfContents(const Node *node, + CodeMarker *marker, + QList<Section>* sections = 0); + QString generateListOfAllMemberFile(const InnerNode *inner, + CodeMarker *marker); + QString generateAllQmlMembersFile(const QmlClassNode* qml_cn, + CodeMarker* marker); + QString generateLowStatusMemberFile(const InnerNode *inner, + CodeMarker *marker, + CodeMarker::Status status); + void generateClassHierarchy(const Node *relative, + CodeMarker *marker, + const NodeMap &classMap); + void generateAnnotatedList(const Node *relative, + CodeMarker *marker, + const NodeMap &nodeMap, + bool allOdd = false); + void generateCompactList(const Node *relative, + CodeMarker *marker, + const NodeMap &classMap, + bool includeAlphabet, + QString commonPrefix = QString()); + void generateFunctionIndex(const Node *relative, CodeMarker *marker); + void generateLegaleseList(const Node *relative, CodeMarker *marker); + void generateOverviewList(const Node *relative, CodeMarker *marker); + void generateSectionList(const Section& section, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style); + void generateQmlSummary(const Section& section, + const Node *relative, + CodeMarker *marker); + void generateQmlItem(const Node *node, + const Node *relative, + CodeMarker *marker, + bool summary); + void generateDetailedQmlMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker); + void generateQmlInherits(const QmlClassNode* qcn, CodeMarker* marker); + void generateQmlInheritedBy(const QmlClassNode* qcn, CodeMarker* marker); + void generateQmlInstantiates(const QmlClassNode* qcn, CodeMarker* marker); + void generateInstantiatedBy(const ClassNode* cn, CodeMarker* marker); + + void generateSection(const NodeList& nl, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style); + void generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style, + bool alignNames = false, + const QString* prefix = 0); + void generateSectionInheritedList(const Section& section, + const Node *relative, + CodeMarker *marker); + QString highlightedCode(const QString& markedCode, + CodeMarker* marker, + const Node* relative, + bool alignNames = false, + const Node* self = 0); + + void generateFullName(const Node *apparentNode, + const Node *relative, + CodeMarker *marker, + const Node *actualNode = 0); + void generateDetailedMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker); + void generateLink(const Atom *atom, + const Node *relative, + CodeMarker *marker); + void generateStatus(const Node *node, CodeMarker *marker); + + QString registerRef(const QString& ref); + virtual QString fileBase(const Node *node) const; + QString fileName(const Node *node); + void findAllClasses(const InnerNode *node); + void findAllFunctions(const InnerNode *node); + void findAllLegaleseTexts(const InnerNode *node); + void findAllNamespaces(const InnerNode *node); + static int hOffset(const Node *node); + static bool isThreeColumnEnumValueTable(const Atom *atom); + QString getLink(const Atom *atom, + const Node *relative, + CodeMarker *marker, + const Node** node); + QString getDisambiguationLink(const Atom* atom, CodeMarker* marker); + virtual void generateIndex(const QString &fileBase, + const QString &url, + const QString &title); +#ifdef GENERATE_MAC_REFS + void generateMacRef(const Node *node, CodeMarker *marker); +#endif + void beginLink(const QString &link, + const Node *node, + const Node *relative, + CodeMarker *marker); + void endLink(); + bool generatePageElement(QXmlStreamWriter& writer, + const Node* node, + CodeMarker* marker) const; + void generatePageElements(QXmlStreamWriter& writer, + const Node* node, + CodeMarker* marker) const; + void generatePageIndex(const QString& fileName) const; + void generateExtractionMark(const Node *node, ExtractionMarkType markType); + void reportOrphans(const InnerNode* parent); + + void beginDitamapPage(const InnerNode* node, const QString& fileName); + void endDitamapPage(); + void writeDitaMap(const DitaMapNode* node); + void writeDitaRefs(const DitaRefList& ditarefs); + QXmlStreamWriter& xmlWriter(); + + QMap<QString, QString> refMap; + int codeIndent; + HelpProjectWriter *helpProjectWriter; + bool inLink; + bool inObsoleteLink; + bool inContents; + bool inSectionHeading; + bool inTableHeader; + int numTableRows; + bool threeColumnEnumValueTable; + QString link; + QStringList sectionNumber; + QRegExp funcLeftParen; + QString style; + QString headerScripts; + QString headerStyles; + QString endHeader; + QString postHeader; + QString postPostHeader; + QString footer; + QString address; + bool pleaseGenerateMacRef; + bool noBreadCrumbs; + QString project; + QString projectDescription; + QString projectUrl; + QString navigationLinks; + QString manifestDir; + QStringList stylesheets; + QStringList customHeadElements; + const Tree *myTree; + bool obsoleteLinks; + QMap<QString, NodeMap > moduleClassMap; + QMap<QString, NodeMap > moduleNamespaceMap; + NodeMap nonCompatClasses; + NodeMap mainClasses; + NodeMap compatClasses; + NodeMap obsoleteClasses; + NodeMap namespaceIndex; + NodeMap serviceClasses; + NodeMap qmlClasses; + QMap<QString, NodeMap > funcIndex; + QMap<Text, const Node *> legaleseTexts; + QStack<QXmlStreamWriter*> xmlWriterStack; + static int id; +public: + static bool debugging_on; + static QString divNavTop; +}; + +#define HTMLGENERATOR_ADDRESS "address" +#define HTMLGENERATOR_FOOTER "footer" +#define HTMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me +#define HTMLGENERATOR_POSTHEADER "postheader" +#define HTMLGENERATOR_POSTPOSTHEADER "postpostheader" +#define HTMLGENERATOR_NOBREADCRUMBS "nobreadcrumbs" + +QT_END_NAMESPACE + +#endif + diff --git a/src/tools/qdoc/jscodemarker.cpp b/src/tools/qdoc/jscodemarker.cpp new file mode 100644 index 0000000000..87dec52189 --- /dev/null +++ b/src/tools/qdoc/jscodemarker.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + jscodemarker.cpp +*/ + +#include "qqmljsast_p.h" +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsparser_p.h" + +#include "atom.h" +#include "node.h" +#include "jscodemarker.h" +#include "qmlmarkupvisitor.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +JsCodeMarker::JsCodeMarker() +{ +} + +JsCodeMarker::~JsCodeMarker() +{ +} + +/*! + Returns true if the \a code is recognized by the parser. + */ +bool JsCodeMarker::recognizeCode(const QString &code) +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + QQmlJS::Parser parser(&engine); + + QString newCode = code; + QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode); + lexer.setCode(newCode, 1); + + return parser.parseProgram(); +} + +/*! + Returns true if \a ext is any of a list of file extensions + for the QML language. + */ +bool JsCodeMarker::recognizeExtension(const QString &ext) +{ + return ext == "js" || ext == "json"; +} + +/*! + Returns true if the \a language is recognized. We recognize JavaScript, + ECMAScript and JSON. + */ +bool JsCodeMarker::recognizeLanguage(const QString &language) +{ + return language == "JavaScript" || language == "ECMAScript" || language == "JSON"; +} + +/*! + Returns the type of atom used to represent JavaScript code in the documentation. +*/ +Atom::Type JsCodeMarker::atomType() const +{ + return Atom::JavaScript; +} + +QString JsCodeMarker::markedUpCode(const QString &code, + const Node *relative, + const Location &location) +{ + return addMarkUp(code, relative, location); +} + +QString JsCodeMarker::addMarkUp(const QString &code, + const Node * /* relative */, + const Location &location) +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + + QString newCode = code; + QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode); + lexer.setCode(newCode, 1); + + QQmlJS::Parser parser(&engine); + QString output; + + if (parser.parseProgram()) { + QQmlJS::AST::Node *ast = parser.rootNode(); + // Pass the unmodified code to the visitor so that pragmas and other + // unhandled source text can be output. + QmlMarkupVisitor visitor(code, pragmas, &engine); + QQmlJS::AST::Node::accept(ast, &visitor); + output = visitor.markedUpCode(); + } else { + location.warning(tr("Unable to parse JavaScript: \"%1\" at line %2, column %3").arg( + parser.errorMessage()).arg(parser.errorLineNumber()).arg( + parser.errorColumnNumber())); + output = protect(code); + } + + return output; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/jscodemarker.h b/src/tools/qdoc/jscodemarker.h new file mode 100644 index 0000000000..2b1064e7b8 --- /dev/null +++ b/src/tools/qdoc/jscodemarker.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + jscodemarker.h +*/ + +#ifndef JSCODEMARKER_H +#define JSCODEMARKER_H + +#include "qmlcodemarker.h" + +QT_BEGIN_NAMESPACE + +class JsCodeMarker : public QmlCodeMarker +{ +public: + JsCodeMarker(); + ~JsCodeMarker(); + + virtual bool recognizeCode(const QString &code); + virtual bool recognizeExtension(const QString &ext); + virtual bool recognizeLanguage(const QString &language); + virtual Atom::Type atomType() const; + + virtual QString markedUpCode(const QString &code, + const Node *relative, + const Location &location); + +private: + QString addMarkUp(const QString &code, const Node *relative, + const Location &location); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/location.cpp b/src/tools/qdoc/location.cpp new file mode 100644 index 0000000000..9cea232555 --- /dev/null +++ b/src/tools/qdoc/location.cpp @@ -0,0 +1,398 @@ +/**************************************************************************** +** +** 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 <QtDebug> +#include "config.h" +#include "location.h" + +#include <qregexp.h> +#include <stdlib.h> +#include <limits.h> + +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +QT_STATIC_CONST_IMPL Location Location::null; + +int Location::tabSize; +QString Location::programName; +QRegExp *Location::spuriousRegExp = 0; + +/*! + \class Location + + \brief The Location class keeps track of where we are in a file. + + It maintains a stack of file positions. A file position + consists of the file path, line number, and column number. + The location is used for printing error messages that are + tied to a location in a file. + */ + +/*! + Constructs an empty location. + */ +Location::Location() + : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false) +{ + // nothing. +} + +/*! + Constructs a location with (fileName, 1, 1) on its file + position stack. + */ +Location::Location(const QString& fileName) + : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false) +{ + push(fileName); +} + +/*! + The copy constructor copies the contents of \a other into + this Location using the assignment operator. + */ +Location::Location(const Location& other) + : stk(0), stkTop(&stkBottom), stkDepth(0), etcetera(false) +{ + *this = other; +} + +/*! + The assignment operator does a deep copy of the entire + state of \a other into this Location. + */ +Location& Location::operator=(const Location& other) +{ + QStack<StackEntry> *oldStk = stk; + + stkBottom = other.stkBottom; + if (other.stk == 0) { + stk = 0; + stkTop = &stkBottom; + } + else { + stk = new QStack<StackEntry>(*other.stk); + stkTop = &stk->top(); + } + stkDepth = other.stkDepth; + etcetera = other.etcetera; + delete oldStk; + return *this; +} + +/*! + If the file position on top of the stack has a line number + less than 1, set its line number to 1 and its column number + to 1. Otherwise, do nothing. + */ +void Location::start() +{ + if (stkTop->lineNo < 1) { + stkTop->lineNo = 1; + stkTop->columnNo = 1; + } +} + +/*! + Advance the current file position, using \a ch to decide how to do + that. If \a ch is a \c{'\\n'}, increment the current line number and + set the column number to 1. If \ch is a \c{'\\t'}, increment to the + next tab column. Otherwise, increment the column number by 1. + + The current file position is the one on top of the position stack. + */ +void Location::advance(QChar ch) +{ + if (ch == QLatin1Char('\n')) { + stkTop->lineNo++; + stkTop->columnNo = 1; + } + else if (ch == QLatin1Char('\t')) { + stkTop->columnNo = + 1 + tabSize * (stkTop->columnNo + tabSize-1) / tabSize; + } + else { + stkTop->columnNo++; + } +} + +/*! + Pushes \a filePath onto the file position stack. The current + file position becomes (\a filePath, 1, 1). + + \sa pop() +*/ +void Location::push(const QString& filePath) +{ + if (stkDepth++ >= 1) { + if (stk == 0) + stk = new QStack<StackEntry>; + stk->push(StackEntry()); + stkTop = &stk->top(); + } + + stkTop->filePath = filePath; + stkTop->lineNo = INT_MIN; + stkTop->columnNo = 1; +} + +/*! + Pops the top of the internal stack. The current file position + becomes the next one in the new top of stack. + + \sa push() +*/ +void Location::pop() +{ + if (--stkDepth == 0) { + stkBottom = StackEntry(); + } + else { + stk->pop(); + if (stk->isEmpty()) { + delete stk; + stk = 0; + stkTop = &stkBottom; + } + else { + stkTop = &stk->top(); + } + } +} + +/*! \fn bool Location::isEmpty() const + + Returns true if there is no file name set yet; returns false + otherwise. The functions filePath(), lineNo() and columnNo() + must not be called on an empty Location object. + */ + +/*! \fn const QString& Location::filePath() const + Returns the current path and file name. + Must not be called on an empty Location object. + + \sa lineNo(), columnNo() + */ + +/*! + Returns the file name part of the file path, ie the + current file. Must not be called on an empty Location + object. + */ +QString Location::fileName() const +{ + QString fp = filePath(); + return fp.mid(fp.lastIndexOf('/') + 1); +} + +/*! \fn int Location::lineNo() const + Returns the current line number. + Must not be called on an empty Location object. + + \sa filePath(), columnNo() +*/ + +/*! \fn int Location::columnNo() const + Returns the current column number. + Must not be called on an empty Location object. + + \sa filePath(), lineNo() +*/ + +/*! + Writes \a message and \a detals to stderr as a formatted + warning message. + */ +void Location::warning(const QString& message, const QString& details) const +{ + emitMessage(Warning, message, details); +} + +/*! + Writes \a message and \a detals to stderr as a formatted + error message. + */ +void Location::error(const QString& message, const QString& details) const +{ + emitMessage(Error, message, details); +} + +/*! + Writes \a message and \a detals to stderr as a formatted + error message and then exits the program. + */ +void Location::fatal(const QString& message, const QString& details) const +{ + emitMessage(Error, message, details); + information(message); + information(details); + information("Aborting"); + exit(EXIT_FAILURE); +} + +/*! + Gets several parameters from the \a config, including + tab size, program name, and a regular expression that + appears to be used for matching certain error messages + so that emitMessage() can avoid printing them. + */ +void Location::initialize(const Config& config) +{ + tabSize = config.getInt(CONFIG_TABSIZE); + programName = config.programName(); + + QRegExp regExp = config.getRegExp(CONFIG_SPURIOUS); + if (regExp.isValid()) { + spuriousRegExp = new QRegExp(regExp); + } + else { + config.lastLocation().warning(tr("Invalid regular expression '%1'") + .arg(regExp.pattern())); + } +} + +/*! + Apparently, all this does is delete the regular expression + used for intercepting certain error messages that should + not be emitted by emitMessage(). + */ +void Location::terminate() +{ + delete spuriousRegExp; + spuriousRegExp = 0; +} + +/*! + Prints \a message to \c stdout followed by a \c{'\n'}. + */ +void Location::information(const QString& message) +{ + printf("%s\n", message.toLatin1().data()); + fflush(stdout); +} + +/*! + Report a program bug, including the \a hint. + */ +void Location::internalError(const QString& hint) +{ + Location::null.fatal(tr("Internal error (%1)").arg(hint), + tr("There is a bug in %1. Seek advice from your local" + " %2 guru.") + .arg(programName).arg(programName)); +} + +/*! + Formats \a message and \a details into a single string + and outputs that string to \c stderr. \a type specifies + whether the \a message is an error or a warning. + */ +void Location::emitMessage(MessageType type, + const QString& message, + const QString& details) const +{ + if (type == Warning && + spuriousRegExp != 0 && + spuriousRegExp->exactMatch(message)) + return; + + QString result = message; + if (!details.isEmpty()) + result += "\n[" + details + QLatin1Char(']'); + result.replace("\n", "\n "); + if (type == Error) + result.prepend(tr("error: ")); + result.prepend(toString()); + fprintf(stderr, "%s\n", result.toLatin1().data()); + fflush(stderr); +} + +/*! + Converts the location to a string to be prepended to error + messages. + */ +QString Location::toString() const +{ + QString str; + + if (isEmpty()) { + str = programName; + } + else { + Location loc2 = *this; + loc2.setEtc(false); + loc2.pop(); + if (!loc2.isEmpty()) { + QString blah = tr("In file included from "); + for (;;) { + str += blah; + str += loc2.top(); + loc2.pop(); + if (loc2.isEmpty()) + break; + str += tr(","); + str += QLatin1Char('\n'); + blah.fill(' '); + } + str += tr(":"); + str += QLatin1Char('\n'); + } + str += top(); + } + str += QLatin1String(": "); + return str; +} + +QString Location::top() const +{ + QString str = filePath(); + if (lineNo() >= 1) { + str += QLatin1Char(':'); + str += QString::number(lineNo()); + } + if (etc()) + str += QLatin1String(" (etc.)"); + return str; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/location.h b/src/tools/qdoc/location.h new file mode 100644 index 0000000000..1e1333f782 --- /dev/null +++ b/src/tools/qdoc/location.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + location.h +*/ + +#ifndef LOCATION_H +#define LOCATION_H + +#include <qstack.h> + +#include "tr.h" + +#define QDOC_QML + +QT_BEGIN_NAMESPACE + +class Config; +class QRegExp; + +class Location +{ +public: + Location(); + Location(const QString& filePath); + Location(const Location& other); + ~Location() { delete stk; } + + Location& operator=(const Location& other); + + void start(); + void advance(QChar ch); + void advanceLines(int n) { stkTop->lineNo += n; stkTop->columnNo = 1; } + + void push(const QString& filePath); + void pop(); + void setEtc(bool etc) { etcetera = etc; } + void setLineNo(int no) { stkTop->lineNo = no; } + void setColumnNo(int no) { stkTop->columnNo = no; } + + bool isEmpty() const { return stkDepth == 0; } + int depth() const { return stkDepth; } + const QString& filePath() const { return stkTop->filePath; } + QString fileName() const; + int lineNo() const { return stkTop->lineNo; } + int columnNo() const { return stkTop->columnNo; } + bool etc() const { return etcetera; } + void warning(const QString& message, + const QString& details = QString()) const; + void error(const QString& message, + const QString& details = QString()) const; + void fatal(const QString& message, + const QString& details = QString()) const; + + QT_STATIC_CONST Location null; + + static void initialize(const Config& config); + static void terminate(); + static void information(const QString& message); + static void internalError(const QString& hint); + +private: + enum MessageType { Warning, Error }; + + struct StackEntry + { + QString filePath; + int lineNo; + int columnNo; + }; + + void emitMessage(MessageType type, + const QString& message, + const QString& details) const; + QString toString() const; + QString top() const; + +private: + StackEntry stkBottom; + QStack<StackEntry> *stk; + StackEntry *stkTop; + int stkDepth; + bool etcetera; + + static int tabSize; + static QString programName; + static QRegExp *spuriousRegExp; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp new file mode 100644 index 0000000000..11a54b4d38 --- /dev/null +++ b/src/tools/qdoc/main.cpp @@ -0,0 +1,481 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + main.cpp +*/ + +#include <qglobal.h> +#include <stdlib.h> +#include "codemarker.h" +#include "codeparser.h" +#include "config.h" +#include "cppcodemarker.h" +#include "cppcodeparser.h" +#include "ditaxmlgenerator.h" +#include "doc.h" +#include "htmlgenerator.h" +#include "plaincodemarker.h" +#include "puredocparser.h" +#include "tokenizer.h" +#include "tree.h" + +#ifdef HAVE_DECLARATIVE +#include "jscodemarker.h" +#include "qmlcodemarker.h" +#include "qmlcodeparser.h" +#endif + +#include <qdebug.h> + +#include "qtranslator.h" +#ifndef QT_BOOTSTRAPPED +# include "qcoreapplication.h" +#endif + +QT_BEGIN_NAMESPACE + +/* + The default indent for code is 4. + The default value for false is 0. + The default language is c++. + The default output format is html. + The default tab size is 8. + And those are all the default values for configuration variables. + */ +static const struct { + const char *key; + const char *value; +} defaults[] = { + { CONFIG_CODEINDENT, "4" }, + { CONFIG_FALSEHOODS, "0" }, + { CONFIG_LANGUAGE, "Cpp" }, + { CONFIG_OUTPUTFORMATS, "HTML" }, + { CONFIG_TABSIZE, "8" }, + { 0, 0 } +}; + +static bool highlighting = false; +static bool showInternal = false; +static bool obsoleteLinks = false; +static QStringList defines; +static QHash<QString, Tree *> trees; + +/*! + Print the help message to \c stdout. + */ +static void printHelp() +{ + Location::information(tr("Usage: qdoc [options] file1.qdocconf ...\n" + "Options:\n" + " -help " + "Display this information and exit\n" + " -version " + "Display version of qdoc and exit\n" + " -D<name> " + "Define <name> as a macro while parsing sources\n" + " -highlighting " + "Turn on syntax highlighting (makes qdoc run slower)\n" + " -showinternal " + "Include stuff marked internal\n" + " -obsoletelinks " + "Report links from obsolete items to non-obsolete items\n" + " -outputdir " + "Specify output directory, overrides setting in qdocconf file\n" + " -outputformat " + "Specify output format, overrides setting in qdocconf file") ); +} + +/*! + Prints the qdoc version number to stdout. + */ +static void printVersion() +{ + QString s = tr("qdoc version %1").arg(QT_VERSION_STR); + Location::information(s); +} + +/*! + Processes the qdoc config file \a fileName. This is the + controller for all of qdoc. + */ +static void processQdocconfFile(const QString &fileName) +{ +#ifndef QT_NO_TRANSLATION + QList<QTranslator *> translators; +#endif + + /* + The Config instance represents the configuration data for qdoc. + All the other classes are initialized with the config. Here we + initialize the configuration with some default values. + */ + Config config(tr("qdoc")); + int i = 0; + while (defaults[i].key) { + config.setStringList(defaults[i].key, + QStringList() << defaults[i].value); + ++i; + } + config.setStringList(CONFIG_SYNTAXHIGHLIGHTING, QStringList(highlighting ? "true" : "false")); + config.setStringList(CONFIG_SHOWINTERNAL, + QStringList(showInternal ? "true" : "false")); + config.setStringList(CONFIG_OBSOLETELINKS, + QStringList(obsoleteLinks ? "true" : "false")); + + /* + With the default configuration values in place, load + the qdoc configuration file. Note that the configuration + file may include other configuration files. + + The Location class keeps track of the current location + in the file being processed, mainly for error reporting + purposes. + */ + Location::initialize(config); + config.load(fileName); + + QStringList sourceModules; + sourceModules = config.getStringList(CONFIG_SOURCEMODULES); + Location sourceModulesLocation = config.lastLocation(); + + if (!sourceModules.isEmpty()) { + Location::information(tr("qdoc will generate documentation for the modules found in the sourcemodules variable.")); + foreach (const QString& sourceModule, sourceModules) { + QString qdocconf = sourceModule; + if (!qdocconf.endsWith(".qdocconf")) + qdocconf += "/doc/config/module.qdocconf"; + QFile f(qdocconf); + if (!f.exists()) { + sourceModulesLocation.warning(tr("Can't find module's qdoc config file '%1'").arg(qdocconf)); + } + else { + Location::information(tr(" Including: %1").arg(qdocconf)); + config.load(qdocconf); + } + } + } + + /* + Add the defines to the configuration variables. + */ + QStringList defs = defines + config.getStringList(CONFIG_DEFINES); + config.setStringList(CONFIG_DEFINES,defs); + Location::terminate(); + + QString prevCurrentDir = QDir::currentPath(); + QString dir = QFileInfo(fileName).path(); + if (!dir.isEmpty()) + QDir::setCurrent(dir); + + /* + Initialize all the classes and data structures with the + qdoc configuration. + */ + Location::initialize(config); + Tokenizer::initialize(config); + Doc::initialize(config); + CodeMarker::initialize(config); + CodeParser::initialize(config); + Generator::initialize(config); + +#ifndef QT_NO_TRANSLATION + /* + Load the language translators, if the configuration specifies any. + */ + QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS); + QStringList::Iterator fn = fileNames.begin(); + while (fn != fileNames.end()) { + QTranslator *translator = new QTranslator(0); + if (!translator->load(*fn)) + config.lastLocation().error(tr("Cannot load translator '%1'") + .arg(*fn)); + QCoreApplication::instance()->installTranslator(translator); + translators.append(translator); + ++fn; + } +#endif + + //QSet<QString> outputLanguages = config.getStringSet(CONFIG_OUTPUTLANGUAGES); + + /* + Get the source language (Cpp) from the configuration + and the location in the configuration file where the + source language was set. + */ + QString lang = config.getString(CONFIG_LANGUAGE); + Location langLocation = config.lastLocation(); + + /* + Initialize the tree where all the parsed sources will be stored. + The tree gets built as the source files are parsed, and then the + documentation output is generated by traversing the tree. + */ + Tree *tree = new Tree; + tree->setVersion(config.getString(CONFIG_VERSION)); + + /* + By default, the only output format is HTML. + */ + QSet<QString> outputFormats = config.getOutputFormats(); + Location outputFormatsLocation = config.lastLocation(); + + /* + Read some XML indexes containing definitions from other documentation sets. + */ + QStringList indexFiles = config.getStringList(CONFIG_INDEXES); + tree->readIndexes(indexFiles); + + QSet<QString> excludedDirs; + QSet<QString> excludedFiles; + QSet<QString> headers; + QSet<QString> sources; + QStringList headerList; + QStringList sourceList; + QStringList excludedDirsList; + QStringList excludedFilesList; + + excludedDirsList = config.getCleanPathList(CONFIG_EXCLUDEDIRS); + foreach (const QString &excludeDir, excludedDirsList) { + QString p = QDir::fromNativeSeparators(excludeDir); + excludedDirs.insert(p); + } + + excludedFilesList = config.getCleanPathList(CONFIG_EXCLUDEFILES); + foreach (const QString& excludeFile, excludedFilesList) { + QString p = QDir::fromNativeSeparators(excludeFile); + excludedFiles.insert(p); + } + + headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles); + headers = QSet<QString>::fromList(headerList); + + sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles); + sources = QSet<QString>::fromList(sourceList); + + /* + Parse each header file in the set using the appropriate parser and add it + to the big tree. + */ + QSet<CodeParser *> usedParsers; + + QSet<QString>::ConstIterator h = headers.begin(); + while (h != headers.end()) { + CodeParser *codeParser = CodeParser::parserForHeaderFile(*h); + if (codeParser) { + codeParser->parseHeaderFile(config.location(), *h, tree); + usedParsers.insert(codeParser); + } + ++h; + } + + foreach (CodeParser *codeParser, usedParsers) + codeParser->doneParsingHeaderFiles(tree); + + usedParsers.clear(); + /* + Parse each source text file in the set using the appropriate parser and + add it to the big tree. + */ + QSet<QString>::ConstIterator s = sources.begin(); + while (s != sources.end()) { + CodeParser *codeParser = CodeParser::parserForSourceFile(*s); + if (codeParser) { + codeParser->parseSourceFile(config.location(), *s, tree); + usedParsers.insert(codeParser); + } + ++s; + } + + foreach (CodeParser *codeParser, usedParsers) + codeParser->doneParsingSourceFiles(tree); + + /* + Now the big tree has been built from all the header and + source files. Resolve all the class names, function names, + targets, URLs, links, and other stuff that needs resolving. + */ + tree->resolveGroups(); + tree->resolveQmlModules(); + tree->resolveTargets(tree->root()); + tree->resolveCppToQmlLinks(); + tree->resolveQmlInheritance(); + + /* + The tree is built and all the stuff that needed resolving + has been resolved. Now traverse the tree and generate the + documentation output. More than one output format can be + requested. The tree is traversed for each one. + */ + QSet<QString>::ConstIterator of = outputFormats.begin(); + while (of != outputFormats.end()) { + Generator* generator = Generator::generatorForFormat(*of); + if (generator == 0) + outputFormatsLocation.fatal(tr("Unknown output format '%1'").arg(*of)); + generator->generateTree(tree); + ++of; + } + + /* + Generate the XML tag file, if it was requested. + */ + QString tagFile = config.getString(CONFIG_TAGFILE); + if (!tagFile.isEmpty()) { + tree->generateTagFile(tagFile); + } + + tree->setVersion(""); + Generator::terminate(); + CodeParser::terminate(); + CodeMarker::terminate(); + Doc::terminate(); + Tokenizer::terminate(); + Location::terminate(); + QDir::setCurrent(prevCurrentDir); + +#ifndef QT_NO_TRANSLATION + qDeleteAll(translators); +#endif +#ifdef DEBUG_SHUTDOWN_CRASH + qDebug() << "main(): Delete tree"; +#endif + delete tree; +#ifdef DEBUG_SHUTDOWN_CRASH + qDebug() << "main(): Tree deleted"; +#endif +} + +QT_END_NAMESPACE + +int main(int argc, char **argv) +{ + QT_USE_NAMESPACE + +#ifndef QT_BOOTSTRAPPED + QCoreApplication app(argc, argv); +#endif + + /* + Create code parsers for the languages to be parsed, + and create a tree for C++. + */ + CppCodeParser cppParser; +#ifdef HAVE_DECLARATIVE + QmlCodeParser qmlParser; +#endif + PureDocParser docParser; + + /* + Create code markers for plain text, C++, + javascript, and QML. + */ + PlainCodeMarker plainMarker; + CppCodeMarker cppMarker; +#ifdef HAVE_DECLARATIVE + JsCodeMarker jsMarker; + QmlCodeMarker qmlMarker; +#endif + + HtmlGenerator htmlGenerator; + DitaXmlGenerator ditaxmlGenerator; + + QStringList qdocFiles; + QString opt; + int i = 1; + + while (i < argc) { + opt = argv[i++]; + + if (opt == "-help") { + printHelp(); + return EXIT_SUCCESS; + } + else if (opt == "-version") { + printVersion(); + return EXIT_SUCCESS; + } + else if (opt == "--") { + while (i < argc) + qdocFiles.append(argv[i++]); + } + else if (opt.startsWith("-D")) { + QString define = opt.mid(2); + defines += define; + } + else if (opt == "-highlighting") { + highlighting = true; + } + else if (opt == "-showinternal") { + showInternal = true; + } + else if (opt == "-obsoletelinks") { + obsoleteLinks = true; + } + else if (opt == "-outputdir") { + Config::overrideOutputDir = argv[i]; + i++; + } + else if (opt == "-outputformat") { + Config::overrideOutputFormats.insert(argv[i]); + i++; + } + else { + qdocFiles.append(opt); + } + } + + if (qdocFiles.isEmpty()) { + printHelp(); + return EXIT_FAILURE; + } + + /* + Main loop. + */ + foreach (QString qf, qdocFiles) { + //qDebug() << "PROCESSING:" << qf; + processQdocconfFile(qf); + } + + qDeleteAll(trees); + return EXIT_SUCCESS; +} + diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp new file mode 100644 index 0000000000..13c51c721d --- /dev/null +++ b/src/tools/qdoc/node.cpp @@ -0,0 +1,2780 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "node.h" +#include "tree.h" +#include "codemarker.h" +#include "codeparser.h" +#include <QUuid> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +ExampleNodeMap ExampleNode::exampleNodeMap; +QStringMap Node::operators_; + +/*! + \class Node + \brief The Node class is a node in the Tree. + + A Node represents a class or function or something else + from the source code.. + */ + +/*! + When this Node is destroyed, if it has a parent Node, it + removes itself from the parent node's child list. + */ +Node::~Node() +{ + if (parent_) + parent_->removeChild(this); + if (relatesTo_) + relatesTo_->removeRelated(this); +} + +/*! + Sets this Node's Doc to \a doc. If \a replace is false and + this Node already has a Doc, a warning is reported that the + Doc is being overridden, and it reports where the previous + Doc was found. If \a replace is true, the Doc is replaced + silently. + */ +void Node::setDoc(const Doc& doc, bool replace) +{ + if (!d.isEmpty() && !replace) { + doc.location().warning(tr("Overrides a previous doc")); + d.location().warning(tr("(The previous doc is here)")); + } + d = doc; +} + +/*! + Construct a node with the given \a type and having the + given \a parent and \a name. The new node is added to the + parent's child list. + */ +Node::Node(Type type, InnerNode *parent, const QString& name) + : nodeType_(type), + access_(Public), + safeness_(UnspecifiedSafeness), + pageType_(NoPageType), + status_(Commendable), + parent_(parent), + relatesTo_(0), + name_(name) +{ + if (parent_) + parent_->addChild(this); + outSubDir_ = CodeParser::currentOutputSubdirectory(); + if (operators_.isEmpty()) { + operators_.insert("++","inc"); + operators_.insert("--","dec"); + operators_.insert("==","eq"); + operators_.insert("!=","ne"); + operators_.insert("<<","lt-lt"); + operators_.insert(">>","gt-gt"); + operators_.insert("+=","plus-assign"); + operators_.insert("-=","minus-assign"); + operators_.insert("*=","mult-assign"); + operators_.insert("/=","div-assign"); + operators_.insert("%=","mod-assign"); + operators_.insert("&=","bitwise-and-assign"); + operators_.insert("|=","bitwise-or-assign"); + operators_.insert("^=","bitwise-xor-assign"); + operators_.insert("<<=","bitwise-left-shift-assign"); + operators_.insert(">>=","bitwise-right-shift-assign"); + operators_.insert("||","logical-or"); + operators_.insert("&&","logical-and"); + operators_.insert("()","call"); + operators_.insert("[]","subscript"); + operators_.insert("->","pointer"); + operators_.insert("->*","pointer-star"); + operators_.insert("+","plus"); + operators_.insert("-","minus"); + operators_.insert("*","mult"); + operators_.insert("/","div"); + operators_.insert("%","mod"); + operators_.insert("|","bitwise-or"); + operators_.insert("&","bitwise-and"); + operators_.insert("^","bitwise-xor"); + operators_.insert("!","not"); + operators_.insert("~","bitwise-not"); + operators_.insert("<=","lt-eq"); + operators_.insert(">=","gt-eq"); + operators_.insert("<","lt"); + operators_.insert(">","gt"); + operators_.insert("=","assign"); + operators_.insert(",","comma"); + operators_.insert("delete[]","delete-array"); + operators_.insert("delete","delete"); + operators_.insert("new[]","new-array"); + operators_.insert("new","new"); + } +} + +/*! + Returns the node's URL. + */ +QString Node::url() const +{ + return url_; +} + +/*! + Sets the node's URL to \a url + */ +void Node::setUrl(const QString &url) +{ + url_ = url; +} + +/*! + Returns this node's page type as a string, for use as an + attribute value in XML or HTML. + */ +QString Node::pageTypeString() const +{ + return pageTypeString(pageType_); +} + +/*! + Returns the page type \a t as a string, for use as an + attribute value in XML or HTML. + */ +QString Node::pageTypeString(unsigned t) +{ + switch ((PageType)t) { + case Node::ApiPage: + return "api"; + case Node::ArticlePage: + return "article"; + case Node::ExamplePage: + return "example"; + case Node::HowToPage: + return "howto"; + case Node::OverviewPage: + return "overview"; + case Node::TutorialPage: + return "tutorial"; + case Node::FAQPage: + return "faq"; + case Node::DitaMapPage: + return "ditamap"; + default: + return "article"; + } +} + +/*! + Returns this node's type as a string for use as an + attribute value in XML or HTML. + */ +QString Node::nodeTypeString() const +{ + return nodeTypeString(type()); +} + +/*! + Returns the node type \a t as a string for use as an + attribute value in XML or HTML. + */ +QString Node::nodeTypeString(unsigned t) +{ + switch ((Type)t) { + case Namespace: + return "namespace"; + case Class: + return "class"; + case Fake: + return "fake"; + case Enum: + return "enum"; + case Typedef: + return "typedef"; + case Function: + return "function"; + case Property: + return "property"; + case Variable: + return "variable"; + case Target: + return "target"; + case QmlProperty: + return "QML property"; + case QmlSignal: + return "QML signal"; + case QmlSignalHandler: + return "QML signal handler"; + case QmlMethod: + return "QML method"; + default: + break; + } + return ""; +} + +/*! + Returns this node's subtype as a string for use as an + attribute value in XML or HTML. This is only useful + in the case where the node type is Fake. + */ +QString Node::nodeSubtypeString() const +{ + return nodeSubtypeString(subType()); +} + +/*! + Returns the node subtype \a t as a string for use as an + attribute value in XML or HTML. This is only useful + in the case where the node type is Fake. + */ +QString Node::nodeSubtypeString(unsigned t) +{ + switch ((SubType)t) { + case Example: + return "example"; + case HeaderFile: + return "header file"; + case File: + return "file"; + case Image: + return "image"; + case Group: + return "group"; + case Module: + return "module"; + case Page: + return "page"; + case ExternalPage: + return "external page"; + case QmlClass: + return "QML class"; + case QmlPropertyGroup: + return "QML property group"; + case QmlBasicType: + return "QML basic type"; + case QmlModule: + return "QML module"; + case DitaMap: + return "ditamap"; + case Collision: + return "collision"; + case NoSubType: + default: + break; + } + return ""; +} + +/*! + Set the page type according to the string \a t. + */ +void Node::setPageType(const QString& t) +{ + if ((t == "API") || (t == "api")) + pageType_ = ApiPage; + else if (t == "howto") + pageType_ = HowToPage; + else if (t == "overview") + pageType_ = OverviewPage; + else if (t == "tutorial") + pageType_ = TutorialPage; + else if (t == "howto") + pageType_ = HowToPage; + else if (t == "article") + pageType_ = ArticlePage; + else if (t == "example") + pageType_ = ExamplePage; + else if (t == "ditamap") + pageType_ = DitaMapPage; +} + +/*! + Sets the pointer to the node that this node relates to. + */ +void Node::setRelates(InnerNode *pseudoParent) +{ + if (relatesTo_) { + relatesTo_->removeRelated(this); + } + relatesTo_ = pseudoParent; + pseudoParent->related_.append(this); +} + +/*! + This function creates a pair that describes a link. + The pair is composed from \a link and \a desc. The + \a linkType is the map index the pair is filed under. + */ +void Node::setLink(LinkType linkType, const QString &link, const QString &desc) +{ + QPair<QString,QString> linkPair; + linkPair.first = link; + linkPair.second = desc; + linkMap[linkType] = linkPair; +} + +/*! + Sets the information about the project and version a node was introduced + in. The string is simplified, removing excess whitespace before being + stored. +*/ +void Node::setSince(const QString &since) +{ + sinc = since.simplified(); +} + +/*! + Returns a string representing the access specifier. + */ +QString Node::accessString() const +{ + switch (access_) { + case Protected: + return "protected"; + case Private: + return "private"; + case Public: + default: + break; + } + return "public"; +} + +/*! + Extract a class name from the type \a string and return it. + */ +QString Node::extractClassName(const QString &string) const +{ + QString result; + for (int i=0; i<=string.size(); ++i) { + QChar ch; + if (i != string.size()) + ch = string.at(i); + + QChar lower = ch.toLower(); + if ((lower >= QLatin1Char('a') && lower <= QLatin1Char('z')) || + ch.digitValue() >= 0 || + ch == QLatin1Char('_') || + ch == QLatin1Char(':')) { + result += ch; + } + else if (!result.isEmpty()) { + if (result != QLatin1String("const")) + return result; + result.clear(); + } + } + return result; +} + +/*! + Returns a string representing the access specifier. + */ +QString RelatedClass::accessString() const +{ + switch (access) { + case Node::Protected: + return "protected"; + case Node::Private: + return "private"; + case Node::Public: + default: + break; + } + return "public"; +} + +/*! + Returns the inheritance status. + */ +Node::Status Node::inheritedStatus() const +{ + Status parentStatus = Commendable; + if (parent_) + parentStatus = parent_->inheritedStatus(); + return (Status)qMin((int)status_, (int)parentStatus); +} + +/*! + Returns the thread safeness value for whatever this node + represents. But if this node has a parent and the thread + safeness value of the parent is the same as the thread + safeness value of this node, what is returned is the + value \c{UnspecifiedSafeness}. Why? + */ +Node::ThreadSafeness Node::threadSafeness() const +{ + if (parent_ && safeness_ == parent_->inheritedThreadSafeness()) + return UnspecifiedSafeness; + return safeness_; +} + +/*! + If this node has a parent, the parent's thread safeness + value is returned. Otherwise, this node's thread safeness + value is returned. Why? + */ +Node::ThreadSafeness Node::inheritedThreadSafeness() const +{ + if (parent_ && safeness_ == UnspecifiedSafeness) + return parent_->inheritedThreadSafeness(); + return safeness_; +} + +/*! + Returns the sanitized file name without the path. + If the the file is an html file, the html suffix + is removed. Why? + */ +QString Node::fileBase() const +{ + QString base = name(); + if (base.endsWith(".html")) + base.chop(5); + base.replace(QRegExp("[^A-Za-z0-9]+"), " "); + base = base.trimmed(); + base.replace(QLatin1Char(' '), QLatin1Char('-')); + return base.toLower(); +} + +/*! + Returns this node's Universally Unique IDentifier as a + QString. Creates the UUID first, if it has not been created. + */ +QString Node::guid() const +{ + if (uuid.isEmpty()) + uuid = idForNode(); + return uuid; +} + +#if 0 +// fossil +QUuid quuid = QUuid::createUuid(); +QString t = quuid.toString(); +uuid = "id-" + t.mid(1,t.length()-2); +#endif + +/*! + Composes a string to be used as an href attribute in DITA + XML. It is composed of the file name and the UUID separated + by a '#'. If this node is a class node, the file name is + taken from this node; if this node is a function node, the + file name is taken from the parent node of this node. + */ +QString Node::ditaXmlHref() +{ + QString href; + if ((type() == Function) || + (type() == Property) || + (type() == Variable)) { + href = parent()->fileBase(); + } + else { + href = fileBase(); + } + if (!href.endsWith(".xml")) + href += ".xml"; + return href + QLatin1Char('#') + guid(); +} + +/*! + If this node is a QML class node, return a pointer to it. + If it is a child of a QML class node, return a pointer to + the QML class node. Otherwise, return 0; + */ +const QmlClassNode* Node::qmlClassNode() const +{ + if (isQmlNode()) { + const Node* n = this; + while (n && n->subType() != Node::QmlClass) + n = n->parent(); + if (n && n->subType() == Node::QmlClass) + return static_cast<const QmlClassNode*>(n); + } + return 0; +} + +/*! + If this node is a QML node, find its QML class node, + and return a pointer to the C++ class node from the + QML class node. That pointer will be null if the QML + class node is a component. It will be non-null if + the QML class node is a QML element. + */ +const ClassNode* Node::declarativeCppNode() const +{ + const QmlClassNode* qcn = qmlClassNode(); + if (qcn) + return qcn->classNode(); + return 0; +} + +/*! + \class InnerNode + */ + +/*! + The inner node destructor deletes the children and removes + this node from its related nodes. + */ +InnerNode::~InnerNode() +{ + deleteChildren(); + removeFromRelated(); +} + +/*! + Find the node in this node's children that has the + given \a name. If this node is a QML class node, be + sure to also look in the children of its property + group nodes. Return the matching node or 0. + */ +Node *InnerNode::findNode(const QString& name) +{ + Node *node = childMap.value(name); + if (node && node->subType() != QmlPropertyGroup) + return node; + if ((type() == Fake) && (subType() == QmlClass)) { + for (int i=0; i<children.size(); ++i) { + Node* n = children.at(i); + if (n->subType() == QmlPropertyGroup) { + node = static_cast<InnerNode*>(n)->findNode(name); + if (node) + return node; + } + } + } + return primaryFunctionMap.value(name); +} +void InnerNode::findNodes(const QString& name, QList<Node*>& n) +{ + n.clear(); + Node* node = 0; + QList<Node*> nodes = childMap.values(name); + /* + <sigh> If this node's child map contains no nodes named + name, then if this node is a QML class, seach each of its + property group nodes for a node named name. If a match is + found, append it to the output list and return immediately. + */ + if (nodes.isEmpty()) { + if ((type() == Fake) && (subType() == QmlClass)) { + for (int i=0; i<children.size(); ++i) { + node = children.at(i); + if (node->subType() == QmlPropertyGroup) { + node = static_cast<InnerNode*>(node)->findNode(name); + if (node) { + n.append(node); + return; + } + } + } + } + } + else { + /* + If the childMap does contain one or more nodes named + name, traverse the list of matching nodes. Append each + matching node that is not a property group node to the + output list. Search each property group node for a node + named name and append that node to the output list. + This is overkill, I think, but should produce a useful + list. + */ + for (int i=0; i<nodes.size(); ++i) { + node = nodes.at(i); + if (node->subType() != QmlPropertyGroup) + n.append(node); + else { + node = static_cast<InnerNode*>(node)->findNode(name); + if (node) + n.append(node); + } + } + } + if (!n.isEmpty()) + return; + node = primaryFunctionMap.value(name); + if (node) + n.append(node); +} + +/*! + Find the node in this node's children that has the given \a name. If + this node is a QML class node, be sure to also look in the children + of its property group nodes. Return the matching node or 0. + + If \a qml is true, only match a node for which node->isQmlNode() + returns true. If \a qml is false, only match a node for which + node->isQmlNode() returns false. + */ +Node* InnerNode::findNode(const QString& name, bool qml) +{ + QList<Node*> nodes = childMap.values(name); + if (!nodes.isEmpty()) { + for (int i=0; i<nodes.size(); ++i) { + Node* node = nodes.at(i); + if (!qml) { + if (!node->isQmlNode()) + return node; + } + else if (node->isQmlNode() && (node->subType() != QmlPropertyGroup)) + return node; + } + } + if (qml && (type() == Fake) && (subType() == QmlClass)) { + for (int i=0; i<children.size(); ++i) { + Node* node = children.at(i); + if (node->subType() == QmlPropertyGroup) { + node = static_cast<InnerNode*>(node)->findNode(name); + if (node) + return node; + } + } + } + return primaryFunctionMap.value(name); +} + +/*! + Same as the other findNode(), but if the node with the + specified \a name is not of the specified \a type, return + 0. + */ +Node *InnerNode::findNode(const QString& name, Type type) +{ + if (type == Function) { + return primaryFunctionMap.value(name); + } + else { + Node *node = childMap.value(name); + if (node && node->type() == type) { + return node; + } + else { + return 0; + } + } +} + +/*! + Find the function node in this node for the function named \a name. + */ +FunctionNode *InnerNode::findFunctionNode(const QString& name) +{ + return static_cast<FunctionNode *>(primaryFunctionMap.value(name)); +} + +/*! + Find the function node in this node that has the same name as \a clone. + */ +FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) +{ + QMap<QString, Node *>::ConstIterator c = + primaryFunctionMap.find(clone->name()); + if (c != primaryFunctionMap.end()) { + if (isSameSignature(clone, (FunctionNode *) *c)) { + return (FunctionNode *) *c; + } + else if (secondaryFunctionMap.contains(clone->name())) { + const NodeList& secs = secondaryFunctionMap[clone->name()]; + NodeList::ConstIterator s = secs.begin(); + while (s != secs.end()) { + if (isSameSignature(clone, (FunctionNode *) *s)) + return (FunctionNode *) *s; + ++s; + } + } + } + return 0; +} + +/*! + Returns the list of keys from the primary function map. + */ +QStringList InnerNode::primaryKeys() +{ + QStringList t; + QMap<QString, Node*>::iterator i = primaryFunctionMap.begin(); + while (i != primaryFunctionMap.end()) { + t.append(i.key()); + ++i; + } + return t; +} + +/*! + Returns the list of keys from the secondary function map. + */ +QStringList InnerNode::secondaryKeys() +{ + QStringList t; + QMap<QString, NodeList>::iterator i = secondaryFunctionMap.begin(); + while (i != secondaryFunctionMap.end()) { + t.append(i.key()); + ++i; + } + return t; +} + +/*! + */ +void InnerNode::setOverload(const FunctionNode *func, bool overlode) +{ + Node *node = (Node *) func; + Node *&primary = primaryFunctionMap[func->name()]; + + if (secondaryFunctionMap.contains(func->name())) { + NodeList& secs = secondaryFunctionMap[func->name()]; + if (overlode) { + if (primary == node) { + primary = secs.first(); + secs.erase(secs.begin()); + secs.append(node); + } + else { + secs.removeAll(node); + secs.append(node); + } + } + else { + if (primary != node) { + secs.removeAll(node); + secs.prepend(primary); + primary = node; + } + } + } +} + +/*! + Mark all child nodes that have no documentation as having + private access and internal status. qdoc will then ignore + them for documentation purposes. + + \note Exception: Name collision nodes are not marked + private/internal. + */ +void InnerNode::makeUndocumentedChildrenInternal() +{ + foreach (Node *child, childNodes()) { + if (child->doc().isEmpty()) { + if (child->subType() != Node::Collision) { + child->setAccess(Node::Private); + child->setStatus(Node::Internal); + } + } + } +} + +/*! + In each child node that is a collision node, + clear the current child pointer. + */ +void InnerNode::clearCurrentChildPointers() +{ + foreach (Node* child, childNodes()) { + if (child->subType() == Collision) { + child->clearCurrentChild(); + } + } +} + +/*! + */ +void InnerNode::normalizeOverloads() +{ + QMap<QString, Node *>::Iterator p1 = primaryFunctionMap.begin(); + while (p1 != primaryFunctionMap.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p1; + if (secondaryFunctionMap.contains(primaryFunc->name()) && + (primaryFunc->status() != Commendable || + primaryFunc->access() == Private)) { + + NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; + NodeList::ConstIterator s = secs.begin(); + while (s != secs.end()) { + FunctionNode *secondaryFunc = (FunctionNode *) *s; + + // Any non-obsolete, non-compatibility, non-private functions + // (i.e, visible functions) are preferable to the primary + // function. + + if (secondaryFunc->status() == Commendable && + secondaryFunc->access() != Private) { + + *p1 = secondaryFunc; + int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc); + secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc); + break; + } + ++s; + } + } + ++p1; + } + + QMap<QString, Node *>::ConstIterator p = primaryFunctionMap.begin(); + while (p != primaryFunctionMap.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p; + if (primaryFunc->isOverload()) + primaryFunc->ove = false; + if (secondaryFunctionMap.contains(primaryFunc->name())) { + NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; + NodeList::ConstIterator s = secs.begin(); + while (s != secs.end()) { + FunctionNode *secondaryFunc = (FunctionNode *) *s; + if (!secondaryFunc->isOverload()) + secondaryFunc->ove = true; + ++s; + } + } + ++p; + } + + NodeList::ConstIterator c = childNodes().begin(); + while (c != childNodes().end()) { + if ((*c)->isInnerNode()) + ((InnerNode *) *c)->normalizeOverloads(); + ++c; + } +} + +/*! + */ +void InnerNode::removeFromRelated() +{ + while (!related_.isEmpty()) { + Node *p = static_cast<Node *>(related_.takeFirst()); + + if (p != 0 && p->relates() == this) p->clearRelated(); + } +} + +/*! + Deletes all this node's children. + */ +void InnerNode::deleteChildren() +{ + NodeList childrenCopy = children; // `children` will be changed in ~Node() + qDeleteAll(childrenCopy); +} + +/*! \fn bool InnerNode::isInnerNode() const + Returns true because this is an inner node. + */ + +/*! + */ +const Node *InnerNode::findNode(const QString& name) const +{ + InnerNode *that = (InnerNode *) this; + return that->findNode(name); +} + +/*! + If \a qml is true, only match a node for which node->isQmlNode() + returns true. If \a qml is false, only match a node for which + node->isQmlNode() returns false. + */ +const Node* InnerNode::findNode(const QString& name, bool qml) const +{ + InnerNode*that = (InnerNode*) this; + return that->findNode(name, qml); +} + +/*! + */ +const Node *InnerNode::findNode(const QString& name, Type type) const +{ + InnerNode *that = (InnerNode *) this; + return that->findNode(name, type); +} + +/*! + Find the function node in this node that has the given \a name. + */ +const FunctionNode *InnerNode::findFunctionNode(const QString& name) const +{ + InnerNode *that = (InnerNode *) this; + return that->findFunctionNode(name); +} + +/*! + Find the function node in this node that has the same name as \a clone. + */ +const FunctionNode *InnerNode::findFunctionNode(const FunctionNode *clone) const +{ + InnerNode *that = (InnerNode *) this; + return that->findFunctionNode(clone); +} + +/*! + */ +const EnumNode *InnerNode::findEnumNodeForValue(const QString &enumValue) const +{ + foreach (const Node *node, enumChildren) { + const EnumNode *enume = static_cast<const EnumNode *>(node); + if (enume->hasItem(enumValue)) + return enume; + } + return 0; +} + +/*! + Returnds the sequence number of the function node \a func + in the list of overloaded functions for a class, such that + all the functions have the same name as the \a func. + */ +int InnerNode::overloadNumber(const FunctionNode *func) const +{ + Node *node = (Node *) func; + if (primaryFunctionMap[func->name()] == node) { + return 1; + } + else { + return secondaryFunctionMap[func->name()].indexOf(node) + 2; + } +} + +/*! + Returns the number of member functions of a class such that + the functions are all named \a funcName. + */ +int InnerNode::numOverloads(const QString& funcName) const +{ + if (primaryFunctionMap.contains(funcName)) { + return secondaryFunctionMap[funcName].count() + 1; + } + else { + return 0; + } +} + +/*! + Returns a node list containing all the member functions of + some class such that the functions overload the name \a funcName. + */ +NodeList InnerNode::overloads(const QString &funcName) const +{ + NodeList result; + Node *primary = primaryFunctionMap.value(funcName); + if (primary) { + result << primary; + result += secondaryFunctionMap[funcName]; + } + return result; +} + +/*! + Construct an inner node (i.e., not a leaf node) of the + given \a type and having the given \a parent and \a name. + */ +InnerNode::InnerNode(Type type, InnerNode *parent, const QString& name) + : Node(type, parent, name) +{ + switch (type) { + case Class: + case Namespace: + setPageType(ApiPage); + break; + default: + break; + } +} + +/*! + Appends an \a include file to the list of include files. + */ +void InnerNode::addInclude(const QString& include) +{ + inc.append(include); +} + +/*! + Sets the list of include files to \a includes. + */ +void InnerNode::setIncludes(const QStringList& includes) +{ + inc = includes; +} + +/*! + f1 is always the clone + */ +bool InnerNode::isSameSignature(const FunctionNode *f1, const FunctionNode *f2) +{ + if (f1->parameters().count() != f2->parameters().count()) + return false; + if (f1->isConst() != f2->isConst()) + return false; + + QList<Parameter>::ConstIterator p1 = f1->parameters().begin(); + QList<Parameter>::ConstIterator p2 = f2->parameters().begin(); + while (p2 != f2->parameters().end()) { + if ((*p1).hasType() && (*p2).hasType()) { + if ((*p1).rightType() != (*p2).rightType()) + return false; + + QString t1 = p1->leftType(); + QString t2 = p2->leftType(); + + if (t1.length() < t2.length()) + qSwap(t1, t2); + + /* + ### hack for C++ to handle superfluous + "Foo::" prefixes gracefully + */ + if (t1 != t2 && t1 != (f2->parent()->name() + "::" + t2)) + return false; + } + ++p1; + ++p2; + } + return true; +} + +/*! + Adds the \a child to this node's child list. + */ +void InnerNode::addChild(Node *child) +{ + children.append(child); + if ((child->type() == Function) || (child->type() == QmlMethod)) { + FunctionNode *func = (FunctionNode *) child; + if (!primaryFunctionMap.contains(func->name())) { + primaryFunctionMap.insert(func->name(), func); + } + else { + NodeList &secs = secondaryFunctionMap[func->name()]; + secs.append(func); + } + } + else { + if (child->type() == Enum) + enumChildren.append(child); + childMap.insertMulti(child->name(), child); + } +} + +/*! + */ +void InnerNode::removeChild(Node *child) +{ + children.removeAll(child); + enumChildren.removeAll(child); + if (child->type() == Function) { + QMap<QString, Node *>::Iterator prim = + primaryFunctionMap.find(child->name()); + NodeList& secs = secondaryFunctionMap[child->name()]; + if (prim != primaryFunctionMap.end() && *prim == child) { + if (secs.isEmpty()) { + primaryFunctionMap.remove(child->name()); + } + else { + primaryFunctionMap.insert(child->name(), secs.takeFirst()); + } + } + else { + secs.removeAll(child); + } + } + QMap<QString, Node *>::Iterator ent = childMap.find(child->name()); + while (ent != childMap.end() && ent.key() == child->name()) { + if (*ent == child) { + childMap.erase(ent); + break; + } + ++ent; + } +} + +/*! + Find the module (QtCore, QtGui, etc.) to which the class belongs. + We do this by obtaining the full path to the header file's location + and examine everything between "src/" and the filename. This is + semi-dirty because we are assuming a particular directory structure. + + This function is only really useful if the class's module has not + been defined in the header file with a QT_MODULE macro or with an + \inmodule command in the documentation. +*/ +QString Node::moduleName() const +{ + if (!mod.isEmpty()) + return mod; + + QString path = location().filePath(); + QString pattern = QString("src") + QDir::separator(); + int start = path.lastIndexOf(pattern); + + if (start == -1) + return ""; + + QString moduleDir = path.mid(start + pattern.size()); + int finish = moduleDir.indexOf(QDir::separator()); + + if (finish == -1) + return ""; + + QString moduleName = moduleDir.left(finish); + + if (moduleName == "corelib") + return "QtCore"; + else if (moduleName == "uitools") + return "QtUiTools"; + else if (moduleName == "gui") + return "QtGui"; + else if (moduleName == "network") + return "QtNetwork"; + else if (moduleName == "opengl") + return "QtOpenGL"; + else if (moduleName == "qt3support") + return "Qt3Support"; + else if (moduleName == "svg") + return "QtSvg"; + else if (moduleName == "sql") + return "QtSql"; + else if (moduleName == "qtestlib") + return "QtTest"; + else if (moduleDir.contains("webkit")) + return "QtWebKit"; + else if (moduleName == "xml") + return "QtXml"; + else + return ""; +} + +/*! + */ +void InnerNode::removeRelated(Node *pseudoChild) +{ + related_.removeAll(pseudoChild); +} + +/*! + \class LeafNode + */ + +/*! + Returns false because this is a LeafNode. + */ +bool LeafNode::isInnerNode() const +{ + return false; +} + +/*! + Constructs a leaf node named \a name of the specified + \a type. The new leaf node becomes a child of \a parent. + */ +LeafNode::LeafNode(Type type, InnerNode *parent, const QString& name) + : Node(type, parent, name) +{ + switch (type) { + case Enum: + case Function: + case Typedef: + case Variable: + case QmlProperty: + case QmlSignal: + case QmlSignalHandler: + case QmlMethod: + setPageType(ApiPage); + break; + default: + break; + } +} + +/*! + This constructor should only be used when this node's parent + is meant to be \a parent, but this node is not to be listed + as a child of \a parent. It is currently only used for the + documentation case where a \e{qmlproperty} command is used + to override the QML definition of a QML property. + */ +LeafNode::LeafNode(InnerNode* parent, Type type, const QString& name) + : Node(type, 0, name) +{ + setParent(parent); + switch (type) { + case Enum: + case Function: + case Typedef: + case Variable: + case QmlProperty: + case QmlSignal: + case QmlSignalHandler: + case QmlMethod: + setPageType(ApiPage); + break; + default: + break; + } +} + + +/*! + \class NamespaceNode + */ + +/*! + Constructs a namespace node. + */ +NamespaceNode::NamespaceNode(InnerNode *parent, const QString& name) + : InnerNode(Namespace, parent, name) +{ + setPageType(ApiPage); +} + +/*! + \class ClassNode + \brief This class represents a C++ class. + */ + +/*! + Constructs a class node. A class node will generate an API page. + */ +ClassNode::ClassNode(InnerNode *parent, const QString& name) + : InnerNode(Class, parent, name) +{ + hidden = false; + abstract = false; + qmlelement = 0; + setPageType(ApiPage); +} + +/*! + */ +void ClassNode::addBaseClass(Access access, + ClassNode *node, + const QString &dataTypeWithTemplateArgs) +{ + bases.append(RelatedClass(access, node, dataTypeWithTemplateArgs)); + node->derived.append(RelatedClass(access, this)); +} + +/*! + */ +void ClassNode::fixBaseClasses() +{ + int i; + i = 0; + QSet<ClassNode *> found; + + // Remove private and duplicate base classes. + while (i < bases.size()) { + ClassNode* bc = bases.at(i).node; + if (bc->access() == Node::Private || found.contains(bc)) { + RelatedClass rc = bases.at(i); + bases.removeAt(i); + ignoredBases.append(rc); + const QList<RelatedClass> &bb = bc->baseClasses(); + for (int j = bb.size() - 1; j >= 0; --j) + bases.insert(i, bb.at(j)); + } + else { + ++i; + } + found.insert(bc); + } + + i = 0; + while (i < derived.size()) { + ClassNode* dc = derived.at(i).node; + if (dc->access() == Node::Private) { + derived.removeAt(i); + const QList<RelatedClass> &dd = dc->derivedClasses(); + for (int j = dd.size() - 1; j >= 0; --j) + derived.insert(i, dd.at(j)); + } + else { + ++i; + } + } +} + +/*! + Search the child list to find the property node with the + specified \a name. + */ +const PropertyNode *ClassNode::findPropertyNode(const QString &name) const +{ + const Node *n = findNode(name, Node::Property); + + if (n) + return static_cast<const PropertyNode*>(n); + + const PropertyNode *pn = 0; + + const QList<RelatedClass> &bases = baseClasses(); + if (!bases.isEmpty()) { + for (int i = 0; i < bases.size(); ++i) { + const ClassNode *cn = bases[i].node; + pn = cn->findPropertyNode(name); + if (pn) + break; + } + } + const QList<RelatedClass>& ignoredBases = ignoredBaseClasses(); + if (!ignoredBases.isEmpty()) { + for (int i = 0; i < ignoredBases.size(); ++i) { + const ClassNode *cn = ignoredBases[i].node; + pn = cn->findPropertyNode(name); + if (pn) + break; + } + } + + return pn; +} + +/*! + This function does a recursive search of this class node's + base classes looking for one that has a QML element. If it + finds one, it returns the pointer to that QML element. If + it doesn't find one, it returns null. + */ +const QmlClassNode* ClassNode::findQmlBaseNode() const +{ + const QmlClassNode* result = 0; + const QList<RelatedClass>& bases = baseClasses(); + + if (!bases.isEmpty()) { + for (int i = 0; i < bases.size(); ++i) { + const ClassNode* cn = bases[i].node; + if (cn && cn->qmlElement()) { + return cn->qmlElement(); + } + } + for (int i = 0; i < bases.size(); ++i) { + const ClassNode* cn = bases[i].node; + if (cn) { + result = cn->findQmlBaseNode(); + if (result != 0) { + return result; + } + } + } + } + return result; +} + +/*! + \class FakeNode + */ + +/*! + The type of a FakeNode is Fake, and it has a \a subtype, + which specifies the type of FakeNode. The page type for + the page index is set here. + */ +FakeNode::FakeNode(InnerNode* parent, const QString& name, SubType subtype, Node::PageType ptype) + : InnerNode(Fake, parent, name), nodeSubtype_(subtype) +{ + switch (subtype) { + case Page: + setPageType(ptype); + break; + case DitaMap: + setPageType(ptype); + break; + case Module: + case QmlModule: + case Group: + setPageType(OverviewPage); + break; + case QmlClass: + case QmlBasicType: + setPageType(ApiPage); + break; + case Example: + setPageType(ExamplePage); + break; + case Collision: + setPageType(ptype); + break; + default: + break; + } +} + +/*! + Returns the fake node's title. This is used for the page title. +*/ +QString FakeNode::title() const +{ + return title_; +} + +/*! + Returns the fake node's full title, which is usually + just title(), but for some SubType values is different + from title() + */ +QString FakeNode::fullTitle() const +{ + if (nodeSubtype_ == File) { + if (title().isEmpty()) + return name().mid(name().lastIndexOf('/') + 1) + " Example File"; + else + return title(); + } + else if (nodeSubtype_ == Image) { + if (title().isEmpty()) + return name().mid(name().lastIndexOf('/') + 1) + " Image File"; + else + return title(); + } + else if ((nodeSubtype_ == HeaderFile) || (nodeSubtype_ == Collision)) { + if (title().isEmpty()) + return name(); + else + return name() + " - " + title(); + } + else { + return title(); + } +} + +/*! + Returns the subtitle. + */ +QString FakeNode::subTitle() const +{ + if (!subtitle_.isEmpty()) + return subtitle_; + + if ((nodeSubtype_ == File) || (nodeSubtype_ == Image)) { + if (title().isEmpty() && name().contains(QLatin1Char('/'))) + return name(); + } + return QString(); +} + +/*! + The constructor calls the FakeNode constructor with + \a parent, \a name, and Node::Example. + */ +ExampleNode::ExampleNode(InnerNode* parent, const QString& name) + : FakeNode(parent, name, Node::Example, Node::ExamplePage) +{ + // nothing +} + +/*! + \class EnumNode + */ + +/*! + The constructor for the node representing an enum type + has a \a parent class and an enum type \a name. + */ +EnumNode::EnumNode(InnerNode *parent, const QString& name) + : LeafNode(Enum, parent, name), ft(0) +{ +} + +/*! + Add \a item to the enum type's item list. + */ +void EnumNode::addItem(const EnumItem& item) +{ + itms.append(item); + names.insert(item.name()); +} + +/*! + Returns the access level of the enumeration item named \a name. + Apparently it is private if it has been omitted by qdoc's + omitvalue command. Otherwise it is public. + */ +Node::Access EnumNode::itemAccess(const QString &name) const +{ + if (doc().omitEnumItemNames().contains(name)) + return Private; + return Public; +} + +/*! + Returns the enum value associated with the enum \a name. + */ +QString EnumNode::itemValue(const QString &name) const +{ + foreach (const EnumItem &item, itms) { + if (item.name() == name) + return item.value(); + } + return QString(); +} + +/*! + \class TypedefNode + */ + +/*! + */ +TypedefNode::TypedefNode(InnerNode *parent, const QString& name) + : LeafNode(Typedef, parent, name), ae(0) +{ +} + +/*! + */ +void TypedefNode::setAssociatedEnum(const EnumNode *enume) +{ + ae = enume; +} + +/*! + \class Parameter + \brief The class Parameter contains one parameter. + + A parameter can be a function parameter or a macro + parameter. + */ + +/*! + Constructs this parameter from the left and right types + \a leftType and rightType, the parameter \a name, and the + \a defaultValue. In practice, \a rightType is not used, + and I don't know what is was meant for. + */ +Parameter::Parameter(const QString& leftType, + const QString& rightType, + const QString& name, + const QString& defaultValue) + : lef(leftType), rig(rightType), nam(name), def(defaultValue) +{ +} + +/*! + The standard copy constructor copies the strings from \a p. + */ +Parameter::Parameter(const Parameter& p) + : lef(p.lef), rig(p.rig), nam(p.nam), def(p.def) +{ +} + +/*! + Assigning Parameter \a p to this Parameter copies the + strings across. + */ +Parameter& Parameter::operator=(const Parameter& p) +{ + lef = p.lef; + rig = p.rig; + nam = p.nam; + def = p.def; + return *this; +} + +/*! + Reconstructs the text describing the parameter and + returns it. If \a value is true, the default value + will be included, if there is one. + */ +QString Parameter::reconstruct(bool value) const +{ + QString p = lef + rig; + if (!p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) && !p.endsWith(QChar(' '))) + p += QLatin1Char(' '); + p += nam; + if (value && !def.isEmpty()) + p += " = " + def; + return p; +} + + +/*! + \class FunctionNode + */ + +/*! + Construct a function node for a C++ function. It's parent + is \a parent, and it's name is \a name. + */ +FunctionNode::FunctionNode(InnerNode *parent, const QString& name) + : LeafNode(Function, parent, name), + met(Plain), + vir(NonVirtual), + con(false), + sta(false), + ove(false), + reimp(false), + attached_(false), + rf(0), + ap(0) +{ + // nothing. +} + +/*! + Construct a function node for a QML method or signal, specified + by \a type. It's parent is \a parent, and it's name is \a name. + If \a attached is true, it is an attached method or signal. + */ +FunctionNode::FunctionNode(Type type, InnerNode *parent, const QString& name, bool attached) + : LeafNode(type, parent, name), + met(Plain), + vir(NonVirtual), + con(false), + sta(false), + ove(false), + reimp(false), + attached_(attached), + rf(0), + ap(0) +{ + // nothing. +} + +/*! + Sets the \a virtualness of this function. If the \a virtualness + is PureVirtual, and if the parent() is a ClassNode, set the parent's + \e abstract flag to true. + */ +void FunctionNode::setVirtualness(Virtualness virtualness) +{ + vir = virtualness; + if ((virtualness == PureVirtual) && parent() && + (parent()->type() == Node::Class)) + parent()->setAbstract(true); +} + +/*! + */ +void FunctionNode::setOverload(bool overlode) +{ + parent()->setOverload(this, overlode); + ove = overlode; +} + +/*! + Sets the function node's reimplementation flag to \a r. + When \a r is true, it is supposed to mean that this function + is a reimplementation of a virtual function in a base class, + but it really just means the \e reimp command was seen in the + qdoc comment. + */ +void FunctionNode::setReimp(bool r) +{ + reimp = r; +} + +/*! + */ +void FunctionNode::addParameter(const Parameter& parameter) +{ + params.append(parameter); +} + +/*! + */ +void FunctionNode::borrowParameterNames(const FunctionNode *source) +{ + QList<Parameter>::Iterator t = params.begin(); + QList<Parameter>::ConstIterator s = source->params.begin(); + while (s != source->params.end() && t != params.end()) { + if (!(*s).name().isEmpty()) + (*t).setName((*s).name()); + ++s; + ++t; + } +} + +/*! + If this function is a reimplementation, \a from points + to the FunctionNode of the function being reimplemented. + */ +void FunctionNode::setReimplementedFrom(FunctionNode *from) +{ + rf = from; + from->rb.append(this); +} + +/*! + Sets the "associated" property to \a property. The function + might be the setter or getter for a property, for example. + */ +void FunctionNode::setAssociatedProperty(PropertyNode *property) +{ + ap = property; +} + +/*! + Returns the overload number for this function obtained + from the parent. + */ +int FunctionNode::overloadNumber() const +{ + return parent()->overloadNumber(this); +} + +/*! + Returns the number of times this function name has been + overloaded, obtained from the parent. + */ +int FunctionNode::numOverloads() const +{ + return parent()->numOverloads(name()); +} + +/*! + Returns the list of parameter names. + */ +QStringList FunctionNode::parameterNames() const +{ + QStringList names; + QList<Parameter>::ConstIterator p = parameters().begin(); + while (p != parameters().end()) { + names << (*p).name(); + ++p; + } + return names; +} + +/*! + Returns a raw list of parameters. If \a names is true, the + names are included. If \a values is true, the default values + are included, if any are present. + */ +QString FunctionNode::rawParameters(bool names, bool values) const +{ + QString raw; + foreach (const Parameter ¶meter, parameters()) { + raw += parameter.leftType() + parameter.rightType(); + if (names) + raw += parameter.name(); + if (values) + raw += parameter.defaultValue(); + } + return raw; +} + +/*! + Returns the list of reconstructed parameters. If \a values + is true, the default values are included, if any are present. + */ +QStringList FunctionNode::reconstructParams(bool values) const +{ + QStringList params; + QList<Parameter>::ConstIterator p = parameters().begin(); + while (p != parameters().end()) { + params << (*p).reconstruct(values); + ++p; + } + return params; +} + +/*! + Reconstructs and returns the function's signature. If \a values + is true, the default values of the parameters are included, if + present. + */ +QString FunctionNode::signature(bool values) const +{ + QString s; + if (!returnType().isEmpty()) + s = returnType() + QLatin1Char(' '); + s += name() + QLatin1Char('('); + QStringList params = reconstructParams(values); + int p = params.size(); + if (p > 0) { + for (int i=0; i<p; i++) { + s += params[i]; + if (i < (p-1)) + s += ", "; + } + } + s += QLatin1Char(')'); + return s; +} + +/*! + Returns true if the node's status is Internal, or if its + parent is a class with internal status. + */ +bool FunctionNode::isInternal() const +{ + if (status() == Internal) + return true; + if (parent() && parent()->status() == Internal) + return true; + if (relates() && relates()->status() == Internal) + return true; + return false; +} + +/*! + Print some debugging stuff. + */ +void FunctionNode::debug() const +{ + qDebug("QML METHOD %s rt %s pp %s", + qPrintable(name()), qPrintable(rt), qPrintable(pp.join(" "))); +} + +/*! + \class PropertyNode + + This class describes one instance of using the Q_PROPERTY macro. + */ + +/*! + The constructor sets the \a parent and the \a name, but + everything else is set to default values. + */ +PropertyNode::PropertyNode(InnerNode *parent, const QString& name) + : LeafNode(Property, parent, name), + sto(Trool_Default), + des(Trool_Default), + scr(Trool_Default), + wri(Trool_Default), + usr(Trool_Default), + cst(false), + fnl(false), + rev(-1), + overrides(0) +{ + // nothing. +} + +/*! + Sets this property's \e {overridden from} property to + \a baseProperty, which indicates that this property + overrides \a baseProperty. To begin with, all the values + in this property are set to the corresponding values in + \a baseProperty. + + We probably should ensure that the constant and final + attributes are not being overridden improperly. + */ +void PropertyNode::setOverriddenFrom(const PropertyNode* baseProperty) +{ + for (int i = 0; i < NumFunctionRoles; ++i) { + if (funcs[i].isEmpty()) + funcs[i] = baseProperty->funcs[i]; + } + if (sto == Trool_Default) + sto = baseProperty->sto; + if (des == Trool_Default) + des = baseProperty->des; + if (scr == Trool_Default) + scr = baseProperty->scr; + if (wri == Trool_Default) + wri = baseProperty->wri; + if (usr == Trool_Default) + usr = baseProperty->usr; + overrides = baseProperty; +} + +/*! + */ +QString PropertyNode::qualifiedDataType() const +{ + if (setters().isEmpty() && resetters().isEmpty()) { + if (type_.contains(QLatin1Char('*')) || type_.contains(QLatin1Char('&'))) { + // 'QWidget *' becomes 'QWidget *' const + return type_ + " const"; + } + else { + /* + 'int' becomes 'const int' ('int const' is + correct C++, but looks wrong) + */ + return "const " + type_; + } + } + else { + return type_; + } +} + +/*! Converts the \a boolean value to an enum representation + of the boolean type, which includes an enum value for the + \e {default value} of the item, i.e. true, false, or default. + */ +PropertyNode::Trool PropertyNode::toTrool(bool boolean) +{ + return boolean ? Trool_True : Trool_False; +} + +/*! + Converts the enum \a troolean back to a boolean value. + If \a troolean is neither the true enum value nor the + false enum value, the boolean value returned is + \a defaultValue. + + Note that runtimeDesignabilityFunction() should be called + first. If that function returns the name of a function, it + means the function must be called at runtime to determine + whether the property is Designable. + */ +bool PropertyNode::fromTrool(Trool troolean, bool defaultValue) +{ + switch (troolean) { + case Trool_True: + return true; + case Trool_False: + return false; + default: + return defaultValue; + } +} + +/*! + \class TargetNode + */ + +/*! + */ +TargetNode::TargetNode(InnerNode *parent, const QString& name) + : LeafNode(Target, parent, name) +{ +} + +/*! + Returns false because this is a TargetNode. + */ +bool TargetNode::isInnerNode() const +{ + return false; +} + +bool QmlClassNode::qmlOnly = false; +QMultiMap<QString,Node*> QmlClassNode::inheritedBy; +QMap<QString, QmlClassNode*> QmlClassNode::moduleMap; + +/*! + Constructs a Qml class node (i.e. a Fake node with the + subtype QmlClass. The new node has the given \a parent + and \a name and is associated with the C++ class node + specified by \a cn which may be null if the the Qml + class node is not associated with a C++ class node. + */ +QmlClassNode::QmlClassNode(InnerNode *parent, + const QString& name, + const ClassNode* cn) + : FakeNode(parent, name, QmlClass, Node::ApiPage), + abstract(false), + cnode_(cn), + base_(0) +{ + int i = 0; + if (name.startsWith("QML:")) { + qDebug() << "BOGUS:" << name; + i = 4; + } + setTitle(name.mid(i)); +} + +/*! + I made this so I could print a debug message here. + */ +QmlClassNode::~QmlClassNode() +{ +#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES + qDebug() << "Deleting QmlClassNode:" << name(); +#endif +} + +/*! + Clear the static maps so that subsequent runs don't try to use + contents from a previous run. + */ +void QmlClassNode::terminate() +{ + inheritedBy.clear(); + moduleMap.clear(); +} + +/*! + The base file name for this kind of node has "qml_" + prepended to it. + + But not yet. Still testing. + */ +QString QmlClassNode::fileBase() const +{ + return Node::fileBase(); +} + +/*! + Record the fact that QML class \a base is inherited by + QML class \a sub. + */ +void QmlClassNode::addInheritedBy(const QString& base, Node* sub) +{ + if (inheritedBy.find(base,sub) == inheritedBy.end()) { + inheritedBy.insert(base,sub); + } +#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES + qDebug() << "QmlClassNode::addInheritedBy(): insert" << base << sub->name() << inheritedBy.size(); +#endif +} + +/*! + Loads the list \a subs with the nodes of all the subclasses of \a base. + */ +void QmlClassNode::subclasses(const QString& base, NodeList& subs) +{ + subs.clear(); + if (inheritedBy.count(base) > 0) { + subs = inheritedBy.values(base); +#ifdef DEBUG_MULTIPLE_QDOCCONF_FILES + qDebug() << "QmlClassNode::subclasses():" << inheritedBy.count(base) << base + << "subs:" << subs.size() << "total size:" << inheritedBy.size(); +#endif + } +} + +/*! \fn QString QmlClassNode::qmlModuleIdentifier() const + This function is called to get a string that is used either + as a prefix for the file name to use for QML element or + component reference page, or as a qualifier to prefix a + reference to a QML element or comnponent. The string that + is returned is the concatenation of the QML module name + and its version number. e.g., if an element or component + is defined to be in the QML module QtQuick 1, its module + identifier is "QtQuick1". See setQmlModuleName(). + */ + +/*! + This function splits \a arg on the blank character to get a + QML module name and version number. It stores these separately. + The version number is not required. + */ +void Node::setQmlModuleName(const QString& arg) +{ + QStringList blankSplit = arg.split(QLatin1Char(' ')); + qmlModuleName_ = blankSplit[0]; + if (blankSplit.size() > 1) + qmlModuleVersion_ = blankSplit[1]; +} + +/*! + The name of this QML class node might be the same as the + name of some other QML class node. If so, then this node's + parent will be a NameCollisionNode.This function sets the + NameCollisionNode's current child to this node. This is + important when outputing the documentation for this node, + when, for example, the documentation contains a link to + the page being output. We don't want to generate a link + to the disambiguation page if we can avoid it, and to be + able to avoid it, the NameCollisionNode must maintain the + current child pointer. That's the purpose of this function. + */ +void QmlClassNode::setCurrentChild() +{ + if (parent()) { + InnerNode* n = parent(); + if (n->subType() == Node::Collision) + n->setCurrentChild(this); + } +} + +/*! + */ +void QmlClassNode::clearCurrentChild() +{ + if (parent()) { + InnerNode* n = parent(); + if (n->subType() == Node::Collision) + n->clearCurrentChild(); + } +} + +/*! + Most QML elements don't have an \\inherits command in their + \\qmlclass command. This leaves qdoc bereft, when it tries + to output the line in the documentation that specifies the + QML element that a QML element inherits. + */ +void QmlClassNode::resolveInheritance(const Tree* tree) +{ + if (!links().empty() && links().contains(Node::InheritsLink)) { + QPair<QString,QString> linkPair; + linkPair = links()[Node::InheritsLink]; + QStringList strList = linkPair.first.split("::"); + const Node* n = tree->findNode(strList,Node::Fake); + if (n && (n->subType() == Node::QmlClass || n->subType() == Node::Collision)) { + base_ = static_cast<const FakeNode*>(n); + if (base_ && base_->subType() == Node::QmlClass) { + return; + } + } + if (base_ && base_->subType() == Node::Collision) { + const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(base_); + const NodeList& children = ncn->childNodes(); + for (int i=0; i<importList_.size(); ++i) { + QString qmid = importList_.at(i).first + importList_.at(i).second; + for (int j=0; j<children.size(); ++j) { + if (qmid == children.at(j)->qmlModuleIdentifier()) { + base_ = static_cast<const FakeNode*>(children.at(j)); + return; + } + } + } + QString qmid = qmlModuleIdentifier(); + for (int k=0; k<children.size(); ++k) { + if (qmid == children.at(k)->qmlModuleIdentifier()) { + base_ = static_cast<const QmlClassNode*>(children.at(k)); + return; + } + } + } + if (base_) + return; + } + if (cnode_) { + const QmlClassNode* qcn = cnode_->findQmlBaseNode(); + if (qcn != 0) + base_ = qcn; + } + return; +} + +/*! + Constructs a Qml basic type node (i.e. a Fake node with + the subtype QmlBasicType. The new node has the given + \a parent and \a name. + */ +QmlBasicTypeNode::QmlBasicTypeNode(InnerNode *parent, + const QString& name) + : FakeNode(parent, name, QmlBasicType, Node::ApiPage) +{ + setTitle(name); +} + +/*! + Constructor for the Qml property group node. \a parent is + always a QmlClassNode. + */ +QmlPropGroupNode::QmlPropGroupNode(QmlClassNode* parent, + const QString& name, + bool attached) + : FakeNode(parent, name, QmlPropertyGroup, Node::ApiPage), + isdefault_(false), + attached_(attached), + readOnly_(-1) +{ + // nothing. +} + +/*! + Constructor for the QML property node, when the \a parent + is QML property group node. This constructor is only used + for creating QML property nodes for QML elements, i.e. + not for creating QML property nodes for QML components. + Hopefully, this constructor will become obsolete, so don't + use it unless one of the other two constructors can't be + used. + */ +QmlPropertyNode::QmlPropertyNode(QmlPropGroupNode *parent, + const QString& name, + const QString& type, + bool attached) + : LeafNode(QmlProperty, parent, name), + type_(type), + sto(Trool_Default), + des(Trool_Default), + isdefault_(false), + attached_(attached), + readOnly_(-1) +{ + setPageType(ApiPage); +} + +/*! + Constructor for the QML property node, when the \a parent + is a QML class node. + */ +QmlPropertyNode::QmlPropertyNode(QmlClassNode *parent, + const QString& name, + const QString& type, + bool attached) + : LeafNode(QmlProperty, parent, name), + type_(type), + sto(Trool_Default), + des(Trool_Default), + isdefault_(false), + attached_(attached), + readOnly_(-1) +{ + setPageType(ApiPage); +} + +/*! + Constructor for the QML property node, when the \a parent + is a QML property node. Strictly speaking, this is not the + way QML property nodes were originally meant to be built, + because this constructor has another QML property node as + its parent. But this constructor is useful for documenting + QML properties in QML components, i.e., when you override + the definition of a property with the \e{qmlproperty} + command. It actually uses the parent of \a parent as the + parent. + */ +QmlPropertyNode::QmlPropertyNode(QmlPropertyNode* parent, + const QString& name, + const QString& type, + bool attached) + : LeafNode(parent->parent(), QmlProperty, name), + type_(type), + sto(Trool_Default), + des(Trool_Default), + isdefault_(false), + attached_(attached), + readOnly_(-1) +{ + setPageType(ApiPage); +} + +/*! + I don't know what this is. + */ +QmlPropertyNode::Trool QmlPropertyNode::toTrool(bool boolean) +{ + return boolean ? Trool_True : Trool_False; +} + +/*! + I don't know what this is either. + */ +bool QmlPropertyNode::fromTrool(Trool troolean, bool defaultValue) +{ + switch (troolean) { + case Trool_True: + return true; + case Trool_False: + return false; + default: + return defaultValue; + } +} + +/*! + Returns true if a QML property or attached property is + read-only. The algorithm for figuring this out is long + amd tedious and almost certainly will break. It currently + doesn't work for qmlproperty bool PropertyChanges::explicit, + because the tokenizer gets confused on "explicit". + */ +bool QmlPropertyNode::isWritable(const Tree* tree) const +{ + if (wri != Trool_Default) + return fromTrool(wri, false); + + const PropertyNode *pn = correspondingProperty(tree); + if (pn) + return pn->isWritable(); + else { + location().warning(tr("Can't determine read-only status of QML property %1; writable assumed.").arg(name())); + return true; + } +} + +const PropertyNode *QmlPropertyNode::correspondingProperty(const Tree *tree) const +{ + const PropertyNode *pn; + + Node* n = parent(); + while (n && n->subType() != Node::QmlClass) + n = n->parent(); + if (n) { + const QmlClassNode* qcn = static_cast<const QmlClassNode*>(n); + const ClassNode* cn = qcn->classNode(); + if (cn) { + QStringList dotSplit = name().split(QChar('.')); + pn = cn->findPropertyNode(dotSplit[0]); + if (pn) { + if (dotSplit.size() > 1) { + // Find the C++ property corresponding to the QML property in + // the property group, <group>.<property>. + + QStringList path(extractClassName(pn->qualifiedDataType())); + const Node* nn = tree->findNode(path,Class); + if (nn) { + const ClassNode* cn = static_cast<const ClassNode*>(nn); + const PropertyNode *pn2 = cn->findPropertyNode(dotSplit[1]); + if (pn2) + return pn2; // Return the property for the QML property. + else + return pn; // Return the property for the QML group. + } + } + else + return pn; + } + else { + pn = cn->findPropertyNode(dotSplit[0]); + if (pn) + return pn; + } + } + } + + return 0; +} + +/*! \class NameCollisionNode + + An instance of this node is inserted in the tree + whenever qdoc discovers that two nodes have the + same name. + */ + +/*! + Constructs a name collision node containing \a child + as its first child. The parent of \a child becomes + this node's parent. + */ +NameCollisionNode::NameCollisionNode(InnerNode* child) + : FakeNode(child->parent(), child->name(), Collision, Node::NoPageType) +{ + setTitle("Name Collisions For: " + child->name()); + addCollision(child); + current = 0; +} + +/*! + Add a collision to this collision node. \a child has + the same name as the other children in this collision + node. \a child becomes the current child. + */ +void NameCollisionNode::addCollision(InnerNode* child) +{ + if (child) { + if (child->parent()) + child->parent()->removeChild(child); + child->setParent((InnerNode*)this); + children.append(child); + } +} + +/*! + The destructor does nothing. + */ +NameCollisionNode::~NameCollisionNode() +{ + // nothing. +} + +/*! \fn const InnerNode* NameCollisionNode::currentChild() const + Returns a pointer to the current child, which may be 0. + */ + +/*! \fn void NameCollisionNode::setCurrentChild(InnerNode* child) + Sets the current child to \a child. The current child is + valid only within the file where it is defined. + */ + +/*! \fn void NameCollisionNode::clearCurrentChild() + Sets the current child to 0. This should be called at the + end of each file, because the current child is only valid + within the file where the child is defined. + */ + +/*! + Returns true if this collision node's current node is a QML node. + */ +bool NameCollisionNode::isQmlNode() const +{ + if (current) + return current->isQmlNode(); + return false; +} + +/*! + Find any of this collision node's children that has type \a t + and subtype \a st and return a pointer to it. +*/ +const InnerNode* NameCollisionNode::findAny(Node::Type t, Node::SubType st) const +{ + if (current) { + if (current->type() == t && current->subType() == st) + return current; + } + const NodeList& cn = childNodes(); + NodeList::ConstIterator i = cn.begin(); + while (i != cn.end()) { + if ((*i)->type() == t && (*i)->subType() == st) + return static_cast<const InnerNode*>(*i); + ++i; + } + return 0; +} + +/*! + This node is a name collision node. Find a child of this node + such that the child's QML module identifier matches origin's + QML module identifier. Return the matching node, or return this + node if there is no matching node. + */ +const Node* NameCollisionNode::applyModuleIdentifier(const Node* origin) const +{ + if (origin && !origin->qmlModuleIdentifier().isEmpty()) { + const NodeList& cn = childNodes(); + NodeList::ConstIterator i = cn.begin(); + while (i != cn.end()) { + if ((*i)->type() == Node::Fake && (*i)->subType() == Node::QmlClass) { + if (origin->qmlModuleIdentifier() == (*i)->qmlModuleIdentifier()) + return (*i); + } + ++i; + } + } + return this; +} + +/*! + Construct the full document name for this node and return it. + */ +QString Node::fullDocumentName() const +{ + QStringList pieces; + const Node* n = this; + + do { + if (!n->name().isEmpty() && + ((n->type() != Node::Fake) || (n->subType() != Node::QmlPropertyGroup))) + pieces.insert(0, n->name()); + + if ((n->type() == Node::Fake) && (n->subType() != Node::QmlPropertyGroup)) { + if ((n->subType() == Node::QmlClass) && !n->qmlModuleName().isEmpty()) + pieces.insert(0, n->qmlModuleIdentifier()); + break; + } + + // Examine the parent node if one exists. + if (n->parent()) + n = n->parent(); + else + break; + } while (true); + + // Create a name based on the type of the ancestor node. + QString concatenator = "::"; + if ((n->type() == Node::Fake) && (n->subType() != Node::QmlClass)) + concatenator = QLatin1Char('#'); + + return pieces.join(concatenator); +} + +/*! + Returns the \a str as an NCName, which means the name can + be used as the value of an \e id attribute. Search for NCName + on the internet for details of what can be an NCName. + */ +QString Node::cleanId(QString str) +{ + QString clean; + QString name = str.simplified(); + + if (name.isEmpty()) + return clean; + + name = name.replace("::","-"); + name = name.replace(" ","-"); + name = name.replace("()","-call"); + + clean.reserve(name.size() + 20); + if (!str.startsWith("id-")) + clean = "id-"; + const QChar c = name[0]; + const uint u = c.unicode(); + + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) { + clean += c; + } + else if (u == '~') { + clean += "dtor."; + } + else if (u == '_') { + clean += "underscore."; + } + else { + clean += QLatin1Char('a'); + } + + for (int i = 1; i < (int) name.length(); i++) { + const QChar c = name[i]; + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9') || u == '-' || + u == '_' || u == '.') { + clean += c; + } + else if (c.isSpace() || u == ':' ) { + clean += QLatin1Char('-'); + } + else if (u == '!') { + clean += "-not"; + } + else if (u == '&') { + clean += "-and"; + } + else if (u == '<') { + clean += "-lt"; + } + else if (u == '=') { + clean += "-eq"; + } + else if (u == '>') { + clean += "-gt"; + } + else if (u == '#') { + clean += "-hash"; + } + else if (u == '(') { + clean += "-"; + } + else if (u == ')') { + clean += "-"; + } + else { + clean += QLatin1Char('-'); + clean += QString::number((int)u, 16); + } + } + return clean; +} + +/*! + Creates a string that can be used as a UUID for the node, + depending on the type and subtype of the node. Uniquenss + is not guaranteed, but it is expected that strings created + here will be unique within an XML document. Hence, the + returned string can be used as the value of an \e id + attribute. + */ +QString Node::idForNode() const +{ + const FunctionNode* func; + const TypedefNode* tdn; + QString str; + + switch (type()) { + case Node::Namespace: + str = "namespace-" + fullDocumentName(); + break; + case Node::Class: + str = "class-" + fullDocumentName(); + break; + case Node::Enum: + str = "enum-" + name(); + break; + case Node::Typedef: + tdn = static_cast<const TypedefNode*>(this); + if (tdn->associatedEnum()) { + return tdn->associatedEnum()->idForNode(); + } + else { + str = "typedef-" + name(); + } + break; + case Node::Function: + func = static_cast<const FunctionNode*>(this); + if (func->associatedProperty()) { + return func->associatedProperty()->idForNode(); + } + else { + if (func->name().startsWith("operator")) { + str = ""; + /* + The test below should probably apply to all + functions, but for now, overloaded operators + are the only ones that produce duplicate id + attributes in the DITA XML files. + */ + if (relatesTo_) + str = "nonmember-"; + QString op = func->name().mid(8); + if (!op.isEmpty()) { + int i = 0; + while (i<op.size() && op.at(i) == ' ') + ++i; + if (i>0 && i<op.size()) { + op = op.mid(i); + } + if (!op.isEmpty()) { + i = 0; + while (i < op.size()) { + const QChar c = op.at(i); + const uint u = c.unicode(); + if ((u >= 'a' && u <= 'z') || + (u >= 'A' && u <= 'Z') || + (u >= '0' && u <= '9')) + break; + ++i; + } + str += "operator-"; + if (i>0) { + QString tail = op.mid(i); + op = op.left(i); + if (operators_.contains(op)) { + str += operators_.value(op); + if (!tail.isEmpty()) + str += "-" + tail; + } + else + qDebug() << "qdoc3 internal error: Operator missing from operators_ map:" << op; + } + else { + str += op; + } + } + } + } + else if (parent_) { + if (parent_->type() == Class) + str = "class-member-" + func->name(); + else if (parent_->type() == Namespace) + str = "namespace-member-" + func->name(); + else if (parent_->type() == Fake) { + if (parent_->subType() == QmlClass) + str = "qml-method-" + func->name(); + else + qDebug() << "qdoc3 internal error: Node subtype not handled:" + << parent_->subType() << func->name(); + } + else + qDebug() << "qdoc3 internal error: Node type not handled:" + << parent_->type() << func->name(); + + } + if (func->overloadNumber() != 1) + str += QLatin1Char('-') + QString::number(func->overloadNumber()); + } + break; + case Node::Fake: + { + switch (subType()) { + case Node::QmlClass: + str = "qml-class-" + name(); + break; + case Node::QmlPropertyGroup: + str = "qml-property-" + name(); + break; + case Node::Page: + case Node::Group: + case Node::Module: + case Node::HeaderFile: + str = title(); + if (str.isEmpty()) { + str = name(); + if (str.endsWith(".html")) + str.remove(str.size()-5,5); + } + str.replace("/","-"); + break; + case Node::File: + str = name(); + str.replace("/","-"); + break; + case Node::Example: + str = name(); + str.replace("/","-"); + break; + case Node::QmlBasicType: + str = "qml-basic-type-" + name(); + break; + case Node::QmlModule: + str = "qml-module-" + name(); + break; + case Node::Collision: + str = title(); + str.replace(": ","-"); + break; + default: + qDebug() << "ERROR: A case was not handled in Node::idForNode():" + << "subType():" << subType() << "type():" << type(); + break; + } + } + break; + case Node::QmlProperty: + str = "qml-property-" + name(); + break; + case Node::Property: + str = "property-" + name(); + break; + case Node::QmlSignal: + str = "qml-signal-" + name(); + break; + case Node::QmlSignalHandler: + str = "qml-signal-handler-" + name(); + break; + case Node::QmlMethod: + str = "qml-method-" + name(); + break; + case Node::Variable: + str = "var-" + name(); + break; + case Node::Target: + str = name(); + break; + default: + qDebug() << "ERROR: A case was not handled in Node::idForNode():" + << "type():" << type() << "subType():" << subType(); + break; + } + if (str.isEmpty()) { + qDebug() << "ERROR: A link text was empty in Node::idForNode():" + << "type():" << type() << "subType():" << subType() + << "name():" << name() + << "title():" << title(); + } + else { + str = cleanId(str); + } + return str; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h new file mode 100644 index 0000000000..440b22dc04 --- /dev/null +++ b/src/tools/qdoc/node.h @@ -0,0 +1,958 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + node.h +*/ + +#ifndef NODE_H +#define NODE_H + +#include <qdir.h> +#include <qmap.h> +#include <qpair.h> +#include <qstringlist.h> + +#include "codechunk.h" +#include "doc.h" +#include "location.h" +#include "text.h" + +QT_BEGIN_NAMESPACE + +class Node; +class ClassNode; +class InnerNode; +class ClassNode; +class ExampleNode; +class QmlClassNode; +class Tree; + +typedef QMap<QString, const Node*> NodeMap; +typedef QMultiMap<QString, Node*> NodeMultiMap; +typedef QMultiMap<QString, const ExampleNode*> ExampleNodeMap; +typedef QList<QPair<QString,QString> > ImportList; + +class Node +{ +public: + enum Type { + Namespace, + Class, + Fake, + Enum, + Typedef, + Function, + Property, + Variable, + Target, + QmlProperty, + QmlSignal, + QmlSignalHandler, + QmlMethod, + LastType + }; + + enum SubType { + NoSubType, + Example, + HeaderFile, + File, + Image, + Group, + Module, + Page, + ExternalPage, + QmlClass, + QmlPropertyGroup, + QmlBasicType, + QmlModule, + DitaMap, + Collision, + LastSubtype + }; + + enum Access { Public, Protected, Private }; + + enum Status { + Compat, + Obsolete, + Deprecated, + Preliminary, + Commendable, + Main, + Internal + }; // don't reorder this enum + + enum ThreadSafeness { + UnspecifiedSafeness, + NonReentrant, + Reentrant, + ThreadSafe + }; + + enum LinkType { + StartLink, + NextLink, + PreviousLink, + ContentsLink, + IndexLink, + InheritsLink /*, + GlossaryLink, + CopyrightLink, + ChapterLink, + SectionLink, + SubsectionLink, + AppendixLink */ + }; + + enum PageType { + NoPageType, + ApiPage, + ArticlePage, + ExamplePage, + HowToPage, + OverviewPage, + TutorialPage, + FAQPage, + DitaMapPage, + OnBeyondZebra + }; + + virtual ~Node(); + + void setAccess(Access access) { access_ = access; } + void setLocation(const Location& location) { loc = location; } + void setDoc(const Doc& doc, bool replace = false); + void setStatus(Status status) { status_ = status; } + void setThreadSafeness(ThreadSafeness safeness) { safeness_ = safeness; } + void setSince(const QString &since); + void setRelates(InnerNode* pseudoParent); + void setModuleName(const QString &module) { mod = module; } + void setLink(LinkType linkType, const QString &link, const QString &desc); + void setUrl(const QString &url); + void setTemplateStuff(const QString &templateStuff) { templateStuff_ = templateStuff; } + void setPageType(PageType t) { pageType_ = t; } + void setPageType(const QString& t); + void setParent(InnerNode* n) { parent_ = n; } + + virtual bool isInnerNode() const = 0; + virtual bool isReimp() const { return false; } + virtual bool isFunction() const { return false; } + virtual bool isQmlNode() const { return false; } + virtual bool isInternal() const { return false; } + virtual bool isQtQuickNode() const { return false; } + virtual bool isAbstract() const { return false; } + virtual void setAbstract(bool ) { } + virtual QString title() const { return QString(); } + Type type() const { return nodeType_; } + virtual SubType subType() const { return NoSubType; } + InnerNode* parent() const { return parent_; } + InnerNode* relates() const { return relatesTo_; } + const QString& name() const { return name_; } + QMap<LinkType, QPair<QString,QString> > links() const { return linkMap; } + QString moduleName() const; + QString url() const; + virtual QString nameForLists() const { return name_; } + + Access access() const { return access_; } + QString accessString() const; + const Location& location() const { return loc; } + const Doc& doc() const { return d; } + Status status() const { return status_; } + Status inheritedStatus() const; + ThreadSafeness threadSafeness() const; + ThreadSafeness inheritedThreadSafeness() const; + QString since() const { return sinc; } + QString templateStuff() const { return templateStuff_; } + PageType pageType() const { return pageType_; } + QString pageTypeString() const; + QString nodeTypeString() const; + QString nodeSubtypeString() const; + virtual void addPageKeywords(const QString& ) { } + + void clearRelated() { relatesTo_ = 0; } + + virtual QString fileBase() const; + QString guid() const; + QString ditaXmlHref(); + QString extractClassName(const QString &string) const; + virtual QString qmlModuleName() const { return qmlModuleName_; } + virtual QString qmlModuleVersion() const { return qmlModuleVersion_; } + virtual QString qmlModuleIdentifier() const { return qmlModuleName_ + qmlModuleVersion_; } + virtual void setQmlModuleName(const QString& ); + virtual const ClassNode* classNode() const { return 0; } + virtual void clearCurrentChild() { } + virtual const ImportList* importList() const { return 0; } + virtual void setImportList(const ImportList& ) { } + virtual const Node* applyModuleIdentifier(const Node* ) const { return 0; } + const QmlClassNode* qmlClassNode() const; + const ClassNode* declarativeCppNode() const; + const QString& outputSubdirectory() const { return outSubDir_; } + QString fullDocumentName() const; + static QString cleanId(QString str); + QString idForNode() const; + + static QString pageTypeString(unsigned t); + static QString nodeTypeString(unsigned t); + static QString nodeSubtypeString(unsigned t); + +protected: + Node(Type type, InnerNode* parent, const QString& name); + +private: + +#ifdef Q_WS_WIN + Type nodeType_; + Access access_; + ThreadSafeness safeness_; + PageType pageType_; + Status status_; +#else + Type nodeType_ : 4; + Access access_ : 2; + ThreadSafeness safeness_ : 2; + PageType pageType_ : 4; + Status status_ : 3; +#endif + InnerNode* parent_; + InnerNode* relatesTo_; + QString name_; + Location loc; + Doc d; + QMap<LinkType, QPair<QString, QString> > linkMap; + QString mod; + QString url_; + QString sinc; + QString templateStuff_; + mutable QString uuid; + QString outSubDir_; + QString qmlModuleName_; + QString qmlModuleVersion_; + static QStringMap operators_; +}; + +class FunctionNode; +class EnumNode; +class NameCollisionNode; + +typedef QList<Node*> NodeList; +typedef QMap<QString, const Node*> NodeMap; +typedef QMultiMap<QString, Node*> NodeMultiMap; + +class InnerNode : public Node +{ +public: + virtual ~InnerNode(); + + Node* findNode(const QString& name); + Node* findNode(const QString& name, bool qml); + Node* findNode(const QString& name, Type type); + void findNodes(const QString& name, QList<Node*>& n); + FunctionNode* findFunctionNode(const QString& name); + FunctionNode* findFunctionNode(const FunctionNode* clone); + void addInclude(const QString &include); + void setIncludes(const QStringList &includes); + void setOverload(const FunctionNode* func, bool overlode); + void normalizeOverloads(); + void makeUndocumentedChildrenInternal(); + void clearCurrentChildPointers(); + void deleteChildren(); + void removeFromRelated(); + + virtual bool isInnerNode() const { return true; } + const Node* findNode(const QString& name) const; + const Node* findNode(const QString& name, bool qml) const; + const Node* findNode(const QString& name, Type type) const; + const FunctionNode* findFunctionNode(const QString& name) const; + const FunctionNode* findFunctionNode(const FunctionNode* clone) const; + const EnumNode* findEnumNodeForValue(const QString &enumValue) const; + const NodeList & childNodes() const { return children; } + const NodeList & relatedNodes() const { return related_; } + int count() const { return children.size(); } + int overloadNumber(const FunctionNode* func) const; + int numOverloads(const QString& funcName) const; + NodeList overloads(const QString &funcName) const; + const QStringList& includes() const { return inc; } + + QStringList primaryKeys(); + QStringList secondaryKeys(); + const QStringList& pageKeywords() const { return pageKeywds; } + virtual void addPageKeywords(const QString& t) { pageKeywds << t; } + virtual void setCurrentChild() { } + virtual void setCurrentChild(InnerNode* ) { } + +protected: + InnerNode(Type type, InnerNode* parent, const QString& name); + +private: + friend class Node; + friend class NameCollisionNode; + + static bool isSameSignature(const FunctionNode* f1, const FunctionNode* f2); + void addChild(Node* child); + void removeRelated(Node* pseudoChild); + void removeChild(Node* child); + + QStringList pageKeywds; + QStringList inc; + NodeList children; + NodeList enumChildren; + NodeList related_; + QMap<QString, Node*> childMap; + QMap<QString, Node*> primaryFunctionMap; + QMap<QString, NodeList> secondaryFunctionMap; +}; + +class LeafNode : public Node +{ +public: + LeafNode(); + virtual ~LeafNode() { } + + virtual bool isInnerNode() const; + +protected: + LeafNode(Type type, InnerNode* parent, const QString& name); + LeafNode(InnerNode* parent, Type type, const QString& name); +}; + +class NamespaceNode : public InnerNode +{ +public: + NamespaceNode(InnerNode* parent, const QString& name); + virtual ~NamespaceNode() { } +}; + +class ClassNode; + +struct RelatedClass +{ + RelatedClass() { } + RelatedClass(Node::Access access0, + ClassNode* node0, + const QString& dataTypeWithTemplateArgs0 = "") + : access(access0), + node(node0), + dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0) { } + QString accessString() const; + + Node::Access access; + ClassNode* node; + QString dataTypeWithTemplateArgs; +}; + +class PropertyNode; + +class ClassNode : public InnerNode +{ +public: + ClassNode(InnerNode* parent, const QString& name); + virtual ~ClassNode() { } + + void addBaseClass(Access access, + ClassNode* node, + const QString &dataTypeWithTemplateArgs = ""); + void fixBaseClasses(); + + const QList<RelatedClass> &baseClasses() const { return bases; } + const QList<RelatedClass> &derivedClasses() const { return derived; } + const QList<RelatedClass> &ignoredBaseClasses() const { return ignoredBases; } + + bool hideFromMainList() const { return hidden; } + void setHideFromMainList(bool value) { hidden = value; } + + QString serviceName() const { return sname; } + void setServiceName(const QString& value) { sname = value; } + const QmlClassNode* qmlElement() const { return qmlelement; } + void setQmlElement(QmlClassNode* qcn) { qmlelement = qcn; } + virtual bool isAbstract() const { return abstract; } + virtual void setAbstract(bool b) { abstract = b; } + const PropertyNode* findPropertyNode(const QString& name) const; + const QmlClassNode* findQmlBaseNode() const; + +private: + QList<RelatedClass> bases; + QList<RelatedClass> derived; + QList<RelatedClass> ignoredBases; + bool hidden; + bool abstract; + QString sname; + QmlClassNode* qmlelement; +}; + +class FakeNode : public InnerNode +{ +public: + + FakeNode(InnerNode* parent, + const QString& name, + SubType subType, + PageType ptype); + virtual ~FakeNode() { } + + void setTitle(const QString &title) { title_ = title; } + void setSubTitle(const QString &subTitle) { subtitle_ = subTitle; } + void addGroupMember(Node* node) { nodeList.append(node); } + void addQmlModuleMember(Node* node) { nodeList.append(node); } + + SubType subType() const { return nodeSubtype_; } + virtual QString title() const; + virtual QString fullTitle() const; + virtual QString subTitle() const; + virtual QString imageFileName() const { return QString(); } + const NodeList& groupMembers() const { return nodeList; } + const NodeList& qmlModuleMembers() const { return nodeList; } + virtual QString nameForLists() const { return title(); } + virtual void setImageFileName(const QString& ) { } + +protected: + SubType nodeSubtype_; + QString title_; + QString subtitle_; + NodeList nodeList; // used for groups and QML modules. +}; + +class NameCollisionNode : public FakeNode +{ +public: + NameCollisionNode(InnerNode* child); + ~NameCollisionNode(); + const InnerNode* currentChild() const { return current; } + virtual void setCurrentChild(InnerNode* child) { current = child; } + virtual void clearCurrentChild() { current = 0; } + virtual bool isQmlNode() const; + virtual const Node* applyModuleIdentifier(const Node* origin) const; + const InnerNode* findAny(Node::Type t, Node::SubType st) const; + void addCollision(InnerNode* child); + const QMap<QString,QString>& linkTargets() const { return targets; } + void addLinkTarget(const QString& t, const QString& v) { targets.insert(t,v); } + +private: + InnerNode* current; + QMap<QString,QString> targets; +}; + +class ExampleNode : public FakeNode +{ +public: + ExampleNode(InnerNode* parent, const QString& name); + virtual ~ExampleNode() { } + virtual QString imageFileName() const { return imageFileName_; } + virtual void setImageFileName(const QString& ifn) { imageFileName_ = ifn; } + + static void terminate() { exampleNodeMap.clear(); } + +public: + static ExampleNodeMap exampleNodeMap; + +private: + QString imageFileName_; +}; + +class QmlClassNode : public FakeNode +{ +public: + QmlClassNode(InnerNode* parent, + const QString& name, + const ClassNode* cn); + virtual ~QmlClassNode(); + virtual bool isQmlNode() const { return true; } + virtual bool isQtQuickNode() const { return (qmlModuleName() == QLatin1String("QtQuick")); } + virtual const ClassNode* classNode() const { return cnode_; } + virtual QString fileBase() const; + virtual void setCurrentChild(); + virtual void clearCurrentChild(); + virtual const ImportList* importList() const { return &importList_; } + virtual void setImportList(const ImportList& il) { importList_ = il; } + virtual bool isAbstract() const { return abstract; } + virtual void setAbstract(bool b) { abstract = b; } + const FakeNode* qmlBase() const { return base_; } + void resolveInheritance(const Tree* tree); + static void addInheritedBy(const QString& base, Node* sub); + static void subclasses(const QString& base, NodeList& subs); + static void terminate(); + +public: + static bool qmlOnly; + static QMultiMap<QString,Node*> inheritedBy; + static QMap<QString, QmlClassNode*> moduleMap; + +private: + bool abstract; + const ClassNode* cnode_; + const FakeNode* base_; + ImportList importList_; +}; + +class QmlBasicTypeNode : public FakeNode +{ +public: + QmlBasicTypeNode(InnerNode* parent, + const QString& name); + virtual ~QmlBasicTypeNode() { } + virtual bool isQmlNode() const { return true; } +}; + +class QmlPropGroupNode : public FakeNode +{ +public: + QmlPropGroupNode(QmlClassNode* parent, + const QString& name, + bool attached); + virtual ~QmlPropGroupNode() { } + virtual bool isQmlNode() const { return true; } + virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); } + virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } + virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); } + virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } + + const QString& element() const { return parent()->name(); } + void setDefault() { isdefault_ = true; } + void setReadOnly(int ro) { readOnly_ = ro; } + int getReadOnly() const { return readOnly_; } + bool isDefault() const { return isdefault_; } + bool isAttached() const { return attached_; } + bool isReadOnly() const { return (readOnly_ > 0); } + +private: + bool isdefault_; + bool attached_; + int readOnly_; +}; + +class QmlPropertyNode; + +class QmlPropertyNode : public LeafNode +{ +public: + QmlPropertyNode(QmlClassNode *parent, + const QString& name, + const QString& type, + bool attached); + QmlPropertyNode(QmlPropGroupNode* parent, + const QString& name, + const QString& type, + bool attached); + QmlPropertyNode(QmlPropertyNode* parent, + const QString& name, + const QString& type, + bool attached); + virtual ~QmlPropertyNode() { } + + void setDataType(const QString& dataType) { type_ = dataType; } + void setStored(bool stored) { sto = toTrool(stored); } + void setDesignable(bool designable) { des = toTrool(designable); } + void setWritable(bool writable) { wri = toTrool(writable); } + + const QString &dataType() const { return type_; } + QString qualifiedDataType() const { return type_; } + void setDefault() { isdefault_ = true; } + void setReadOnly(int ro) { readOnly_ = ro; } + int getReadOnly() const { return readOnly_; } + bool isDefault() const { return isdefault_; } + bool isStored() const { return fromTrool(sto,true); } + bool isDesignable() const { return fromTrool(des,false); } + bool isWritable(const Tree* tree) const; + bool isAttached() const { return attached_; } + bool isReadOnly() const { return (readOnly_ > 0); } + virtual bool isQmlNode() const { return true; } + virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); } + virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } + virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); } + virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } + + const PropertyNode *correspondingProperty(const Tree *tree) const; + + const QString& element() const { return static_cast<QmlPropGroupNode*>(parent())->element(); } + void appendQmlPropNode(QmlPropertyNode* p) { qmlPropNodes_.append(p); } + const NodeList& qmlPropNodes() const { return qmlPropNodes_; } + +private: + enum Trool { Trool_True, Trool_False, Trool_Default }; + + static Trool toTrool(bool boolean); + static bool fromTrool(Trool troolean, bool defaultValue); + + QString type_; + Trool sto; + Trool des; + Trool wri; + bool isdefault_; + bool attached_; + int readOnly_; + NodeList qmlPropNodes_; +}; + +class EnumItem +{ +public: + EnumItem() { } + EnumItem(const QString& name, const QString& value) + : nam(name), val(value) { } + EnumItem(const QString& name, const QString& value, const Text &txt) + : nam(name), val(value), txt(txt) { } + + const QString& name() const { return nam; } + const QString& value() const { return val; } + const Text &text() const { return txt; } + +private: + QString nam; + QString val; + Text txt; +}; + +class TypedefNode; + +class EnumNode : public LeafNode +{ +public: + EnumNode(InnerNode* parent, const QString& name); + virtual ~EnumNode() { } + + void addItem(const EnumItem& item); + void setFlagsType(TypedefNode* typedeff); + bool hasItem(const QString &name) const { return names.contains(name); } + + const QList<EnumItem>& items() const { return itms; } + Access itemAccess(const QString& name) const; + const TypedefNode* flagsType() const { return ft; } + QString itemValue(const QString &name) const; + +private: + QList<EnumItem> itms; + QSet<QString> names; + const TypedefNode* ft; +}; + +class TypedefNode : public LeafNode +{ +public: + TypedefNode(InnerNode* parent, const QString& name); + virtual ~TypedefNode() { } + + const EnumNode* associatedEnum() const { return ae; } + +private: + void setAssociatedEnum(const EnumNode* enume); + + friend class EnumNode; + + const EnumNode* ae; +}; + +inline void EnumNode::setFlagsType(TypedefNode* typedeff) +{ + ft = typedeff; + typedeff->setAssociatedEnum(this); +} + + +class Parameter +{ +public: + Parameter() {} + Parameter(const QString& leftType, + const QString& rightType = "", + const QString& name = "", + const QString& defaultValue = ""); + Parameter(const Parameter& p); + + Parameter& operator=(const Parameter& p); + + void setName(const QString& name) { nam = name; } + + bool hasType() const { return lef.length() + rig.length() > 0; } + const QString& leftType() const { return lef; } + const QString& rightType() const { return rig; } + const QString& name() const { return nam; } + const QString& defaultValue() const { return def; } + + QString reconstruct(bool value = false) const; + +private: + QString lef; + QString rig; + QString nam; + QString def; +}; + +class PropertyNode; + +class FunctionNode : public LeafNode +{ +public: + enum Metaness { + Plain, + Signal, + Slot, + Ctor, + Dtor, + MacroWithParams, + MacroWithoutParams, + Native }; + enum Virtualness { NonVirtual, ImpureVirtual, PureVirtual }; + + FunctionNode(InnerNode* parent, const QString &name); + FunctionNode(Type type, InnerNode* parent, const QString &name, bool attached); + virtual ~FunctionNode() { } + + void setReturnType(const QString& returnType) { rt = returnType; } + void setParentPath(const QStringList& parentPath) { pp = parentPath; } + void setMetaness(Metaness metaness) { met = metaness; } + void setVirtualness(Virtualness virtualness); + void setConst(bool conste) { con = conste; } + void setStatic(bool statique) { sta = statique; } + void setOverload(bool overlode); + void setReimp(bool r); + void addParameter(const Parameter& parameter); + inline void setParameters(const QList<Parameter>& parameters); + void borrowParameterNames(const FunctionNode* source); + void setReimplementedFrom(FunctionNode* from); + + const QString& returnType() const { return rt; } + Metaness metaness() const { return met; } + bool isMacro() const { + return met == MacroWithParams || met == MacroWithoutParams; + } + Virtualness virtualness() const { return vir; } + bool isConst() const { return con; } + bool isStatic() const { return sta; } + bool isOverload() const { return ove; } + bool isReimp() const { return reimp; } + bool isFunction() const { return true; } + int overloadNumber() const; + int numOverloads() const; + const QList<Parameter>& parameters() const { return params; } + QStringList parameterNames() const; + QString rawParameters(bool names = false, bool values = false) const; + const FunctionNode* reimplementedFrom() const { return rf; } + const QList<FunctionNode*> &reimplementedBy() const { return rb; } + const PropertyNode* associatedProperty() const { return ap; } + const QStringList& parentPath() const { return pp; } + + QStringList reconstructParams(bool values = false) const; + QString signature(bool values = false) const; + const QString& element() const { return parent()->name(); } + bool isAttached() const { return attached_; } + virtual bool isInternal() const; + virtual bool isQmlNode() const { + return ((type() == QmlSignal) || + (type() == QmlMethod) || + (type() == QmlSignalHandler)); + } + virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); } + virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } + virtual QString qmlModuleVersion() const { return parent()->qmlModuleVersion(); } + virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } + + void debug() const; + +private: + void setAssociatedProperty(PropertyNode* property); + + friend class InnerNode; + friend class PropertyNode; + + QString rt; + QStringList pp; +#ifdef Q_WS_WIN + Metaness met; + Virtualness vir; +#else + Metaness met : 4; + Virtualness vir : 2; +#endif + bool con : 1; + bool sta : 1; + bool ove : 1; + bool reimp: 1; + bool attached_: 1; + QList<Parameter> params; + const FunctionNode* rf; + const PropertyNode* ap; + QList<FunctionNode*> rb; +}; + +class PropertyNode : public LeafNode +{ +public: + enum FunctionRole { Getter, Setter, Resetter, Notifier }; + enum { NumFunctionRoles = Notifier + 1 }; + + PropertyNode(InnerNode* parent, const QString& name); + virtual ~PropertyNode() { } + + void setDataType(const QString& dataType) { type_ = dataType; } + void addFunction(FunctionNode* function, FunctionRole role); + void addSignal(FunctionNode* function, FunctionRole role); + void setStored(bool stored) { sto = toTrool(stored); } + void setDesignable(bool designable) { des = toTrool(designable); } + void setScriptable(bool scriptable) { scr = toTrool(scriptable); } + void setWritable(bool writable) { wri = toTrool(writable); } + void setUser(bool user) { usr = toTrool(user); } + void setOverriddenFrom(const PropertyNode* baseProperty); + void setRuntimeDesFunc(const QString& rdf) { runtimeDesFunc = rdf; } + void setRuntimeScrFunc(const QString& scrf) { runtimeScrFunc = scrf; } + void setConstant() { cst = true; } + void setFinal() { fnl = true; } + void setRevision(int revision) { rev = revision; } + + const QString &dataType() const { return type_; } + QString qualifiedDataType() const; + NodeList functions() const; + NodeList functions(FunctionRole role) const { return funcs[(int)role]; } + NodeList getters() const { return functions(Getter); } + NodeList setters() const { return functions(Setter); } + NodeList resetters() const { return functions(Resetter); } + NodeList notifiers() const { return functions(Notifier); } + bool isStored() const { return fromTrool(sto, storedDefault()); } + bool isDesignable() const { return fromTrool(des, designableDefault()); } + bool isScriptable() const { return fromTrool(scr, scriptableDefault()); } + const QString& runtimeDesignabilityFunction() const { return runtimeDesFunc; } + const QString& runtimeScriptabilityFunction() const { return runtimeScrFunc; } + bool isWritable() const { return fromTrool(wri, writableDefault()); } + bool isUser() const { return fromTrool(usr, userDefault()); } + bool isConstant() const { return cst; } + bool isFinal() const { return fnl; } + const PropertyNode* overriddenFrom() const { return overrides; } + + bool storedDefault() const { return true; } + bool userDefault() const { return false; } + bool designableDefault() const { return !setters().isEmpty(); } + bool scriptableDefault() const { return true; } + bool writableDefault() const { return !setters().isEmpty(); } + +private: + enum Trool { Trool_True, Trool_False, Trool_Default }; + + static Trool toTrool(bool boolean); + static bool fromTrool(Trool troolean, bool defaultValue); + + QString type_; + QString runtimeDesFunc; + QString runtimeScrFunc; + NodeList funcs[NumFunctionRoles]; + Trool sto; + Trool des; + Trool scr; + Trool wri; + Trool usr; + bool cst; + bool fnl; + int rev; + const PropertyNode* overrides; +}; + +inline void FunctionNode::setParameters(const QList<Parameter> ¶meters) +{ + params = parameters; +} + +inline void PropertyNode::addFunction(FunctionNode* function, FunctionRole role) +{ + funcs[(int)role].append(function); + function->setAssociatedProperty(this); +} + +inline void PropertyNode::addSignal(FunctionNode* function, FunctionRole role) +{ + funcs[(int)role].append(function); +} + +inline NodeList PropertyNode::functions() const +{ + NodeList list; + for (int i = 0; i < NumFunctionRoles; ++i) + list += funcs[i]; + return list; +} + +class VariableNode : public LeafNode +{ +public: + VariableNode(InnerNode* parent, const QString &name); + virtual ~VariableNode() { } + + void setLeftType(const QString &leftType) { lt = leftType; } + void setRightType(const QString &rightType) { rt = rightType; } + void setStatic(bool statique) { sta = statique; } + + const QString &leftType() const { return lt; } + const QString &rightType() const { return rt; } + QString dataType() const { return lt + rt; } + bool isStatic() const { return sta; } + +private: + QString lt; + QString rt; + bool sta; +}; + +inline VariableNode::VariableNode(InnerNode* parent, const QString &name) + : LeafNode(Variable, parent, name), sta(false) +{ + // nothing. +} + +class TargetNode : public LeafNode +{ +public: + TargetNode(InnerNode* parent, const QString& name); + virtual ~TargetNode() { } + + virtual bool isInnerNode() const; +}; + +class DitaMapNode : public FakeNode +{ +public: + DitaMapNode(InnerNode* parent, const QString& name) + : FakeNode(parent, name, Node::Page, Node::DitaMapPage) { } + virtual ~DitaMapNode() { } + + const DitaRefList& map() const { return doc().ditamap(); } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/openedlist.cpp b/src/tools/qdoc/openedlist.cpp new file mode 100644 index 0000000000..9fa93f0d28 --- /dev/null +++ b/src/tools/qdoc/openedlist.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + openedlist.cpp +*/ + +#include <qregexp.h> + +#include "atom.h" +#include "openedlist.h" + +QT_BEGIN_NAMESPACE + +static const char roman[] = "m\2d\5c\2l\5x\2v\5i"; + +OpenedList::OpenedList( Style style ) + : sty( style ), ini( 1 ), nex( 0 ) +{ +} + +OpenedList::OpenedList( const Location& location, const QString& hint ) + : sty( Bullet ), ini( 1 ) +{ + QRegExp hintSyntax( "(\\W*)([0-9]+|[A-Z]+|[a-z]+)(\\W*)" ); + + if ( hintSyntax.exactMatch(hint) ) { + bool ok; + int asNumeric = hint.toInt( &ok ); + int asRoman = fromRoman( hintSyntax.cap(2) ); + int asAlpha = fromAlpha( hintSyntax.cap(2) ); + + if ( ok ) { + sty = Numeric; + ini = asNumeric; + } else if ( asRoman > 0 && asRoman != 100 && asRoman != 500 ) { + sty = ( hint == hint.toLower() ) ? LowerRoman : UpperRoman; + ini = asRoman; + } else { + sty = ( hint == hint.toLower() ) ? LowerAlpha : UpperAlpha; + ini = asAlpha; + } + pref = hintSyntax.cap( 1 ); + suff = hintSyntax.cap( 3 ); + } else if ( !hint.isEmpty() ) { + location.warning( tr("Unrecognized list style '%1'").arg(hint) ); + } + nex = ini - 1; +} + +QString OpenedList::styleString() const +{ + switch ( style() ) { + case Bullet: + default: + return ATOM_LIST_BULLET; + case Tag: + return ATOM_LIST_TAG; + case Value: + return ATOM_LIST_VALUE; + case Numeric: + return ATOM_LIST_NUMERIC; + case UpperAlpha: + return ATOM_LIST_UPPERALPHA; + case LowerAlpha: + return ATOM_LIST_LOWERALPHA; + case UpperRoman: + return ATOM_LIST_UPPERROMAN; + case LowerRoman: + return ATOM_LIST_LOWERROMAN; + } +} + +QString OpenedList::numberString() const +{ + return QString::number( number() ); + /* + switch ( style() ) { + case Numeric: + return QString::number( number() ); + case UpperAlpha: + return toAlpha( number() ).toUpper(); + case LowerAlpha: + return toAlpha( number() ); + case UpperRoman: + return toRoman( number() ).toUpper(); + case LowerRoman: + return toRoman( number() ); + case Bullet: + default: + return "*"; + }*/ +} + +QString OpenedList::toAlpha( int n ) +{ + QString str; + + while ( n > 0 ) { + n--; + str.prepend( (n % 26) + 'a' ); + n /= 26; + } + return str; +} + +int OpenedList::fromAlpha( const QString& str ) +{ + int n = 0; + int u; + + for ( int i = 0; i < (int) str.length(); i++ ) { + u = str[i].toLower().unicode(); + if ( u >= 'a' && u <= 'z' ) { + n *= 26; + n += u - 'a' + 1; + } else { + return 0; + } + } + return n; +} + +QString OpenedList::toRoman( int n ) +{ + /* + See p. 30 of Donald E. Knuth's "TeX: The Program". + */ + QString str; + int j = 0; + int k; + int u; + int v = 1000; + + for ( ;; ) { + while ( n >= v ) { + str += roman[j]; + n -= v; + } + + if ( n <= 0 ) + break; + + k = j + 2; + u = v / roman[k - 1]; + if ( roman[k - 1] == 2 ) { + k += 2; + u /= 5; + } + if ( n + u >= v ) { + str += roman[k]; + n += u; + } else { + j += 2; + v /= roman[j - 1]; + } + } + return str; +} + +int OpenedList::fromRoman( const QString& str ) +{ + int n = 0; + int j; + int u; + int v = 0; + + for ( int i = str.length() - 1; i >= 0; i-- ) { + j = 0; + u = 1000; + while ( roman[j] != 'i' && roman[j] != str[i].toLower() ) { + j += 2; + u /= roman[j - 1]; + } + if ( u < v ) { + n -= u; + } else { + n += u; + } + v = u; + } + + if ( str.toLower() == toRoman(n) ) { + return n; + } else { + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/openedlist.h b/src/tools/qdoc/openedlist.h new file mode 100644 index 0000000000..7150ca60c7 --- /dev/null +++ b/src/tools/qdoc/openedlist.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + openedlist.h +*/ + +#ifndef OPENEDLIST_H +#define OPENEDLIST_H + +#include <qstring.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class OpenedList +{ +public: + enum Style { Bullet, Tag, Value, Numeric, UpperAlpha, LowerAlpha, + UpperRoman, LowerRoman }; + + OpenedList() + : sty( Bullet ), ini( 1 ), nex( 0 ) { } + OpenedList( Style style ); + OpenedList( const Location& location, const QString& hint ); + + void next() { nex++; } + + bool isStarted() const { return nex >= ini; } + Style style() const { return sty; } + QString styleString() const; + int number() const { return nex; } + QString numberString() const; + QString prefix() const { return pref; } + QString suffix() const { return suff; } + +private: + static QString toAlpha( int n ); + static int fromAlpha( const QString& str ); + static QString toRoman( int n ); + static int fromRoman( const QString& str ); + + Style sty; + int ini; + int nex; + QString pref; + QString suff; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/pagegenerator.cpp b/src/tools/qdoc/pagegenerator.cpp new file mode 100644 index 0000000000..d8c3babe2e --- /dev/null +++ b/src/tools/qdoc/pagegenerator.cpp @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + pagegenerator.cpp +*/ + +#include <qfile.h> +#include <qfileinfo.h> +#include <qdebug.h> +#include "codemarker.h" +#include "pagegenerator.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +/*! + Nothing to do in the constructor. + */ +PageGenerator::PageGenerator() + : outputCodec(0) +{ + // nothing. +} + +/*! + The destructor + */ +PageGenerator::~PageGenerator() +{ + while (!outStreamStack.isEmpty()) + endSubPage(); +} + +bool PageGenerator::parseArg(const QString& src, + const QString& tag, + int* pos, + int n, + QStringRef* contents, + QStringRef* par1, + bool debug) +{ +#define SKIP_CHAR(c) \ + if (debug) \ + qDebug() << "looking for " << c << " at " << QString(src.data() + i, n - i); \ + if (i >= n || src[i] != c) { \ + if (debug) \ + qDebug() << " char '" << c << "' not found"; \ + return false; \ +} \ + ++i; + + +#define SKIP_SPACE \ + while (i < n && src[i] == ' ') \ + ++i; + + int i = *pos; + int j = i; + + // assume "<@" has been parsed outside + //SKIP_CHAR('<'); + //SKIP_CHAR('@'); + + if (tag != QStringRef(&src, i, tag.length())) { + if (0 && debug) + qDebug() << "tag " << tag << " not found at " << i; + return false; + } + + if (debug) + qDebug() << "haystack:" << src << "needle:" << tag << "i:" <<i; + + // skip tag + i += tag.length(); + + // parse stuff like: linkTag("(<@link node=\"([^\"]+)\">).*(</@link>)"); + if (par1) { + SKIP_SPACE; + // read parameter name + j = i; + while (i < n && src[i].isLetter()) + ++i; + if (src[i] == '=') { + if (debug) + qDebug() << "read parameter" << QString(src.data() + j, i - j); + SKIP_CHAR('='); + SKIP_CHAR('"'); + // skip parameter name + j = i; + while (i < n && src[i] != '"') + ++i; + *par1 = QStringRef(&src, j, i - j); + SKIP_CHAR('"'); + SKIP_SPACE; + } else { + if (debug) + qDebug() << "no optional parameter found"; + } + } + SKIP_SPACE; + SKIP_CHAR('>'); + + // find contents up to closing "</@tag> + j = i; + for (; true; ++i) { + if (i + 4 + tag.length() > n) + return false; + if (src[i] != '<') + continue; + if (src[i + 1] != '/') + continue; + if (src[i + 2] != '@') + continue; + if (tag != QStringRef(&src, i + 3, tag.length())) + continue; + if (src[i + 3 + tag.length()] != '>') + continue; + break; + } + + *contents = QStringRef(&src, j, i - j); + + i += tag.length() + 4; + + *pos = i; + if (debug) + qDebug() << " tag " << tag << " found: pos now: " << i; + return true; +#undef SKIP_CHAR +} + +/*! + This function is recursive. + */ +void PageGenerator::generateTree(const Tree *tree) +{ + generateInnerNode(tree->root()); +} + +QString PageGenerator::fileBase(const Node *node) const +{ + if (node->relates()) + node = node->relates(); + else if (!node->isInnerNode()) + node = node->parent(); + if (node->subType() == Node::QmlPropertyGroup) { + node = node->parent(); + } + + QString base = node->doc().baseName(); + if (!base.isEmpty()) + return base; + + const Node *p = node; + + forever { + const Node *pp = p->parent(); + base.prepend(p->name()); + if (!p->qmlModuleIdentifier().isEmpty()) + base.prepend(p->qmlModuleIdentifier()+QChar('-')); + /* + To avoid file name conflicts in the html directory, + we prepend a prefix (by default, "qml-") to the file name of QML + element doc files. + */ + if ((p->subType() == Node::QmlClass) || + (p->subType() == Node::QmlBasicType)) { + base.prepend(outputPrefix(QLatin1String("QML"))); + } + if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake) + break; + base.prepend(QLatin1Char('-')); + p = pp; + } + if (node->type() == Node::Fake) { + if (node->subType() == Node::Collision) { + const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); + if (ncn->currentChild()) + return fileBase(ncn->currentChild()); + base.prepend("collision-"); + } +#ifdef QDOC2_COMPAT + if (base.endsWith(".html")) + base.truncate(base.length() - 5); +#endif + } + + // the code below is effectively equivalent to: + // base.replace(QRegExp("[^A-Za-z0-9]+"), " "); + // base = base.trimmed(); + // base.replace(QLatin1Char(' '), QLatin1Char('-')); + // base = base.toLower(); + // as this function accounted for ~8% of total running time + // we optimize a bit... + + QString res; + // +5 prevents realloc in fileName() below + res.reserve(base.size() + 5); + bool begun = false; + for (int i = 0; i != base.size(); ++i) { + QChar c = base.at(i); + uint u = c.unicode(); + if (u >= 'A' && u <= 'Z') + u -= 'A' - 'a'; + if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) { + res += QLatin1Char(u); + begun = true; + } + else if (begun) { + res += QLatin1Char('-'); + begun = false; + } + } + while (res.endsWith(QLatin1Char('-'))) + res.chop(1); + return res; +} + +/*! + If the \a node has a URL, return the URL as the file name. + Otherwise, construct the file name from the fileBase() and + the fileExtension(), and return the constructed name. + */ +QString PageGenerator::fileName(const Node* node) const +{ + if (!node->url().isEmpty()) + return node->url(); + + QString name = fileBase(node); + name += QLatin1Char('.'); + name += fileExtension(node); + return name; +} + +/*! + Return the current output file name. + */ +QString PageGenerator::outFileName() +{ + return QFileInfo(static_cast<QFile*>(out().device())->fileName()).fileName(); +} + +/*! + Creates the file named \a fileName in the output directory. + Attaches a QTextStream to the created file, which is written + to all over the place using out(). + */ +void PageGenerator::beginSubPage(const InnerNode* node, const QString& fileName) +{ + QString path = outputDir() + QLatin1Char('/'); + if (!node->outputSubdirectory().isEmpty()) + path += node->outputSubdirectory() + QLatin1Char('/'); + path += fileName; + QFile* outFile = new QFile(path); + if (!outFile->open(QFile::WriteOnly)) + node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName())); + QTextStream* out = new QTextStream(outFile); + + if (outputCodec) + out->setCodec(outputCodec); + outStreamStack.push(out); +} + +/*! + Flush the text stream associated with the subpage, and + then pop it off the text stream stack and delete it. + This terminates output of the subpage. + */ +void PageGenerator::endSubPage() +{ + outStreamStack.top()->flush(); + delete outStreamStack.top()->device(); + delete outStreamStack.pop(); +} + +/*! + Used for writing to the current output stream. Returns a + reference to the crrent output stream, which is then used + with the \c {<<} operator for writing. + */ +QTextStream &PageGenerator::out() +{ + return *outStreamStack.top(); +} + +/*! + Recursive writing of HTML files from the root \a node. + + \note NameCollisionNodes are skipped here and processed + later. See HtmlGenerator::generateDisambiguationPages() + for more on this. + */ +void +PageGenerator::generateInnerNode(const InnerNode* node) +{ + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == Node::ExternalPage) + return; + if (fakeNode->subType() == Node::Image) + return; + if (fakeNode->subType() == Node::QmlPropertyGroup) + return; + if (fakeNode->subType() == Node::Page) { + if (node->count() > 0) + qDebug("PAGE %s HAS CHILDREN", qPrintable(fakeNode->title())); + } + } + + /* + Obtain a code marker for the source file. + */ + CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); + + if (node->parent() != 0) { + /* + Skip name collision nodes here and process them + later in generateDisambiguationPages(). Each one + is appended to a list for later. + */ + if ((node->type() == Node::Fake) && (node->subType() == Node::Collision)) { + const NameCollisionNode* ncn = static_cast<const NameCollisionNode*>(node); + collisionNodes.append(const_cast<NameCollisionNode*>(ncn)); + } + else { + beginSubPage(node, fileName(node)); + if (node->type() == Node::Namespace || node->type() == Node::Class) { + generateClassLikeNode(node, marker); + } + else if (node->type() == Node::Fake) { + generateFakeNode(static_cast<const FakeNode *>(node), marker); + } + endSubPage(); + } + } + + NodeList::ConstIterator c = node->childNodes().begin(); + while (c != node->childNodes().end()) { + if ((*c)->isInnerNode() && (*c)->access() != Node::Private) { + generateInnerNode((const InnerNode *) *c); + } + ++c; + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/pagegenerator.h b/src/tools/qdoc/pagegenerator.h new file mode 100644 index 0000000000..da0d32d2ff --- /dev/null +++ b/src/tools/qdoc/pagegenerator.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + pagegenerator.h +*/ + +#ifndef PAGEGENERATOR_H +#define PAGEGENERATOR_H + +#include <QStack> +#include <qtextstream.h> +#include "generator.h" +#include "location.h" + +QT_BEGIN_NAMESPACE + +class QTextCodec; +class ClassNode; +class InnerNode; +class NamespaceNode; +class NameCollisionNode; + +class PageGenerator : public Generator +{ +public: + PageGenerator(); + ~PageGenerator(); + + virtual void generateTree(const Tree *tree); + virtual void generateDisambiguationPages() { } + +protected: + virtual QString fileBase(const Node* node) const; + virtual QString fileExtension(const Node* node) const = 0; + QString fileName(const Node* node) const; + QString outFileName(); + virtual void beginSubPage(const InnerNode* node, const QString& fileName); + virtual void endSubPage(); + virtual void generateInnerNode(const InnerNode *node); + QTextStream& out(); + + QString naturalLanguage; + QString outputEncoding; + QTextCodec* outputCodec; + bool parseArg(const QString& src, + const QString& tag, + int* pos, + int n, + QStringRef* contents, + QStringRef* par1 = 0, + bool debug = false); + +protected: + QStack<QTextStream*> outStreamStack; + QList<NameCollisionNode*> collisionNodes; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/plaincodemarker.cpp b/src/tools/qdoc/plaincodemarker.cpp new file mode 100644 index 0000000000..a8b2277056 --- /dev/null +++ b/src/tools/qdoc/plaincodemarker.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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 "plaincodemarker.h" + +QT_BEGIN_NAMESPACE + +PlainCodeMarker::PlainCodeMarker() +{ +} + +PlainCodeMarker::~PlainCodeMarker() +{ +} + +bool PlainCodeMarker::recognizeCode( const QString& /* code */ ) +{ + return true; +} + +bool PlainCodeMarker::recognizeExtension( const QString& /* ext */ ) +{ + return true; +} + +bool PlainCodeMarker::recognizeLanguage( const QString& /* lang */ ) +{ + return false; +} + +Atom::Type PlainCodeMarker::atomType() const +{ + return Atom::Code; +} + +QString PlainCodeMarker::plainName( const Node * /* node */ ) +{ + return ""; +} + +QString PlainCodeMarker::plainFullName(const Node * /* node */, const Node * /* relative */) +{ + return ""; +} + +QString PlainCodeMarker::markedUpCode( const QString& code, + const Node * /* relative */, + const Location & /* location */ ) +{ + return protect( code ); +} + +QString PlainCodeMarker::markedUpSynopsis( const Node * /* node */, + const Node * /* relative */, + SynopsisStyle /* style */ ) +{ + return "foo"; +} + +QString PlainCodeMarker::markedUpName( const Node * /* node */ ) +{ + return ""; +} + +QString PlainCodeMarker::markedUpFullName( const Node * /* node */, + const Node * /* relative */ ) +{ + return ""; +} + +QString PlainCodeMarker::markedUpEnumValue(const QString & /* enumValue */, + const Node * /* relative */) +{ + return ""; +} + +QString PlainCodeMarker::markedUpIncludes( const QStringList& /* includes */ ) +{ + return ""; +} + +QString PlainCodeMarker::functionBeginRegExp( const QString& /* funcName */ ) +{ + return ""; +} + +QString PlainCodeMarker::functionEndRegExp( const QString& /* funcName */ ) +{ + return ""; +} + +QList<Section> PlainCodeMarker::sections(const InnerNode * /* innerNode */, + SynopsisStyle /* style */, + Status /* status */) +{ + return QList<Section>(); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/plaincodemarker.h b/src/tools/qdoc/plaincodemarker.h new file mode 100644 index 0000000000..0a46e2d4d4 --- /dev/null +++ b/src/tools/qdoc/plaincodemarker.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + plaincodemarker.h +*/ + +#ifndef PLAINCODEMARKER_H +#define PLAINCODEMARKER_H + +#include "codemarker.h" + +QT_BEGIN_NAMESPACE + +class PlainCodeMarker : public CodeMarker +{ +public: + PlainCodeMarker(); + ~PlainCodeMarker(); + + bool recognizeCode( const QString& code ); + bool recognizeExtension( const QString& ext ); + bool recognizeLanguage( const QString& lang ); + Atom::Type atomType() const; + QString plainName( const Node *node ); + QString plainFullName( const Node *node, const Node *relative ); + QString markedUpCode( const QString& code, const Node *relative, const Location &location ); + QString markedUpSynopsis( const Node *node, const Node *relative, + SynopsisStyle style ); + QString markedUpName( const Node *node ); + QString markedUpFullName( const Node *node, const Node *relative ); + QString markedUpEnumValue(const QString &enumValue, const Node *relative); + QString markedUpIncludes( const QStringList& includes ); + QString functionBeginRegExp( const QString& funcName ); + QString functionEndRegExp( const QString& funcName ); + QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/puredocparser.cpp b/src/tools/qdoc/puredocparser.cpp new file mode 100644 index 0000000000..2303591974 --- /dev/null +++ b/src/tools/qdoc/puredocparser.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + puredocparser.cpp +*/ + +#include "puredocparser.h" + +QT_BEGIN_NAMESPACE + +PureDocParser::PureDocParser() +{ +} + +PureDocParser::~PureDocParser() +{ +} + +QStringList PureDocParser::sourceFileNameFilter() +{ + return QStringList("*.qdoc"); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/puredocparser.h b/src/tools/qdoc/puredocparser.h new file mode 100644 index 0000000000..80efadf6a4 --- /dev/null +++ b/src/tools/qdoc/puredocparser.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + puredocparser.h +*/ + +#ifndef PUREDOCPARSER_H +#define PUREDOCPARSER_H + +#include <QSet> + +#include "cppcodeparser.h" +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Config; +class Node; +class QString; +class Tree; + +class PureDocParser : public CppCodeParser +{ +public: + PureDocParser(); + virtual ~PureDocParser(); + + virtual QStringList sourceFileNameFilter(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qdoc.pro b/src/tools/qdoc/qdoc.pro new file mode 100644 index 0000000000..fca5a3daf4 --- /dev/null +++ b/src/tools/qdoc/qdoc.pro @@ -0,0 +1,114 @@ +TEMPLATE = app +TARGET = qdoc + +DESTDIR = ../../../bin +DEFINES += QDOC2_COMPAT + +include(../bootstrap/bootstrap.pri) +DEFINES -= QT_NO_CAST_FROM_ASCII +DEFINES += QT_NO_TRANSLATION + +INCLUDEPATH += $$QT_SOURCE_TREE/src/tools/qdoc \ + $$QT_SOURCE_TREE/src/tools/qdoc/qmlparser +DEPENDPATH += $$QT_SOURCE_TREE/src/tools/qdoc \ + $$QT_SOURCE_TREE/src/tools/qdoc/qmlparser + +# Increase the stack size on MSVC to 4M to avoid a stack overflow +win32-msvc*:{ + QMAKE_LFLAGS += /STACK:4194304 +} + +HEADERS += atom.h \ + codechunk.h \ + codemarker.h \ + codeparser.h \ + config.h \ + cppcodemarker.h \ + cppcodeparser.h \ + ditaxmlgenerator.h \ + doc.h \ + editdistance.h \ + generator.h \ + helpprojectwriter.h \ + htmlgenerator.h \ + location.h \ + node.h \ + openedlist.h \ + pagegenerator.h \ + plaincodemarker.h \ + puredocparser.h \ + quoter.h \ + separator.h \ + text.h \ + tokenizer.h \ + tr.h \ + tree.h +SOURCES += atom.cpp \ + codechunk.cpp \ + codemarker.cpp \ + codeparser.cpp \ + config.cpp \ + cppcodemarker.cpp \ + cppcodeparser.cpp \ + ditaxmlgenerator.cpp \ + doc.cpp \ + editdistance.cpp \ + generator.cpp \ + helpprojectwriter.cpp \ + htmlgenerator.cpp \ + location.cpp \ + main.cpp \ + node.cpp \ + openedlist.cpp \ + pagegenerator.cpp \ + plaincodemarker.cpp \ + puredocparser.cpp \ + quoter.cpp \ + separator.cpp \ + text.cpp \ + tokenizer.cpp \ + tree.cpp \ + yyindent.cpp + +### QML/JS Parser ### + +DEFINES += HAVE_DECLARATIVE +include(qmlparser/qmlparser.pri) + +HEADERS += jscodemarker.h \ + qmlcodemarker.h \ + qmlcodeparser.h \ + qmlmarkupvisitor.h \ + qmlvisitor.h + +SOURCES += jscodemarker.cpp \ + qmlcodemarker.cpp \ + qmlcodeparser.cpp \ + qmlmarkupvisitor.cpp \ + qmlvisitor.cpp + +### Documentation for qdoc3 ### + +qtPrepareTool(QDOC, qdoc3) +qtPrepareTool(QHELPGENERATOR, qhelpgenerator) + +equals(QMAKE_DIR_SEP, /) { + QDOC = QT_BUILD_TREE=$$QT_BUILD_TREE QT_SOURCE_TREE=$$QT_SOURCE_TREE $$QDOC +} else { + QDOC = set QT_BUILD_TREE=$$QT_BUILD_TREE&& set QT_SOURCE_TREE=$$QT_SOURCE_TREE&& $$QDOC + QDOC = $$replace(QDOC, "/", "\\") +} + +html-docs.commands = cd \"$$QT_BUILD_TREE/doc\" && $$QDOC $$QT_SOURCE_TREE/tools/qdoc3/doc/config/qdoc.qdocconf +html-docs.files = $$QT_BUILD_TREE/doc/html + +qch-docs.commands = cd \"$$QT_BUILD_TREE/doc\" && $$QHELPGENERATOR $$QT_BUILD_TREE/tools/qdoc3/doc/html/qdoc.qhp -o $$QT_BUILD_TREE/tools/qdoc3/doc/qch/qdoc.qch +qch-docs.files = $$QT_BUILD_TREE/tools/qdoc3/doc/qch +qch-docs.path = $$[QT_INSTALL_DOCS] +qch-docs.CONFIG += no_check_exist directory + +QMAKE_EXTRA_TARGETS += html-docs qch-docs + +target.path = $$[QT_HOST_BINS] +INSTALLS += target +load(qt_targets) diff --git a/src/tools/qdoc/qmlcodemarker.cpp b/src/tools/qdoc/qmlcodemarker.cpp new file mode 100644 index 0000000000..26483cc0d8 --- /dev/null +++ b/src/tools/qdoc/qmlcodemarker.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + qmlcodemarker.cpp +*/ + +#include "qqmljsast_p.h" +#include "qqmljsastfwd_p.h" +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsparser_p.h" + +#include "atom.h" +#include "node.h" +#include "qmlcodemarker.h" +#include "qmlmarkupvisitor.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +QmlCodeMarker::QmlCodeMarker() +{ +} + +QmlCodeMarker::~QmlCodeMarker() +{ +} + +/*! + Returns true if the \a code is recognized by the parser. + */ +bool QmlCodeMarker::recognizeCode(const QString &code) +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + QQmlJS::Parser parser(&engine); + + QString newCode = code; + extractPragmas(newCode); + lexer.setCode(newCode, 1); + + return parser.parse(); +} + +/*! + Returns true if \a ext is any of a list of file extensions + for the QML language. + */ +bool QmlCodeMarker::recognizeExtension(const QString &ext) +{ + return ext == "qml"; +} + +/*! + Returns true if the \a language is recognized. Only "QML" is + recognized by this marker. + */ +bool QmlCodeMarker::recognizeLanguage(const QString &language) +{ + return language == "QML"; +} + +/*! + Returns the type of atom used to represent QML code in the documentation. +*/ +Atom::Type QmlCodeMarker::atomType() const +{ + return Atom::Qml; +} + +/*! + Returns the name of the \a node. Method names include are returned with a + trailing set of parentheses. + */ +QString QmlCodeMarker::plainName(const Node *node) +{ + QString name = node->name(); + if (node->type() == Node::QmlMethod) + name += "()"; + return name; +} + +QString QmlCodeMarker::plainFullName(const Node *node, const Node *relative) +{ + if (node->name().isEmpty()) { + return "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("::"); + node = node->parent(); + } + return fullName; + } +} + +QString QmlCodeMarker::markedUpCode(const QString &code, + const Node *relative, + const Location &location) +{ + return addMarkUp(code, relative, location); +} + +QString QmlCodeMarker::markedUpName(const Node *node) +{ + QString name = linkTag(node, taggedNode(node)); + if (node->type() == Node::QmlMethod) + name += "()"; + return name; +} + +QString QmlCodeMarker::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 QmlCodeMarker::markedUpIncludes(const QStringList& includes) +{ + QString code; + + QStringList::ConstIterator inc = includes.begin(); + while (inc != includes.end()) { + code += "import " + *inc + QLatin1Char('\n'); + ++inc; + } + Location location; + return addMarkUp(code, 0, location); +} + +QString QmlCodeMarker::functionBeginRegExp(const QString& funcName) +{ + return "^" + QRegExp::escape("function " + funcName) + "$"; + +} + +QString QmlCodeMarker::functionEndRegExp(const QString& /* funcName */) +{ + return "^\\}$"; +} + +QString QmlCodeMarker::addMarkUp(const QString &code, + const Node * /* relative */, + const Location &location) +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + + QString newCode = code; + QList<QQmlJS::AST::SourceLocation> pragmas = extractPragmas(newCode); + lexer.setCode(newCode, 1); + + QQmlJS::Parser parser(&engine); + QString output; + + if (parser.parse()) { + QQmlJS::AST::UiProgram *ast = parser.ast(); + // Pass the unmodified code to the visitor so that pragmas and other + // unhandled source text can be output. + QmlMarkupVisitor visitor(code, pragmas, &engine); + QQmlJS::AST::Node::accept(ast, &visitor); + output = visitor.markedUpCode(); + } else { + location.warning(tr("Unable to parse QML snippet: \"%1\" at line %2, column %3").arg( + parser.errorMessage()).arg(parser.errorLineNumber()).arg( + parser.errorColumnNumber())); + output = protect(code); + } + + return output; +} + +/* + Copied and pasted from + src/declarative/qml/qqmlscriptparser.cpp. +*/ +static void replaceWithSpace(QString &str, int idx, int n) +{ + QChar *data = str.data() + idx; + const QChar space(QLatin1Char(' ')); + for (int ii = 0; ii < n; ++ii) + *data++ = space; +} + +/* + Copied and pasted from + src/declarative/qml/qqmlscriptparser.cpp then modified to + return a list of removed pragmas. + + Searches for ".pragma <value>" declarations within \a script. + Currently supported pragmas are: library +*/ +QList<QQmlJS::AST::SourceLocation> QmlCodeMarker::extractPragmas(QString &script) +{ + const QString pragma(QLatin1String("pragma")); + const QString library(QLatin1String("library")); + QList<QQmlJS::AST::SourceLocation> removed; + + QQmlJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QQmlJSGrammar::T_DOT) + return removed; + + int startOffset = l.tokenOffset(); + int startLine = l.tokenStartLine(); + int startColumn = l.tokenStartColumn(); + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine || + script.mid(l.tokenOffset(), l.tokenLength()) != pragma) + return removed; + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine) + return removed; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return removed; + + if (pragmaValue == QLatin1String("library")) { + replaceWithSpace(script, startOffset, endOffset - startOffset); + removed.append( + QQmlJS::AST::SourceLocation( + startOffset, endOffset - startOffset, + startLine, startColumn)); + } else + return removed; + } + return removed; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qmlcodemarker.h b/src/tools/qdoc/qmlcodemarker.h new file mode 100644 index 0000000000..4bcebb43ba --- /dev/null +++ b/src/tools/qdoc/qmlcodemarker.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + qmlcodemarker.h +*/ + +#ifndef QMLCODEMARKER_H +#define QMLCODEMARKER_H + +#include "qqmljsastfwd_p.h" +#include "cppcodemarker.h" + +QT_BEGIN_NAMESPACE + +class QmlCodeMarker : public CppCodeMarker +{ +public: + QmlCodeMarker(); + ~QmlCodeMarker(); + + virtual bool recognizeCode(const QString &code); + virtual bool recognizeExtension(const QString &ext); + virtual bool recognizeLanguage(const QString &language); + 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 markedUpName(const Node *node); + virtual QString markedUpFullName(const Node *node, const Node *relative); + virtual QString markedUpIncludes(const QStringList &includes); + virtual QString functionBeginRegExp(const QString &funcName); + virtual QString functionEndRegExp(const QString &funcName); + + /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */ + QList<QQmlJS::AST::SourceLocation> extractPragmas(QString &script); + +private: + QString addMarkUp(const QString &code, const Node *relative, + const Location &location); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlcodeparser.cpp b/src/tools/qdoc/qmlcodeparser.cpp new file mode 100644 index 0000000000..23b8af8567 --- /dev/null +++ b/src/tools/qdoc/qmlcodeparser.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + qmlcodeparser.cpp +*/ + +#include "qqmljsast_p.h" +#include "qqmljsastvisitor_p.h" + +#include "qmlcodeparser.h" +#include "node.h" +#include "tree.h" +#include "config.h" +#include "qmlvisitor.h" +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +#define COMMAND_STARTPAGE Doc::alias("startpage") +#define COMMAND_VARIABLE Doc::alias("variable") + +#define COMMAND_DEPRECATED Doc::alias("deprecated") +#define COMMAND_INGROUP Doc::alias("ingroup") +#define COMMAND_INTERNAL Doc::alias("internal") +#define COMMAND_OBSOLETE Doc::alias("obsolete") +#define COMMAND_PAGEKEYWORDS Doc::alias("pagekeywords") +#define COMMAND_PRELIMINARY Doc::alias("preliminary") +#define COMMAND_SINCE Doc::alias("since") + +#define COMMAND_QMLABSTRACT Doc::alias("qmlabstract") +#define COMMAND_QMLCLASS Doc::alias("qmlclass") +#define COMMAND_QMLMODULE Doc::alias("qmlmodule") +#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty") +#define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty") +#define COMMAND_QMLINHERITS Doc::alias("inherits") +#define COMMAND_INQMLMODULE Doc::alias("inqmlmodule") +#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") + +QmlCodeParser::QmlCodeParser() +{ +} + +QmlCodeParser::~QmlCodeParser() +{ +} + +/*! + Initializes the code parser base class. The \a config argument + is passed to the initialization functions in the base class. + + Also creates a lexer and parser from QQmlJS. + */ +void QmlCodeParser::initializeParser(const Config &config) +{ + CodeParser::initializeParser(config); + + lexer = new QQmlJS::Lexer(&engine); + parser = new QQmlJS::Parser(&engine); +} + +/*! + Deletes the lexer and parser created by the constructor. + */ +void QmlCodeParser::terminateParser() +{ + delete lexer; + delete parser; +} + +/*! + Returns "QML". + */ +QString QmlCodeParser::language() +{ + return "QML"; +} + +/*! + Returns a filter string of "*.qml". + */ +QStringList QmlCodeParser::sourceFileNameFilter() +{ + return QStringList("*.qml"); +} + +/*! + Parses the source file at \a filePath, creating nodes as + needed and inserting them into the \a tree. \a location is + used for error reporting. + + If it can't open the file at \a filePath, it reports an + error and returns without doing anything. + */ +void QmlCodeParser::parseSourceFile(const Location& location, + const QString& filePath, + Tree *tree) +{ + QFile in(filePath); + if (!in.open(QIODevice::ReadOnly)) { + location.error(tr("Cannot open QML file '%1'").arg(filePath)); + return; + } + createOutputSubdirectory(location, filePath); + + QString document = in.readAll(); + in.close(); + + Location fileLocation(filePath); + + QString newCode = document; + extractPragmas(newCode); + lexer->setCode(newCode, 1); + + QSet<QString> topicCommandsAllowed = topicCommands(); + QSet<QString> otherMetacommandsAllowed = otherMetaCommands(); + QSet<QString> metacommandsAllowed = topicCommandsAllowed + otherMetacommandsAllowed; + + if (parser->parse()) { + QQmlJS::AST::UiProgram *ast = parser->ast(); + QmlDocVisitor visitor(filePath, + newCode, + &engine, + tree, + metacommandsAllowed, + topicCommandsAllowed); + QQmlJS::AST::Node::accept(ast, &visitor); + } + foreach (const QQmlJS::DiagnosticMessage &msg, parser->diagnosticMessages()) { + qDebug().nospace() << qPrintable(filePath) << ':' << msg.loc.startLine + << ": QML syntax error at col " << msg.loc.startColumn + << ": " << qPrintable(msg.message); + } +} + +/*! + This function is called when the parser finishes parsing + the file, but in this case the function does nothing. + */ +void QmlCodeParser::doneParsingSourceFiles(Tree *) +{ +} + +/*! + Returns the set of strings representing the topic commands. + */ +QSet<QString> QmlCodeParser::topicCommands() +{ + return QSet<QString>() << COMMAND_VARIABLE + << COMMAND_QMLCLASS + << COMMAND_QMLPROPERTY + << COMMAND_QMLATTACHEDPROPERTY + << COMMAND_QMLSIGNAL + << COMMAND_QMLATTACHEDSIGNAL + << COMMAND_QMLMETHOD + << COMMAND_QMLATTACHEDMETHOD + << COMMAND_QMLBASICTYPE; +} + +/*! + Returns the set of strings representing the common metacommands + plus some other metacommands. + */ +QSet<QString> QmlCodeParser::otherMetaCommands() +{ + return commonMetaCommands() << COMMAND_STARTPAGE + << COMMAND_QMLINHERITS + << COMMAND_QMLDEFAULT + << COMMAND_QMLREADONLY + << COMMAND_DEPRECATED + << COMMAND_INGROUP + << COMMAND_INTERNAL + << COMMAND_OBSOLETE + << COMMAND_PRELIMINARY + << COMMAND_SINCE + << COMMAND_QMLABSTRACT + << COMMAND_INQMLMODULE; + +} + +/*! + Copy and paste from src/declarative/qml/qdeclarativescriptparser.cpp. + This function blanks out the section of the \a str beginning at \a idx + and running for \a n characters. +*/ +static void replaceWithSpace(QString &str, int idx, int n) +{ + QChar *data = str.data() + idx; + const QChar space(QLatin1Char(' ')); + for (int ii = 0; ii < n; ++ii) + *data++ = space; +} + +/*! + Copy & paste from src/declarative/qml/qdeclarativescriptparser.cpp, + then modified to return no values. + + Searches for ".pragma <value>" declarations within \a script. + Currently supported pragmas are: library +*/ +void QmlCodeParser::extractPragmas(QString &script) +{ + const QString pragma(QLatin1String("pragma")); + const QString library(QLatin1String("library")); + + QQmlJS::Lexer l(0); + l.setCode(script, 0); + + int token = l.lex(); + + while (true) { + if (token != QQmlJSGrammar::T_DOT) + return; + + int startOffset = l.tokenOffset(); + int startLine = l.tokenStartLine(); + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine || + script.mid(l.tokenOffset(), l.tokenLength()) != pragma) + return; + + token = l.lex(); + + if (token != QQmlJSGrammar::T_IDENTIFIER || + l.tokenStartLine() != startLine) + return; + + QString pragmaValue = script.mid(l.tokenOffset(), l.tokenLength()); + int endOffset = l.tokenLength() + l.tokenOffset(); + + token = l.lex(); + if (l.tokenStartLine() == startLine) + return; + + if (pragmaValue == QLatin1String("library")) + replaceWithSpace(script, startOffset, endOffset - startOffset); + else + return; + } + return; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qmlcodeparser.h b/src/tools/qdoc/qmlcodeparser.h new file mode 100644 index 0000000000..295c6a80ef --- /dev/null +++ b/src/tools/qdoc/qmlcodeparser.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + qmlcodeparser.h +*/ + +#ifndef QMLCODEPARSER_H +#define QMLCODEPARSER_H + +#include <QSet> +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsparser_p.h" + +#include "codeparser.h" + +QT_BEGIN_NAMESPACE + +class Config; +class Node; +class QString; +class Tree; + +class QmlCodeParser : public CodeParser +{ +public: + QmlCodeParser(); + virtual ~QmlCodeParser(); + + virtual void initializeParser(const Config& config); + virtual void terminateParser(); + virtual QString language(); + virtual QStringList sourceFileNameFilter(); + virtual void parseSourceFile(const Location& location, + const QString& filePath, Tree *tree); + virtual void doneParsingSourceFiles(Tree *tree); + + /* Copied from src/declarative/qml/qdeclarativescriptparser.cpp */ + void extractPragmas(QString &script); + +protected: + virtual QSet<QString> topicCommands(); + virtual QSet<QString> otherMetaCommands(); + +private: + QQmlJS::Engine engine; + QQmlJS::Lexer *lexer; + QQmlJS::Parser *parser; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlmarkupvisitor.cpp b/src/tools/qdoc/qmlmarkupvisitor.cpp new file mode 100644 index 0000000000..0b1883723f --- /dev/null +++ b/src/tools/qdoc/qmlmarkupvisitor.cpp @@ -0,0 +1,851 @@ +/**************************************************************************** +** +** 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 <QStringList> +#include <QtGlobal> +#include "qqmljsast_p.h" +#include "qqmljsastfwd_p.h" +#include "qqmljsengine_p.h" + +#include "qmlmarkupvisitor.h" + +QT_BEGIN_NAMESPACE + +QmlMarkupVisitor::QmlMarkupVisitor(const QString &source, + const QList<QQmlJS::AST::SourceLocation> &pragmas, + QQmlJS::Engine *engine) +{ + this->source = source; + this->engine = engine; + + cursor = 0; + extraIndex = 0; + + // Merge the lists of locations of pragmas and comments in the source code. + int i = 0; + int j = 0; + while (i < engine->comments().length() && j < pragmas.length()) { + if (engine->comments()[i].offset < pragmas[j].offset) { + extraTypes.append(Comment); + extraLocations.append(engine->comments()[i]); + ++i; + } else { + extraTypes.append(Pragma); + extraLocations.append(engine->comments()[j]); + ++j; + } + } + + while (i < engine->comments().length()) { + extraTypes.append(Comment); + extraLocations.append(engine->comments()[i]); + ++i; + } + + while (j < pragmas.length()) { + extraTypes.append(Pragma); + extraLocations.append(pragmas[j]); + ++j; + } +} + +QmlMarkupVisitor::~QmlMarkupVisitor() +{ +} + +// The protect() function is a copy of the one from CppCodeMarker. + +static const QString samp = QLatin1String("&"); +static const QString slt = QLatin1String("<"); +static const QString sgt = QLatin1String(">"); +static const QString squot = QLatin1String("""); + +QString QmlMarkupVisitor::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 QmlMarkupVisitor::markedUpCode() +{ + if (int(cursor) < source.length()) + addExtra(cursor, source.length()); + + return output; +} + +void QmlMarkupVisitor::addExtra(quint32 start, quint32 finish) +{ + if (extraIndex >= extraLocations.length()) { + QString extra = source.mid(start, finish - start); + if (extra.trimmed().isEmpty()) + output += extra; + else + output += protect(extra); // text that should probably have been caught by the parser + + cursor = finish; + return; + } + + while (extraIndex < extraLocations.length()) { + if (extraTypes[extraIndex] == Comment) { + if (extraLocations[extraIndex].offset - 2 >= start) + break; + } else { + if (extraLocations[extraIndex].offset >= start) + break; + } + extraIndex++; + } + + quint32 i = start; + while (i < finish && extraIndex < extraLocations.length()) { + quint32 j = extraLocations[extraIndex].offset - 2; + if (i <= j && j < finish) { + if (i < j) + output += protect(source.mid(i, j - i)); + + quint32 l = extraLocations[extraIndex].length; + if (extraTypes[extraIndex] == Comment) { + if (source.mid(j, 2) == QLatin1String("/*")) + l += 4; + else + l += 2; + output += QLatin1String("<@comment>"); + output += protect(source.mid(j, l)); + output += QLatin1String("</@comment>"); + } else + output += protect(source.mid(j, l)); + + extraIndex++; + i = j + l; + } else + break; + } + + QString extra = source.mid(i, finish - i); + if (extra.trimmed().isEmpty()) + output += extra; + else + output += protect(extra); // text that should probably have been caught by the parser + + cursor = finish; +} + +void QmlMarkupVisitor::addMarkedUpToken( + QQmlJS::AST::SourceLocation &location, const QString &tagName, + const QHash<QString, QString> &attributes) +{ + if (!location.isValid()) + return; + + if (cursor < location.offset) + addExtra(cursor, location.offset); + else if (cursor > location.offset) + return; + + output += QString(QLatin1String("<@%1")).arg(tagName); + foreach (const QString &key, attributes) + output += QString(QLatin1String(" %1=\"%2\"")).arg(key).arg(attributes[key]); + output += QString(QLatin1String(">%2</@%3>")).arg(protect(sourceText(location)), tagName); + cursor += location.length; +} + +QString QmlMarkupVisitor::sourceText(QQmlJS::AST::SourceLocation &location) +{ + return source.mid(location.offset, location.length); +} + +void QmlMarkupVisitor::addVerbatim(QQmlJS::AST::SourceLocation first, + QQmlJS::AST::SourceLocation last) +{ + if (!first.isValid()) + return; + + quint32 start = first.begin(); + quint32 finish; + if (last.isValid()) + finish = last.end(); + else + finish = first.end(); + + if (cursor < start) + addExtra(cursor, start); + else if (cursor > start) + return; + + QString text = source.mid(start, finish - start); + output += protect(text); + cursor = finish; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiImport *uiimport) +{ + addVerbatim(uiimport->importToken); + if (!uiimport->importUri) + addMarkedUpToken(uiimport->fileNameToken, QLatin1String("headerfile")); + return false; +} + +void QmlMarkupVisitor::endVisit(QQmlJS::AST::UiImport *uiimport) +{ + addVerbatim(uiimport->versionToken); + addVerbatim(uiimport->asToken); + addMarkedUpToken(uiimport->importIdToken, QLatin1String("headerfile")); + addVerbatim(uiimport->semicolonToken); +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiPublicMember *member) +{ + if (member->type == QQmlJS::AST::UiPublicMember::Property) { + addVerbatim(member->defaultToken); + addVerbatim(member->readonlyToken); + addVerbatim(member->propertyToken); + addVerbatim(member->typeModifierToken); + addMarkedUpToken(member->typeToken, QLatin1String("type")); + addMarkedUpToken(member->identifierToken, QLatin1String("name")); + addVerbatim(member->colonToken); + if (member->binding) + QQmlJS::AST::Node::accept(member->binding, this); + else if (member->statement) + QQmlJS::AST::Node::accept(member->statement, this); + } else { + addVerbatim(member->propertyToken); + addVerbatim(member->typeModifierToken); + addMarkedUpToken(member->typeToken, QLatin1String("type")); + //addVerbatim(member->identifierToken); + QQmlJS::AST::Node::accept(member->parameters, this); + } + addVerbatim(member->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectInitializer *initializer) +{ + addVerbatim(initializer->lbraceToken, initializer->lbraceToken); + return true; +} + +void QmlMarkupVisitor::endVisit(QQmlJS::AST::UiObjectInitializer *initializer) +{ + addVerbatim(initializer->rbraceToken, initializer->rbraceToken); +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectBinding *binding) +{ + QQmlJS::AST::Node::accept(binding->qualifiedId, this); + addVerbatim(binding->colonToken); + QQmlJS::AST::Node::accept(binding->qualifiedTypeNameId, this); + QQmlJS::AST::Node::accept(binding->initializer, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiScriptBinding *binding) +{ + QQmlJS::AST::Node::accept(binding->qualifiedId, this); + addVerbatim(binding->colonToken); + QQmlJS::AST::Node::accept(binding->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiArrayBinding *binding) +{ + QQmlJS::AST::Node::accept(binding->qualifiedId, this); + addVerbatim(binding->colonToken); + addVerbatim(binding->lbracketToken); + QQmlJS::AST::Node::accept(binding->members, this); + addVerbatim(binding->rbracketToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiArrayMemberList *list) +{ + for (QQmlJS::AST::UiArrayMemberList *it = list; it; it = it->next) { + QQmlJS::AST::Node::accept(it->member, this); + //addVerbatim(it->commaToken); + } + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiQualifiedId *id) +{ + addMarkedUpToken(id->identifierToken, QLatin1String("name")); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ThisExpression *expression) +{ + addVerbatim(expression->thisToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::IdentifierExpression *identifier) +{ + addMarkedUpToken(identifier->identifierToken, QLatin1String("name")); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::NullExpression *null) +{ + addMarkedUpToken(null->nullToken, QLatin1String("number")); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::TrueLiteral *literal) +{ + addMarkedUpToken(literal->trueToken, QLatin1String("number")); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::FalseLiteral *literal) +{ + addMarkedUpToken(literal->falseToken, QLatin1String("number")); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::NumericLiteral *literal) +{ + addMarkedUpToken(literal->literalToken, QLatin1String("number")); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::StringLiteral *literal) +{ + addMarkedUpToken(literal->literalToken, QLatin1String("string")); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::RegExpLiteral *literal) +{ + addVerbatim(literal->literalToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ArrayLiteral *literal) +{ + addVerbatim(literal->lbracketToken); + QQmlJS::AST::Node::accept(literal->elements, this); + addVerbatim(literal->rbracketToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ObjectLiteral *literal) +{ + addVerbatim(literal->lbraceToken); + return true; +} + +void QmlMarkupVisitor::endVisit(QQmlJS::AST::ObjectLiteral *literal) +{ + addVerbatim(literal->rbraceToken); +} + + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ElementList *list) +{ + for (QQmlJS::AST::ElementList *it = list; it; it = it->next) { + QQmlJS::AST::Node::accept(it->expression, this); + //addVerbatim(it->commaToken); + } + QQmlJS::AST::Node::accept(list->elision, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::Elision *elision) +{ + addVerbatim(elision->commaToken, elision->commaToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::PropertyNameAndValueList *list) +{ + QQmlJS::AST::Node::accept(list->name, this); + addVerbatim(list->colonToken, list->colonToken); + QQmlJS::AST::Node::accept(list->value, this); + addVerbatim(list->commaToken, list->commaToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ArrayMemberExpression *expression) +{ + QQmlJS::AST::Node::accept(expression->base, this); + addVerbatim(expression->lbracketToken); + QQmlJS::AST::Node::accept(expression->expression, this); + addVerbatim(expression->rbracketToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::FieldMemberExpression *expression) +{ + QQmlJS::AST::Node::accept(expression->base, this); + addVerbatim(expression->dotToken); + addMarkedUpToken(expression->identifierToken, QLatin1String("name")); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::NewMemberExpression *expression) +{ + addVerbatim(expression->newToken); + QQmlJS::AST::Node::accept(expression->base, this); + addVerbatim(expression->lparenToken); + QQmlJS::AST::Node::accept(expression->arguments, this); + addVerbatim(expression->rparenToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::NewExpression *expression) +{ + addVerbatim(expression->newToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ArgumentList *list) +{ + addVerbatim(list->commaToken, list->commaToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::PostIncrementExpression *expression) +{ + addVerbatim(expression->incrementToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::PostDecrementExpression *expression) +{ + addVerbatim(expression->decrementToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::DeleteExpression *expression) +{ + addVerbatim(expression->deleteToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::VoidExpression *expression) +{ + addVerbatim(expression->voidToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::TypeOfExpression *expression) +{ + addVerbatim(expression->typeofToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::PreIncrementExpression *expression) +{ + addVerbatim(expression->incrementToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::PreDecrementExpression *expression) +{ + addVerbatim(expression->decrementToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UnaryPlusExpression *expression) +{ + addVerbatim(expression->plusToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UnaryMinusExpression *expression) +{ + addVerbatim(expression->minusToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::TildeExpression *expression) +{ + addVerbatim(expression->tildeToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::NotExpression *expression) +{ + addVerbatim(expression->notToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::BinaryExpression *expression) +{ + QQmlJS::AST::Node::accept(expression->left, this); + addMarkedUpToken(expression->operatorToken, QLatin1String("op")); + QQmlJS::AST::Node::accept(expression->right, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ConditionalExpression *expression) +{ + QQmlJS::AST::Node::accept(expression->expression, this); + addVerbatim(expression->questionToken); + QQmlJS::AST::Node::accept(expression->ok, this); + addVerbatim(expression->colonToken); + QQmlJS::AST::Node::accept(expression->ko, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::Expression *expression) +{ + QQmlJS::AST::Node::accept(expression->left, this); + addVerbatim(expression->commaToken); + QQmlJS::AST::Node::accept(expression->right, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::Block *block) +{ + addVerbatim(block->lbraceToken); + return true; +} + +void QmlMarkupVisitor::endVisit(QQmlJS::AST::Block *block) +{ + addVerbatim(block->rbraceToken); +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableStatement *statement) +{ + addVerbatim(statement->declarationKindToken); + QQmlJS::AST::Node::accept(statement->declarations, this); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableDeclarationList *list) +{ + for (QQmlJS::AST::VariableDeclarationList *it = list; it; it = it->next) { + QQmlJS::AST::Node::accept(it->declaration, this); + addVerbatim(it->commaToken); + } + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::VariableDeclaration *declaration) +{ + addMarkedUpToken(declaration->identifierToken, QLatin1String("name")); + QQmlJS::AST::Node::accept(declaration->expression, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::EmptyStatement *statement) +{ + addVerbatim(statement->semicolonToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ExpressionStatement *statement) +{ + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::IfStatement *statement) +{ + addMarkedUpToken(statement->ifToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->ok, this); + if (statement->ko) { + addMarkedUpToken(statement->elseToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->ko, this); + } + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::DoWhileStatement *statement) +{ + addMarkedUpToken(statement->doToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->statement, this); + addMarkedUpToken(statement->whileToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::WhileStatement *statement) +{ + addMarkedUpToken(statement->whileToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ForStatement *statement) +{ + addMarkedUpToken(statement->forToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->initialiser, this); + addVerbatim(statement->firstSemicolonToken); + QQmlJS::AST::Node::accept(statement->condition, this); + addVerbatim(statement->secondSemicolonToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::LocalForStatement *statement) +{ + addMarkedUpToken(statement->forToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + addMarkedUpToken(statement->varToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->declarations, this); + addVerbatim(statement->firstSemicolonToken); + QQmlJS::AST::Node::accept(statement->condition, this); + addVerbatim(statement->secondSemicolonToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ForEachStatement *statement) +{ + addMarkedUpToken(statement->forToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->initialiser, this); + addVerbatim(statement->inToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::LocalForEachStatement *statement) +{ + addMarkedUpToken(statement->forToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + addMarkedUpToken(statement->varToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->declaration, this); + addVerbatim(statement->inToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ContinueStatement *statement) +{ + addMarkedUpToken(statement->continueToken, QLatin1String("keyword")); + addMarkedUpToken(statement->identifierToken, QLatin1String("name")); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::BreakStatement *statement) +{ + addMarkedUpToken(statement->breakToken, QLatin1String("keyword")); + addMarkedUpToken(statement->identifierToken, QLatin1String("name")); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ReturnStatement *statement) +{ + addMarkedUpToken(statement->returnToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::WithStatement *statement) +{ + addMarkedUpToken(statement->withToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::CaseBlock *block) +{ + addVerbatim(block->lbraceToken); + return true; +} + +void QmlMarkupVisitor::endVisit(QQmlJS::AST::CaseBlock *block) +{ + addVerbatim(block->rbraceToken, block->rbraceToken); +} + + +bool QmlMarkupVisitor::visit(QQmlJS::AST::SwitchStatement *statement) +{ + addMarkedUpToken(statement->switchToken, QLatin1String("keyword")); + addVerbatim(statement->lparenToken); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->rparenToken); + QQmlJS::AST::Node::accept(statement->block, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::CaseClause *clause) +{ + addMarkedUpToken(clause->caseToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(clause->expression, this); + addVerbatim(clause->colonToken); + QQmlJS::AST::Node::accept(clause->statements, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::DefaultClause *clause) +{ + addMarkedUpToken(clause->defaultToken, QLatin1String("keyword")); + addVerbatim(clause->colonToken, clause->colonToken); + return true; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::LabelledStatement *statement) +{ + addMarkedUpToken(statement->identifierToken, QLatin1String("name")); + addVerbatim(statement->colonToken); + QQmlJS::AST::Node::accept(statement->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::ThrowStatement *statement) +{ + addMarkedUpToken(statement->throwToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->expression, this); + addVerbatim(statement->semicolonToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::Catch *c) +{ + addMarkedUpToken(c->catchToken, QLatin1String("keyword")); + addVerbatim(c->lparenToken); + addMarkedUpToken(c->identifierToken, QLatin1String("name")); + addVerbatim(c->rparenToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::Finally *f) +{ + addMarkedUpToken(f->finallyToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(f->statement, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::TryStatement *statement) +{ + addMarkedUpToken(statement->tryToken, QLatin1String("keyword")); + QQmlJS::AST::Node::accept(statement->statement, this); + QQmlJS::AST::Node::accept(statement->catchExpression, this); + QQmlJS::AST::Node::accept(statement->finallyExpression, this); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::FunctionExpression *expression) +{ + addMarkedUpToken(expression->functionToken, QLatin1String("keyword")); + addMarkedUpToken(expression->identifierToken, QLatin1String("name")); + addVerbatim(expression->lparenToken); + QQmlJS::AST::Node::accept(expression->formals, this); + addVerbatim(expression->rparenToken); + addVerbatim(expression->lbraceToken); + QQmlJS::AST::Node::accept(expression->body, this); + addVerbatim(expression->rbraceToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::FunctionDeclaration *declaration) +{ + addMarkedUpToken(declaration->functionToken, QLatin1String("keyword")); + addMarkedUpToken(declaration->identifierToken, QLatin1String("name")); + addVerbatim(declaration->lparenToken); + QQmlJS::AST::Node::accept(declaration->formals, this); + addVerbatim(declaration->rparenToken); + addVerbatim(declaration->lbraceToken); + QQmlJS::AST::Node::accept(declaration->body, this); + addVerbatim(declaration->rbraceToken); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::FormalParameterList *list) +{ + addVerbatim(list->commaToken); + addMarkedUpToken(list->identifierToken, QLatin1String("name")); + return false; +} + +bool QmlMarkupVisitor::visit(QQmlJS::AST::DebuggerStatement *statement) +{ + addVerbatim(statement->debuggerToken); + addVerbatim(statement->semicolonToken); + return true; +} + +// Elements and items are represented by UiObjectDefinition nodes. + +bool QmlMarkupVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition) +{ + QHash<QString, QString> attributes; + addMarkedUpToken(definition->qualifiedTypeNameId->identifierToken, QLatin1String("type")); + QQmlJS::AST::Node::accept(definition->initializer, this); + return false; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qmlmarkupvisitor.h b/src/tools/qdoc/qmlmarkupvisitor.h new file mode 100644 index 0000000000..e6b8aee8b8 --- /dev/null +++ b/src/tools/qdoc/qmlmarkupvisitor.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLVISITOR_H +#define QMLVISITOR_H + +#include <QString> +#include "qqmljsastvisitor_p.h" +#include "node.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +class QmlMarkupVisitor : public QQmlJS::AST::Visitor +{ +public: + enum ExtraType{ + Comment, + Pragma + }; + + QmlMarkupVisitor(const QString &code, + const QList<QQmlJS::AST::SourceLocation> &pragmas, + QQmlJS::Engine *engine); + virtual ~QmlMarkupVisitor(); + + QString markedUpCode(); + + virtual bool visit(QQmlJS::AST::UiImport *); + virtual void endVisit(QQmlJS::AST::UiImport *); + + virtual bool visit(QQmlJS::AST::UiPublicMember *); + virtual bool visit(QQmlJS::AST::UiObjectDefinition *); + + virtual bool visit(QQmlJS::AST::UiObjectInitializer *); + virtual void endVisit(QQmlJS::AST::UiObjectInitializer *); + + virtual bool visit(QQmlJS::AST::UiObjectBinding *); + virtual bool visit(QQmlJS::AST::UiScriptBinding *); + virtual bool visit(QQmlJS::AST::UiArrayBinding *); + virtual bool visit(QQmlJS::AST::UiArrayMemberList *); + virtual bool visit(QQmlJS::AST::UiQualifiedId *); + + virtual bool visit(QQmlJS::AST::ThisExpression *); + virtual bool visit(QQmlJS::AST::IdentifierExpression *); + virtual bool visit(QQmlJS::AST::NullExpression *); + virtual bool visit(QQmlJS::AST::TrueLiteral *); + virtual bool visit(QQmlJS::AST::FalseLiteral *); + virtual bool visit(QQmlJS::AST::NumericLiteral *); + virtual bool visit(QQmlJS::AST::StringLiteral *); + virtual bool visit(QQmlJS::AST::RegExpLiteral *); + virtual bool visit(QQmlJS::AST::ArrayLiteral *); + + virtual bool visit(QQmlJS::AST::ObjectLiteral *); + virtual void endVisit(QQmlJS::AST::ObjectLiteral *); + + virtual bool visit(QQmlJS::AST::ElementList *); + virtual bool visit(QQmlJS::AST::Elision *); + virtual bool visit(QQmlJS::AST::PropertyNameAndValueList *); + virtual bool visit(QQmlJS::AST::ArrayMemberExpression *); + virtual bool visit(QQmlJS::AST::FieldMemberExpression *); + virtual bool visit(QQmlJS::AST::NewMemberExpression *); + virtual bool visit(QQmlJS::AST::NewExpression *); + virtual bool visit(QQmlJS::AST::ArgumentList *); + virtual bool visit(QQmlJS::AST::PostIncrementExpression *); + virtual bool visit(QQmlJS::AST::PostDecrementExpression *); + virtual bool visit(QQmlJS::AST::DeleteExpression *); + virtual bool visit(QQmlJS::AST::VoidExpression *); + virtual bool visit(QQmlJS::AST::TypeOfExpression *); + virtual bool visit(QQmlJS::AST::PreIncrementExpression *); + virtual bool visit(QQmlJS::AST::PreDecrementExpression *); + virtual bool visit(QQmlJS::AST::UnaryPlusExpression *); + virtual bool visit(QQmlJS::AST::UnaryMinusExpression *); + virtual bool visit(QQmlJS::AST::TildeExpression *); + virtual bool visit(QQmlJS::AST::NotExpression *); + virtual bool visit(QQmlJS::AST::BinaryExpression *); + virtual bool visit(QQmlJS::AST::ConditionalExpression *); + virtual bool visit(QQmlJS::AST::Expression *); + + virtual bool visit(QQmlJS::AST::Block *); + virtual void endVisit(QQmlJS::AST::Block *); + + virtual bool visit(QQmlJS::AST::VariableStatement *); + virtual bool visit(QQmlJS::AST::VariableDeclarationList *); + virtual bool visit(QQmlJS::AST::VariableDeclaration *); + virtual bool visit(QQmlJS::AST::EmptyStatement *); + virtual bool visit(QQmlJS::AST::ExpressionStatement *); + virtual bool visit(QQmlJS::AST::IfStatement *); + virtual bool visit(QQmlJS::AST::DoWhileStatement *); + virtual bool visit(QQmlJS::AST::WhileStatement *); + virtual bool visit(QQmlJS::AST::ForStatement *); + virtual bool visit(QQmlJS::AST::LocalForStatement *); + virtual bool visit(QQmlJS::AST::ForEachStatement *); + virtual bool visit(QQmlJS::AST::LocalForEachStatement *); + virtual bool visit(QQmlJS::AST::ContinueStatement *); + virtual bool visit(QQmlJS::AST::BreakStatement *); + virtual bool visit(QQmlJS::AST::ReturnStatement *); + virtual bool visit(QQmlJS::AST::WithStatement *); + + virtual bool visit(QQmlJS::AST::CaseBlock *); + virtual void endVisit(QQmlJS::AST::CaseBlock *); + + virtual bool visit(QQmlJS::AST::SwitchStatement *); + virtual bool visit(QQmlJS::AST::CaseClause *); + virtual bool visit(QQmlJS::AST::DefaultClause *); + virtual bool visit(QQmlJS::AST::LabelledStatement *); + virtual bool visit(QQmlJS::AST::ThrowStatement *); + virtual bool visit(QQmlJS::AST::TryStatement *); + virtual bool visit(QQmlJS::AST::Catch *); + virtual bool visit(QQmlJS::AST::Finally *); + virtual bool visit(QQmlJS::AST::FunctionDeclaration *); + virtual bool visit(QQmlJS::AST::FunctionExpression *); + virtual bool visit(QQmlJS::AST::FormalParameterList *); + virtual bool visit(QQmlJS::AST::DebuggerStatement *); + +protected: + QString protect(const QString &string); + +private: + typedef QHash<QString, QString> StringHash; + void addExtra(quint32 start, quint32 finish); + void addMarkedUpToken(QQmlJS::AST::SourceLocation &location, + const QString &text, + const StringHash &attributes = StringHash()); + void addVerbatim(QQmlJS::AST::SourceLocation first, + QQmlJS::AST::SourceLocation last = QQmlJS::AST::SourceLocation()); + QString sourceText(QQmlJS::AST::SourceLocation &location); + + QQmlJS::Engine *engine; + QList<ExtraType> extraTypes; + QList<QQmlJS::AST::SourceLocation> extraLocations; + QString source; + QString output; + quint32 cursor; + int extraIndex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlparser/qmlparser.pri b/src/tools/qdoc/qmlparser/qmlparser.pri new file mode 100644 index 0000000000..6be85ba85a --- /dev/null +++ b/src/tools/qdoc/qmlparser/qmlparser.pri @@ -0,0 +1,19 @@ +HEADERS += \ + $$PWD/qqmljsast_p.h \ + $$PWD/qqmljsastfwd_p.h \ + $$PWD/qqmljsastvisitor_p.h \ + $$PWD/qqmljsengine_p.h \ + $$PWD/qqmljsgrammar_p.h \ + $$PWD/qqmljslexer_p.h \ + $$PWD/qqmljsmemorypool_p.h \ + $$PWD/qqmljsparser_p.h \ + $$PWD/qqmljsglobal_p.h \ + $$PWD/qqmljskeywords_p.h + +SOURCES += \ + $$PWD/qqmljsast.cpp \ + $$PWD/qqmljsastvisitor.cpp \ + $$PWD/qqmljsengine_p.cpp \ + $$PWD/qqmljsgrammar.cpp \ + $$PWD/qqmljslexer.cpp \ + $$PWD/qqmljsparser.cpp diff --git a/src/tools/qdoc/qmlparser/qqmljs.g b/src/tools/qdoc/qmlparser/qqmljs.g new file mode 100644 index 0000000000..746fcb24df --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljs.g @@ -0,0 +1,3016 @@ +---------------------------------------------------------------------------- +-- +-- Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +-- Contact: http://www.qt-project.org/ +-- +-- This file is part of the QtQml module of the Qt Toolkit. +-- +-- $QT_BEGIN_LICENSE:LGPL-ONLY$ +-- 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. +-- +-- If you have questions regarding the use of this file, please contact +-- us via http://www.qt-project.org/. +-- +-- $QT_END_LICENSE$ +-- +---------------------------------------------------------------------------- + +%parser QQmlJSGrammar +%decl qqmljsparser_p.h +%impl qdeclarativejsparser.cpp +%expect 2 +%expect-rr 2 + +%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" +%token T_BREAK "break" T_CASE "case" T_CATCH "catch" +%token T_COLON ":" T_COMMA "," T_CONTINUE "continue" +%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/" +%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "." +%token T_ELSE "else" T_EQ "=" T_EQ_EQ "==" +%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for" +%token T_FUNCTION "function" T_GE ">=" T_GT ">" +%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>" +%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if" +%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{" +%token T_LBRACKET "[" T_LE "<=" T_LPAREN "(" +%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<=" +%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--" +%token T_NEW "new" T_NOT "!" T_NOT_EQ "!=" +%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|" +%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+" +%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?" +%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%" +%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")" +%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*" +%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal" +%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly" +%token T_SWITCH "switch" T_THIS "this" T_THROW "throw" +%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof" +%token T_VAR "var" T_VOID "void" T_WHILE "while" +%token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" +%token T_NULL "null" T_TRUE "true" T_FALSE "false" +%token T_CONST "const" +%token T_DEBUGGER "debugger" +%token T_RESERVED_WORD "reserved word" +%token T_MULTILINE_STRING_LITERAL "multiline string literal" +%token T_COMMENT "comment" + +--- context keywords. +%token T_PUBLIC "public" +%token T_IMPORT "import" +%token T_AS "as" +%token T_ON "on" + +%token T_ERROR + +--- feed tokens +%token T_FEED_UI_PROGRAM +%token T_FEED_UI_OBJECT_MEMBER +%token T_FEED_JS_STATEMENT +%token T_FEED_JS_EXPRESSION +%token T_FEED_JS_SOURCE_ELEMENT +%token T_FEED_JS_PROGRAM + +%nonassoc SHIFT_THERE +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc REDUCE_HERE + +%start TopLevel + +/./**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 <QtCore/QtDebug> +#include <QtCore/QCoreApplication> + +#include <string.h> + +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsast_p.h" +#include "qqmljsmemorypool_p.h" + +./ + +/:/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QDECLARATIVEJSPARSER_P_H +#define QDECLARATIVEJSPARSER_P_H + +#include "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include "qqmljsast_p.h" +#include "qqmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Parser: protected $table +{ +public: + union Value { + int ival; + double dval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline QStringRef &stringRef(int index) + { return string_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + MemoryPool *pool; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + QStringRef *string_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + QStringRef spell; + }; + + double yylval; + QStringRef yytokenspell; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QQmlJS + + +:/ + + +/. + +#include "qqmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QQmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef))); +} + +Parser::Parser(Engine *engine): + driver(engine), + pool(engine->pool()), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + string_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + free(sym_stack); + free(state_stack); + free(location_stack); + free(string_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->tokenStartLine(); + loc.startColumn = lexer->tokenStartColumn(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<QStringRef, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->tokenValue(); + yytokenspell = lexer->tokenSpell(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yytokenspell = first_token->spell; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + stringRef(1) = yytokenspell; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { +./ + +-------------------------------------------------------------------------------------------------------- +-- Declarative UI +-------------------------------------------------------------------------------------------------------- + +TopLevel: T_FEED_UI_PROGRAM UiProgram ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_STATEMENT Statement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_EXPRESSION Expression ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +TopLevel: T_FEED_JS_PROGRAM Program ; +/. +case $rule_number: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; +./ + +UiProgram: UiImportListOpt UiRootMember ; +/. +case $rule_number: { + sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; +./ + +UiImportListOpt: Empty ; +UiImportListOpt: UiImportList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; +./ + +UiImportList: UiImport ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport); +} break; +./ + +UiImportList: UiImportList UiImport ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport); +} break; +./ + +ImportId: MemberExpression ; + +UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->semicolonToken = loc(2); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; +./ + +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = stringRef(4); + sym(1).UiImport->semicolonToken = loc(5); +} break; +./ + +UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = stringRef(3); + sym(1).UiImport->semicolonToken = loc(4); +} break; +./ + + +UiImportHead: T_IMPORT ImportId ; +/. +case $rule_number: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = new (pool) AST::UiImport(importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = new (pool) AST::UiImport(qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; +./ + +Empty: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiRootMember: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMember ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; +./ + +UiObjectMemberList: UiObjectMemberList UiObjectMember ; +/. +case $rule_number: { + AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; +./ + +UiArrayMemberList: UiObjectDefinition ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); +} break; +./ + +UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ; +/. +case $rule_number: { + AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ; +/. +case $rule_number: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectDefinition: UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiObjectDefinition ; + +UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; +./ + +UiScriptStatement: Block ; +UiScriptStatement: EmptyStatement ; +UiScriptStatement: ExpressionStatement ; +UiScriptStatement: IfStatement ; +UiScriptStatement: WithStatement ; +UiScriptStatement: SwitchStatement ; +UiScriptStatement: TryStatement ; + +UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ; +/. +case $rule_number: +{ + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiPropertyType: T_VAR ; +UiPropertyType: T_RESERVED_WORD ; +UiPropertyType: T_IDENTIFIER ; + +UiParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +UiParameterListOpt: UiParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; +./ + +UiParameterList: UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2)); + node->propertyTypeToken = loc(1); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; +./ + +UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ; +/. +case $rule_number: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4)); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ; +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3), + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ; +/. +case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; +./ + +UiObjectMember: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; +./ + +UiObjectMember: VariableStatement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; +./ + +JsIdentifier: T_IDENTIFIER; + +JsIdentifier: T_PROPERTY ; +JsIdentifier: T_SIGNAL ; +JsIdentifier: T_READONLY ; +JsIdentifier: T_ON ; + +-------------------------------------------------------------------------------------------------------- +-- Expressions +-------------------------------------------------------------------------------------------------------- + +PrimaryExpression: T_THIS ; +/. +case $rule_number: { + AST::ThisExpression *node = new (pool) AST::ThisExpression(); + node->thisToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: JsIdentifier ; +/. +case $rule_number: { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NULL ; +/. +case $rule_number: { + AST::NullExpression *node = new (pool) AST::NullExpression(); + node->nullToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_TRUE ; +/. +case $rule_number: { + AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); + node->trueToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_FALSE ; +/. +case $rule_number: { + AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); + node->falseToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_MULTILINE_STRING_LITERAL ; +/.case $rule_number:./ + +PrimaryExpression: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_DIVIDE_EQ ; +/: +#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number +:/ +/. +case $rule_number: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; +./ + +-- PrimaryExpression: T_LBRACE T_RBRACE ; +-- /. +-- case $rule_number: { +-- sym(1).Node = new (pool) AST::ObjectLiteral(); +-- } break; +-- ./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + else + node = new (pool) AST::ObjectLiteral(); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +/. +case $rule_number: { + AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; +./ + +PrimaryExpression: T_LPAREN Expression T_RPAREN ; +/. +case $rule_number: { + AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; +./ + +UiQualifiedId: MemberExpression ; +/. +case $rule_number: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; +./ + +ElementList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); +} break; +./ + +ElementList: Elision AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); +} break; +./ + +ElementList: ElementList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ElementList: ElementList T_COMMA Elision AssignmentExpression ; +/. +case $rule_number: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +Elision: T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(); + node->commaToken = loc(1); + sym(1).Node = node; +} break; +./ + +Elision: Elision T_COMMA ; +/. +case $rule_number: { + AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +PropertyName: T_IDENTIFIER %prec SHIFT_THERE ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_SIGNAL ; +/.case $rule_number:./ + +PropertyName: T_PROPERTY ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_STRING_LITERAL ; +/. +case $rule_number: { + AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: T_NUMERIC_LITERAL ; +/. +case $rule_number: { + AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +PropertyName: ReservedIdentifier ; +/. +case $rule_number: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +./ + +ReservedIdentifier: T_BREAK ; +ReservedIdentifier: T_CASE ; +ReservedIdentifier: T_CATCH ; +ReservedIdentifier: T_CONTINUE ; +ReservedIdentifier: T_DEFAULT ; +ReservedIdentifier: T_DELETE ; +ReservedIdentifier: T_DO ; +ReservedIdentifier: T_ELSE ; +ReservedIdentifier: T_FALSE ; +ReservedIdentifier: T_FINALLY ; +ReservedIdentifier: T_FOR ; +ReservedIdentifier: T_FUNCTION ; +ReservedIdentifier: T_IF ; +ReservedIdentifier: T_IN ; +ReservedIdentifier: T_INSTANCEOF ; +ReservedIdentifier: T_NEW ; +ReservedIdentifier: T_NULL ; +ReservedIdentifier: T_RETURN ; +ReservedIdentifier: T_SWITCH ; +ReservedIdentifier: T_THIS ; +ReservedIdentifier: T_THROW ; +ReservedIdentifier: T_TRUE ; +ReservedIdentifier: T_TRY ; +ReservedIdentifier: T_TYPEOF ; +ReservedIdentifier: T_VAR ; +ReservedIdentifier: T_VOID ; +ReservedIdentifier: T_WHILE ; +ReservedIdentifier: T_CONST ; +ReservedIdentifier: T_DEBUGGER ; +ReservedIdentifier: T_RESERVED_WORD ; +ReservedIdentifier: T_WITH ; + +PropertyIdentifier: JsIdentifier ; +PropertyIdentifier: ReservedIdentifier ; + +MemberExpression: PrimaryExpression ; +MemberExpression: FunctionExpression ; + +MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +MemberExpression: MemberExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; +./ + +NewExpression: MemberExpression ; + +NewExpression: T_NEW NewExpression ; +/. +case $rule_number: { + AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; +./ + +CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ; +/. +case $rule_number: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ; +/. +case $rule_number: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; +./ + +CallExpression: CallExpression T_DOT PropertyIdentifier ; +/. +case $rule_number: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +ArgumentListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ArgumentListOpt: ArgumentList ; +/. +case $rule_number: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; +./ + +ArgumentList: AssignmentExpression ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); +} break; +./ + +ArgumentList: ArgumentList T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +LeftHandSideExpression: NewExpression ; +LeftHandSideExpression: CallExpression ; +PostfixExpression: LeftHandSideExpression ; + +PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ; +/. +case $rule_number: { + AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ; +/. +case $rule_number: { + AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; +./ + +UnaryExpression: PostfixExpression ; + +UnaryExpression: T_DELETE UnaryExpression ; +/. +case $rule_number: { + AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_VOID UnaryExpression ; +/. +case $rule_number: { + AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TYPEOF UnaryExpression ; +/. +case $rule_number: { + AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_PLUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_MINUS UnaryExpression ; +/. +case $rule_number: { + AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_TILDE UnaryExpression ; +/. +case $rule_number: { + AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; +./ + +UnaryExpression: T_NOT UnaryExpression ; +/. +case $rule_number: { + AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: UnaryExpression ; + +MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: MultiplicativeExpression ; + +AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: AdditiveExpression ; + +ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: ShiftExpression ; + +RelationalExpression: RelationalExpression T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpression: RelationalExpression T_IN ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: ShiftExpression ; + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: RelationalExpression ; + +EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: RelationalExpressionNotIn ; + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpression: EqualityExpression ; + +BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseANDExpressionNotIn: EqualityExpressionNotIn ; + +BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpression: BitwiseANDExpression ; + +BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ; + +BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpression: BitwiseXORExpression ; + +BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ; + +BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpression: BitwiseORExpression ; + +LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ; + +LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpression: LogicalANDExpression ; + +LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +LogicalORExpressionNotIn: LogicalANDExpressionNotIn ; + +LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +ConditionalExpression: LogicalORExpression ; + +ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ; +/. +case $rule_number: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +ConditionalExpressionNotIn: LogicalORExpressionNotIn ; + +ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; +./ + +AssignmentExpression: ConditionalExpression ; + +AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentExpressionNotIn: ConditionalExpressionNotIn ; + +AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; +./ + +AssignmentOperator: T_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::Assign; +} break; +./ + +AssignmentOperator: T_STAR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMul; +} break; +./ + +AssignmentOperator: T_DIVIDE_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceDiv; +} break; +./ + +AssignmentOperator: T_REMAINDER_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceMod; +} break; +./ + +AssignmentOperator: T_PLUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAdd; +} break; +./ + +AssignmentOperator: T_MINUS_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceSub; +} break; +./ + +AssignmentOperator: T_LT_LT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; +./ + +AssignmentOperator: T_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; +./ + +AssignmentOperator: T_GT_GT_GT_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; +./ + +AssignmentOperator: T_AND_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceAnd; +} break; +./ + +AssignmentOperator: T_XOR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceXor; +} break; +./ + +AssignmentOperator: T_OR_EQ ; +/. +case $rule_number: { + sym(1).ival = QSOperator::InplaceOr; +} break; +./ + +Expression: AssignmentExpression ; + +Expression: Expression T_COMMA AssignmentExpression ; +/. +case $rule_number: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionOpt: Expression ; + +ExpressionNotIn: AssignmentExpressionNotIn ; + +ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ; +/. +case $rule_number: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +ExpressionNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +ExpressionNotInOpt: ExpressionNotIn ; + +Statement: Block ; +Statement: VariableStatement ; +Statement: EmptyStatement ; +Statement: ExpressionStatement ; +Statement: IfStatement ; +Statement: IterationStatement ; +Statement: ContinueStatement ; +Statement: BreakStatement ; +Statement: ReturnStatement ; +Statement: WithStatement ; +Statement: LabelledStatement ; +Statement: SwitchStatement ; +Statement: ThrowStatement ; +Statement: TryStatement ; +Statement: DebuggerStatement ; + + +Block: T_LBRACE StatementListOpt T_RBRACE ; +/. +case $rule_number: { + AST::Block *node = new (pool) AST::Block(sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +StatementList: Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); +} break; +./ + +StatementList: StatementList Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); +} break; +./ + +StatementListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +StatementListOpt: StatementList ; +/. +case $rule_number: { + sym(1).Node = sym(1).StatementList->finish (); +} break; +./ + +VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; +/. +case $rule_number: { + AST::VariableStatement *node = new (pool) AST::VariableStatement( + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +VariableDeclarationKind: T_CONST ; +/. +case $rule_number: { + sym(1).ival = T_CONST; +} break; +./ + +VariableDeclarationKind: T_VAR ; +/. +case $rule_number: { + sym(1).ival = T_VAR; +} break; +./ + +VariableDeclarationList: VariableDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ; +/. +case $rule_number: { + AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; +./ + +VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; +./ + +VariableDeclaration: JsIdentifier InitialiserOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; +/. +case $rule_number: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +Initialiser: T_EQ AssignmentExpression ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserOpt: Initialiser ; + +InitialiserNotIn: T_EQ AssignmentExpressionNotIn ; +/. +case $rule_number: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; +./ + +InitialiserNotInOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +InitialiserNotInOpt: InitialiserNotIn ; + +EmptyStatement: T_SEMICOLON ; +/. +case $rule_number: { + AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; +./ + +ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ExpressionStatement: Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ; +/. +case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(6); + sym(1).Node = node; +} break; +./ + +IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + + +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ; +/. +case $rule_number: { + AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForStatement *node = new (pool) AST::LocalForStatement( + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; +./ + +IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ; +/. +case $rule_number: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ; +/. +case $rule_number: { + AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ; +/. +case $rule_number: { + AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ; +/. +case $rule_number: { + AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; +./ + +CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ; +/. +case $rule_number: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; +./ + +CaseClauses: CaseClause ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); +} break; +./ + +CaseClauses: CaseClauses CaseClause ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); +} break; +./ + +CaseClausesOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +CaseClausesOpt: CaseClauses ; +/. +case $rule_number: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; +./ + +CaseClause: T_CASE Expression T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; +./ + +DefaultClause: T_DEFAULT T_COLON StatementListOpt ; +/. +case $rule_number: { + AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_SIGNAL T_COLON Statement ; +/.case $rule_number:./ + +LabelledStatement: T_PROPERTY T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +LabelledStatement: T_IDENTIFIER T_COLON Statement ; +/. +case $rule_number: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +./ + +ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +ThrowStatement: T_THROW Expression T_SEMICOLON ; +/. +case $rule_number: { + AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Finally ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +TryStatement: T_TRY Block Catch Finally ; +/. +case $rule_number: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; +./ + +Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ; +/. +case $rule_number: { + AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; +./ + +Finally: T_FINALLY Block ; +/. +case $rule_number: { + AST::Finally *node = new (pool) AST::Finally(sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; +./ + +DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon +DebuggerStatement: T_DEBUGGER T_SEMICOLON ; +/. +case $rule_number: { + AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; +./ + +FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (! stringRef(2).isNull()) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; +./ + +FormalParameterList: JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; +./ + +FormalParameterList: FormalParameterList T_COMMA JsIdentifier ; +/. +case $rule_number: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; +./ + +FormalParameterListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FormalParameterListOpt: FormalParameterList ; +/. +case $rule_number: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; +./ + +FunctionBodyOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +FunctionBodyOpt: FunctionBody ; + +FunctionBody: SourceElements ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); +} break; +./ + +Program: Empty ; + +Program: SourceElements ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); +} break; +./ + +SourceElements: SourceElement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); +} break; +./ + +SourceElements: SourceElements SourceElement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); +} break; +./ + +SourceElement: Statement ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); +} break; +./ + +SourceElement: FunctionDeclaration ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); +} break; +./ + +IdentifierOpt: ; +/. +case $rule_number: { + stringRef(1) = QStringRef(); +} break; +./ + +IdentifierOpt: JsIdentifier ; + +PropertyNameAndValueListOpt: ; +/. +case $rule_number: { + sym(1).Node = 0; +} break; +./ + +PropertyNameAndValueListOpt: PropertyNameAndValueList ; + +/. + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.spell = yytokenspell; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = qApp->translate("QQmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].spell = yytokenspell; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->tokenValue(); + token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = qApp->translate("QQmlParser", "Syntax error"); + else + msg = qApp->translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = qApp->translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = qApp->translate("QQmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + +./ +/: +QT_QML_END_NAMESPACE + + + +#endif // QDECLARATIVEJSPARSER_P_H +:/ diff --git a/src/tools/qdoc/qmlparser/qqmljsast.cpp b/src/tools/qdoc/qmlparser/qqmljsast.cpp new file mode 100644 index 0000000000..d0b984fc9e --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsast.cpp @@ -0,0 +1,931 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 "qqmljsast_p.h" + +#include "qqmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +void Node::accept(Visitor *visitor) +{ + if (visitor->preVisit(this)) { + accept0(visitor); + } + visitor->postVisit(this); +} + +void Node::accept(Node *node, Visitor *visitor) +{ + if (node) + node->accept(visitor); +} + +ExpressionNode *Node::expressionCast() +{ + return 0; +} + +BinaryExpression *Node::binaryExpressionCast() +{ + return 0; +} + +Statement *Node::statementCast() +{ + return 0; +} + +UiObjectMember *Node::uiObjectMemberCast() +{ + return 0; +} + +ExpressionNode *ExpressionNode::expressionCast() +{ + return this; +} + +BinaryExpression *BinaryExpression::binaryExpressionCast() +{ + return this; +} + +Statement *Statement::statementCast() +{ + return this; +} + +UiObjectMember *UiObjectMember::uiObjectMemberCast() +{ + return this; +} + +void NestedExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + visitor->endVisit(this); +} + +void ThisExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void IdentifierExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NullExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void TrueLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void FalseLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void RegExpLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + accept(elision, visitor); + } + + visitor->endVisit(this); +} + +void ObjectLiteral::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(properties, visitor); + } + + visitor->endVisit(this); +} + +void ElementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ElementList *it = this; it; it = it->next) { + accept(it->elision, visitor); + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void Elision::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void PropertyNameAndValueList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (PropertyNameAndValueList *it = this; it; it = it->next) { + accept(it->name, visitor); + accept(it->value, visitor); + } + } + + visitor->endVisit(this); +} + +void IdentifierPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void StringLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void NumericLiteralPropertyName::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ArrayMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void FieldMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void NewMemberExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void NewExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void CallExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + accept(arguments, visitor); + } + + visitor->endVisit(this); +} + +void ArgumentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (ArgumentList *it = this; it; it = it->next) { + accept(it->expression, visitor); + } + } + + visitor->endVisit(this); +} + +void PostIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void PostDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(base, visitor); + } + + visitor->endVisit(this); +} + +void DeleteExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void VoidExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TypeOfExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreIncrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void PreDecrementExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryPlusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void UnaryMinusExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TildeExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void NotExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void BinaryExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void ConditionalExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void Expression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(left, visitor); + accept(right, visitor); + } + + visitor->endVisit(this); +} + +void Block::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void StatementList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (StatementList *it = this; it; it = it->next) { + accept(it->statement, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + } + + visitor->endVisit(this); +} + +void VariableDeclarationList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (VariableDeclarationList *it = this; it; it = it->next) { + accept(it->declaration, visitor); + } + } + + visitor->endVisit(this); +} + +void VariableDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void EmptyStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ExpressionStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void IfStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(ok, visitor); + accept(ko, visitor); + } + + visitor->endVisit(this); +} + +void DoWhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WhileStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declarations, visitor); + accept(condition, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(initialiser, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void LocalForEachStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ContinueStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void BreakStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void ReturnStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void WithStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void SwitchStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(block, visitor); + } + + visitor->endVisit(this); +} + +void CaseBlock::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(clauses, visitor); + accept(defaultClause, visitor); + accept(moreClauses, visitor); + } + + visitor->endVisit(this); +} + +void CaseClauses::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (CaseClauses *it = this; it; it = it->next) { + accept(it->clause, visitor); + } + } + + visitor->endVisit(this); +} + +void CaseClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void DefaultClause::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statements, visitor); + } + + visitor->endVisit(this); +} + +void LabelledStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void ThrowStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(expression, visitor); + } + + visitor->endVisit(this); +} + +void TryStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(catchExpression, visitor); + accept(finallyExpression, visitor); + } + + visitor->endVisit(this); +} + +void Catch::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void Finally::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void FunctionDeclaration::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FunctionExpression::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(formals, visitor); + accept(body, visitor); + } + + visitor->endVisit(this); +} + +void FormalParameterList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + // ### + } + + visitor->endVisit(this); +} + +void FunctionBody::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void Program::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(elements, visitor); + } + + visitor->endVisit(this); +} + +void SourceElements::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (SourceElements *it = this; it; it = it->next) { + accept(it->element, visitor); + } + } + + visitor->endVisit(this); +} + +void FunctionSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(declaration, visitor); + } + + visitor->endVisit(this); +} + +void StatementSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void DebuggerStatement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiProgram::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(imports, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiPublicMember::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(statement, visitor); + accept(binding, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectDefinition::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectInitializer::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(qualifiedTypeNameId, visitor); + accept(initializer, visitor); + } + + visitor->endVisit(this); +} + +void UiScriptBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(statement, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayBinding::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(qualifiedId, visitor); + accept(members, visitor); + } + + visitor->endVisit(this); +} + +void UiObjectMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiObjectMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiArrayMemberList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (UiArrayMemberList *it = this; it; it = it->next) + accept(it->member, visitor); + } + + visitor->endVisit(this); +} + +void UiQualifiedId::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + } + + visitor->endVisit(this); +} + +void UiImport::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(importUri, visitor); + } + + visitor->endVisit(this); +} + +void UiImportList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(import, visitor); + accept(next, visitor); + } + + visitor->endVisit(this); +} + +void UiSourceElement::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(sourceElement, visitor); + } + + visitor->endVisit(this); +} + +} } // namespace QQmlJS::AST + +QT_QML_END_NAMESPACE + + diff --git a/src/tools/qdoc/qmlparser/qqmljsast_p.h b/src/tools/qdoc/qmlparser/qqmljsast_p.h new file mode 100644 index 0000000000..f85eb4ca5f --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsast_p.h @@ -0,0 +1,2640 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSAST_P_H +#define QQMLJSAST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmljsastvisitor_p.h" +#include "qqmljsglobal_p.h" +#include "qqmljsmemorypool_p.h" + +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +#define QQMLJS_DECLARE_AST_NODE(name) \ + enum { K = Kind_##name }; + +namespace QSOperator // ### rename +{ + +enum Op { + Add, + And, + InplaceAnd, + Assign, + BitAnd, + BitOr, + BitXor, + InplaceSub, + Div, + InplaceDiv, + Equal, + Ge, + Gt, + In, + InplaceAdd, + InstanceOf, + Le, + LShift, + InplaceLeftShift, + Lt, + Mod, + InplaceMod, + Mul, + InplaceMul, + NotEqual, + Or, + InplaceOr, + RShift, + InplaceRightShift, + StrictEqual, + StrictNotEqual, + Sub, + URShift, + InplaceURightShift, + InplaceXor +}; + +} // namespace QSOperator + +namespace QQmlJS { + +namespace AST { + +template <typename _T1, typename _T2> +_T1 cast(_T2 *ast) +{ + if (ast && ast->kind == static_cast<_T1>(0)->K) + return static_cast<_T1>(ast); + + return 0; +} + +class QML_PARSER_EXPORT Node: public Managed +{ +public: + enum Kind { + Kind_Undefined, + + Kind_ArgumentList, + Kind_ArrayLiteral, + Kind_ArrayMemberExpression, + Kind_BinaryExpression, + Kind_Block, + Kind_BreakStatement, + Kind_CallExpression, + Kind_CaseBlock, + Kind_CaseClause, + Kind_CaseClauses, + Kind_Catch, + Kind_ConditionalExpression, + Kind_ContinueStatement, + Kind_DebuggerStatement, + Kind_DefaultClause, + Kind_DeleteExpression, + Kind_DoWhileStatement, + Kind_ElementList, + Kind_Elision, + Kind_EmptyStatement, + Kind_Expression, + Kind_ExpressionStatement, + Kind_FalseLiteral, + Kind_FieldMemberExpression, + Kind_Finally, + Kind_ForEachStatement, + Kind_ForStatement, + Kind_FormalParameterList, + Kind_FunctionBody, + Kind_FunctionDeclaration, + Kind_FunctionExpression, + Kind_FunctionSourceElement, + Kind_IdentifierExpression, + Kind_IdentifierPropertyName, + Kind_IfStatement, + Kind_LabelledStatement, + Kind_LocalForEachStatement, + Kind_LocalForStatement, + Kind_NewExpression, + Kind_NewMemberExpression, + Kind_NotExpression, + Kind_NullExpression, + Kind_NumericLiteral, + Kind_NumericLiteralPropertyName, + Kind_ObjectLiteral, + Kind_PostDecrementExpression, + Kind_PostIncrementExpression, + Kind_PreDecrementExpression, + Kind_PreIncrementExpression, + Kind_Program, + Kind_PropertyName, + Kind_PropertyNameAndValueList, + Kind_RegExpLiteral, + Kind_ReturnStatement, + Kind_SourceElement, + Kind_SourceElements, + Kind_StatementList, + Kind_StatementSourceElement, + Kind_StringLiteral, + Kind_StringLiteralPropertyName, + Kind_SwitchStatement, + Kind_ThisExpression, + Kind_ThrowStatement, + Kind_TildeExpression, + Kind_TrueLiteral, + Kind_TryStatement, + Kind_TypeOfExpression, + Kind_UnaryMinusExpression, + Kind_UnaryPlusExpression, + Kind_VariableDeclaration, + Kind_VariableDeclarationList, + Kind_VariableStatement, + Kind_VoidExpression, + Kind_WhileStatement, + Kind_WithStatement, + Kind_NestedExpression, + + Kind_UiArrayBinding, + Kind_UiImport, + Kind_UiImportList, + Kind_UiObjectBinding, + Kind_UiObjectDefinition, + Kind_UiObjectInitializer, + Kind_UiObjectMemberList, + Kind_UiArrayMemberList, + Kind_UiProgram, + Kind_UiParameterList, + Kind_UiPublicMember, + Kind_UiQualifiedId, + Kind_UiScriptBinding, + Kind_UiSourceElement + }; + + inline Node() + : kind(Kind_Undefined) {} + + // NOTE: node destructors are never called, + // instead we block free the memory + // (see the NodePool class) + virtual ~Node() {} + + virtual ExpressionNode *expressionCast(); + virtual BinaryExpression *binaryExpressionCast(); + virtual Statement *statementCast(); + virtual UiObjectMember *uiObjectMemberCast(); + + void accept(Visitor *visitor); + static void accept(Node *node, Visitor *visitor); + + inline static void acceptChild(Node *node, Visitor *visitor) + { return accept(node, visitor); } // ### remove + + virtual void accept0(Visitor *visitor) = 0; + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + +// attributes + int kind; +}; + +class QML_PARSER_EXPORT ExpressionNode: public Node +{ +public: + ExpressionNode() {} + + virtual ExpressionNode *expressionCast(); +}; + +class QML_PARSER_EXPORT Statement: public Node +{ +public: + Statement() {} + + virtual Statement *statementCast(); +}; + +class QML_PARSER_EXPORT NestedExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NestedExpression) + + NestedExpression(ExpressionNode *expression) + : expression(expression) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lparenToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *expression; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ThisExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ThisExpression) + + ThisExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return thisToken; } + + virtual SourceLocation lastSourceLocation() const + { return thisToken; } + +// attributes + SourceLocation thisToken; +}; + +class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(IdentifierExpression) + + IdentifierExpression(const QStringRef &n): + name (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + +// attributes + QStringRef name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NullExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NullExpression) + + NullExpression() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return nullToken; } + + virtual SourceLocation lastSourceLocation() const + { return nullToken; } + +// attributes + SourceLocation nullToken; +}; + +class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TrueLiteral) + + TrueLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return trueToken; } + + virtual SourceLocation lastSourceLocation() const + { return trueToken; } + +// attributes + SourceLocation trueToken; +}; + +class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FalseLiteral) + + FalseLiteral() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return falseToken; } + + virtual SourceLocation lastSourceLocation() const + { return falseToken; } + +// attributes + SourceLocation falseToken; +}; + +class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NumericLiteral) + + NumericLiteral(double v): + value(v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + double value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT StringLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(StringLiteral) + + StringLiteral(const QStringRef &v): + value (v) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + QStringRef value; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(RegExpLiteral) + + RegExpLiteral(const QStringRef &p, int f): + pattern (p), flags (f) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return literalToken; } + + virtual SourceLocation lastSourceLocation() const + { return literalToken; } + +// attributes: + QStringRef pattern; + int flags; + SourceLocation literalToken; +}; + +class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ArrayLiteral) + + ArrayLiteral(Elision *e): + elements (0), elision (e) + { kind = K; } + + ArrayLiteral(ElementList *elts): + elements (elts), elision (0) + { kind = K; } + + ArrayLiteral(ElementList *elts, Elision *e): + elements (elts), elision (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbracketToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ElementList *elements; + Elision *elision; + SourceLocation lbracketToken; + SourceLocation commaToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ObjectLiteral) + + ObjectLiteral(): + properties (0) { kind = K; } + + ObjectLiteral(PropertyNameAndValueList *plist): + properties (plist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + PropertyNameAndValueList *properties; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT Elision: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Elision) + + Elision(): + next (this) { kind = K; } + + Elision(Elision *previous) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return commaToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : commaToken; } + + inline Elision *finish () + { + Elision *front = next; + next = 0; + return front; + } + +// attributes + Elision *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT ElementList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(ElementList) + + ElementList(Elision *e, ExpressionNode *expr): + elision (e), expression (expr), next (this) + { kind = K; } + + ElementList(ElementList *previous, Elision *e, ExpressionNode *expr): + elision (e), expression (expr) + { + kind = K; + next = previous->next; + previous->next = this; + } + + inline ElementList *finish () + { + ElementList *front = next; + next = 0; + return front; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (elision) + return elision->firstSourceLocation(); + return expression->firstSourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return expression->lastSourceLocation(); + } + +// attributes + Elision *elision; + ExpressionNode *expression; + ElementList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyName: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyName) + + PropertyName() { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return propertyNameToken; } + + virtual SourceLocation lastSourceLocation() const + { return propertyNameToken; } + +// attributes + SourceLocation propertyNameToken; +}; + +class QML_PARSER_EXPORT PropertyNameAndValueList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyNameAndValueList) + + PropertyNameAndValueList(PropertyName *n, ExpressionNode *v): + name (n), value (v), next (this) + { kind = K; } + + PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v): + name (n), value (v) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return name->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return value->lastSourceLocation(); + } + + inline PropertyNameAndValueList *finish () + { + PropertyNameAndValueList *front = next; + next = 0; + return front; + } + +// attributes + PropertyName *name; + ExpressionNode *value; + PropertyNameAndValueList *next; + SourceLocation colonToken; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(IdentifierPropertyName) + + IdentifierPropertyName(const QStringRef &n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + QStringRef id; +}; + +class QML_PARSER_EXPORT StringLiteralPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(StringLiteralPropertyName) + + StringLiteralPropertyName(const QStringRef &n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + QStringRef id; +}; + +class QML_PARSER_EXPORT NumericLiteralPropertyName: public PropertyName +{ +public: + QQMLJS_DECLARE_AST_NODE(NumericLiteralPropertyName) + + NumericLiteralPropertyName(double n): + id (n) { kind = K; } + + virtual void accept0(Visitor *visitor); + +// attributes + double id; +}; + +class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression) + + ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e): + base (b), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + +// attributes + ExpressionNode *base; + ExpressionNode *expression; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FieldMemberExpression) + + FieldMemberExpression(ExpressionNode *b, const QStringRef &n): + base (b), name (n) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return identifierToken; } + + // attributes + ExpressionNode *base; + QStringRef name; + SourceLocation dotToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NewMemberExpression) + + NewMemberExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + + // attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation newToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT NewExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NewExpression) + + NewExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return newToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation newToken; +}; + +class QML_PARSER_EXPORT CallExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(CallExpression) + + CallExpression(ExpressionNode *b, ArgumentList *a): + base (b), arguments (a) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return rparenToken; } + +// attributes + ExpressionNode *base; + ArgumentList *arguments; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ArgumentList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(ArgumentList) + + ArgumentList(ExpressionNode *e): + expression (e), next (this) + { kind = K; } + + ArgumentList(ArgumentList *previous, ExpressionNode *e): + expression (e) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return expression->lastSourceLocation(); + } + + inline ArgumentList *finish () + { + ArgumentList *front = next; + next = 0; + return front; + } + +// attributes + ExpressionNode *expression; + ArgumentList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PostIncrementExpression) + + PostIncrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return incrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PostDecrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PostDecrementExpression) + + PostDecrementExpression(ExpressionNode *b): + base (b) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return base->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return decrementToken; } + +// attributes + ExpressionNode *base; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT DeleteExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(DeleteExpression) + + DeleteExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return deleteToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation deleteToken; +}; + +class QML_PARSER_EXPORT VoidExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(VoidExpression) + + VoidExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return voidToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation voidToken; +}; + +class QML_PARSER_EXPORT TypeOfExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TypeOfExpression) + + TypeOfExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return typeofToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation typeofToken; +}; + +class QML_PARSER_EXPORT PreIncrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PreIncrementExpression) + + PreIncrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return incrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation incrementToken; +}; + +class QML_PARSER_EXPORT PreDecrementExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(PreDecrementExpression) + + PreDecrementExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return decrementToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation decrementToken; +}; + +class QML_PARSER_EXPORT UnaryPlusExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(UnaryPlusExpression) + + UnaryPlusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return plusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation plusToken; +}; + +class QML_PARSER_EXPORT UnaryMinusExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(UnaryMinusExpression) + + UnaryMinusExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return minusToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation minusToken; +}; + +class QML_PARSER_EXPORT TildeExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(TildeExpression) + + TildeExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tildeToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation tildeToken; +}; + +class QML_PARSER_EXPORT NotExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(NotExpression) + + NotExpression(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return notToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + SourceLocation notToken; +}; + +class QML_PARSER_EXPORT BinaryExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(BinaryExpression) + + BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r): + left (l), op (o), right (r) + { kind = K; } + + virtual BinaryExpression *binaryExpressionCast(); + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + int op; + ExpressionNode *right; + SourceLocation operatorToken; +}; + +class QML_PARSER_EXPORT ConditionalExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(ConditionalExpression) + + ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return ko->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + ExpressionNode *ok; + ExpressionNode *ko; + SourceLocation questionToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT Expression: public ExpressionNode // ### rename +{ +public: + QQMLJS_DECLARE_AST_NODE(Expression) + + Expression(ExpressionNode *l, ExpressionNode *r): + left (l), right (r) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return left->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return right->lastSourceLocation(); } + +// attributes + ExpressionNode *left; + ExpressionNode *right; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT Block: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(Block) + + Block(StatementList *slist): + statements (slist) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + + // attributes + StatementList *statements; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT StatementList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(StatementList) + + StatementList(Statement *stmt): + statement (stmt), next (this) + { kind = K; } + + StatementList(StatementList *previous, Statement *stmt): + statement (stmt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return statement->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : statement->lastSourceLocation(); } + + inline StatementList *finish () + { + StatementList *front = next; + next = 0; + return front; + } + +// attributes + Statement *statement; + StatementList *next; +}; + +class QML_PARSER_EXPORT VariableStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableStatement) + + VariableStatement(VariableDeclarationList *vlist): + declarations (vlist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declarationKindToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + VariableDeclarationList *declarations; + SourceLocation declarationKindToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT VariableDeclaration: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableDeclaration) + + VariableDeclaration(const QStringRef &n, ExpressionNode *e): + name (n), expression (e), readOnly(false) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return expression ? expression->lastSourceLocation() : identifierToken; } + +// attributes + QStringRef name; + ExpressionNode *expression; + bool readOnly; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT VariableDeclarationList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(VariableDeclarationList) + + VariableDeclarationList(VariableDeclaration *decl): + declaration (decl), next (this) + { kind = K; } + + VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl): + declaration (decl) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declaration->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { + if (next) + return next->lastSourceLocation(); + return declaration->lastSourceLocation(); + } + + inline VariableDeclarationList *finish (bool readOnly) + { + VariableDeclarationList *front = next; + next = 0; + if (readOnly) { + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) + vdl->declaration->readOnly = true; + } + return front; + } + +// attributes + VariableDeclaration *declaration; + VariableDeclarationList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT EmptyStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(EmptyStatement) + + EmptyStatement() { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return semicolonToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ExpressionStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ExpressionStatement) + + ExpressionStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return expression->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT IfStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(IfStatement) + + IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0): + expression (e), ok (t), ko (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return ifToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (ko) + return ko->lastSourceLocation(); + + return ok->lastSourceLocation(); + } + +// attributes + ExpressionNode *expression; + Statement *ok; + Statement *ko; + SourceLocation ifToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation elseToken; +}; + +class QML_PARSER_EXPORT DoWhileStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(DoWhileStatement) + + DoWhileStatement(Statement *stmt, ExpressionNode *e): + statement (stmt), expression (e) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return doToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + Statement *statement; + ExpressionNode *expression; + SourceLocation doToken; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WhileStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(WhileStatement) + + WhileStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return whileToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation whileToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ForStatement) + + ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + initialiser (i), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LocalForStatement) + + LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt): + declarations (vlist), condition (c), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclarationList *declarations; + ExpressionNode *condition; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation firstSemicolonToken; + SourceLocation secondSemicolonToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ForEachStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ForEachStatement) + + ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt): + initialiser (i), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *initialiser; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT LocalForEachStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LocalForEachStatement) + + LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt): + declaration (v), expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return forToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + VariableDeclaration *declaration; + ExpressionNode *expression; + Statement *statement; + SourceLocation forToken; + SourceLocation lparenToken; + SourceLocation varToken; + SourceLocation inToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT ContinueStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ContinueStatement) + + ContinueStatement(const QStringRef &l = QStringRef()): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return continueToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + QStringRef label; + SourceLocation continueToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT BreakStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(BreakStatement) + + BreakStatement(const QStringRef &l): + label (l) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return breakToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + QStringRef label; + SourceLocation breakToken; + SourceLocation identifierToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT ReturnStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ReturnStatement) + + ReturnStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return returnToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + ExpressionNode *expression; + SourceLocation returnToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT WithStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(WithStatement) + + WithStatement(ExpressionNode *e, Statement *stmt): + expression (e), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return withToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + ExpressionNode *expression; + Statement *statement; + SourceLocation withToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseBlock: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseBlock) + + CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0): + clauses (c), defaultClause (d), moreClauses (r) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + CaseClauses *clauses; + DefaultClause *defaultClause; + CaseClauses *moreClauses; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT SwitchStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(SwitchStatement) + + SwitchStatement(ExpressionNode *e, CaseBlock *b): + expression (e), block (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return switchToken; } + + virtual SourceLocation lastSourceLocation() const + { return block->rbraceToken; } + +// attributes + ExpressionNode *expression; + CaseBlock *block; + SourceLocation switchToken; + SourceLocation lparenToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT CaseClause: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseClause) + + CaseClause(ExpressionNode *e, StatementList *slist): + expression (e), statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return caseToken; } + + virtual SourceLocation lastSourceLocation() const + { return statements ? statements->lastSourceLocation() : colonToken; } + +// attributes + ExpressionNode *expression; + StatementList *statements; + SourceLocation caseToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT CaseClauses: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(CaseClauses) + + CaseClauses(CaseClause *c): + clause (c), next (this) + { kind = K; } + + CaseClauses(CaseClauses *previous, CaseClause *c): + clause (c) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return clause->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : clause->lastSourceLocation(); } + + inline CaseClauses *finish () + { + CaseClauses *front = next; + next = 0; + return front; + } + +//attributes + CaseClause *clause; + CaseClauses *next; +}; + +class QML_PARSER_EXPORT DefaultClause: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(DefaultClause) + + DefaultClause(StatementList *slist): + statements (slist) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return defaultToken; } + + virtual SourceLocation lastSourceLocation() const + { return statements ? statements->lastSourceLocation() : colonToken; } + +// attributes + StatementList *statements; + SourceLocation defaultToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT LabelledStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(LabelledStatement) + + LabelledStatement(const QStringRef &l, Statement *stmt): + label (l), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + QStringRef label; + Statement *statement; + SourceLocation identifierToken; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT ThrowStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(ThrowStatement) + + ThrowStatement(ExpressionNode *e): + expression (e) { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return throwToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + + // attributes + ExpressionNode *expression; + SourceLocation throwToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT Catch: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Catch) + + Catch(const QStringRef &n, Block *stmt): + name (n), statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return catchToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + QStringRef name; + Block *statement; + SourceLocation catchToken; + SourceLocation lparenToken; + SourceLocation identifierToken; + SourceLocation rparenToken; +}; + +class QML_PARSER_EXPORT Finally: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Finally) + + Finally(Block *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return finallyToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement ? statement->lastSourceLocation() : finallyToken; } + +// attributes + Block *statement; + SourceLocation finallyToken; +}; + +class QML_PARSER_EXPORT TryStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(TryStatement) + + TryStatement(Statement *stmt, Catch *c, Finally *f): + statement (stmt), catchExpression (c), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Finally *f): + statement (stmt), catchExpression (0), finallyExpression (f) + { kind = K; } + + TryStatement(Statement *stmt, Catch *c): + statement (stmt), catchExpression (c), finallyExpression (0) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return tryToken; } + + virtual SourceLocation lastSourceLocation() const + { + if (finallyExpression) + return finallyExpression->statement->rbraceToken; + else if (catchExpression) + return catchExpression->statement->rbraceToken; + + return statement->lastSourceLocation(); + } + +// attributes + Statement *statement; + Catch *catchExpression; + Finally *finallyExpression; + SourceLocation tryToken; +}; + +class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionExpression) + + FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + name (n), formals (f), body (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return functionToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + QStringRef name; + FormalParameterList *formals; + FunctionBody *body; + SourceLocation functionToken; + SourceLocation identifierToken; + SourceLocation lparenToken; + SourceLocation rparenToken; + SourceLocation lbraceToken; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionDeclaration) + + FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b): + FunctionExpression(n, f, b) + { kind = K; } + + virtual void accept0(Visitor *visitor); +}; + +class QML_PARSER_EXPORT FormalParameterList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(FormalParameterList) + + FormalParameterList(const QStringRef &n): + name (n), next (this) + { kind = K; } + + FormalParameterList(FormalParameterList *previous, const QStringRef &n): + name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + + inline FormalParameterList *finish () + { + FormalParameterList *front = next; + next = 0; + return front; + } + +// attributes + QStringRef name; + FormalParameterList *next; + SourceLocation commaToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT SourceElement: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(SourceElement) + + inline SourceElement() + { kind = K; } +}; + +class QML_PARSER_EXPORT SourceElements: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(SourceElements) + + SourceElements(SourceElement *elt): + element (elt), next (this) + { kind = K; } + + SourceElements(SourceElements *previous, SourceElement *elt): + element (elt) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return element->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : element->lastSourceLocation(); } + + inline SourceElements *finish () + { + SourceElements *front = next; + next = 0; + return front; + } + +// attributes + SourceElement *element; + SourceElements *next; +}; + +class QML_PARSER_EXPORT FunctionBody: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionBody) + + FunctionBody(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return elements ? elements->firstSourceLocation() : SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return elements ? elements->lastSourceLocation() : SourceLocation(); } + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT Program: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(Program) + + Program(SourceElements *elts): + elements (elts) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return elements ? elements->firstSourceLocation() : SourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return elements ? elements->lastSourceLocation() : SourceLocation(); } + +// attributes + SourceElements *elements; +}; + +class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement +{ +public: + QQMLJS_DECLARE_AST_NODE(FunctionSourceElement) + + FunctionSourceElement(FunctionDeclaration *f): + declaration (f) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return declaration->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return declaration->lastSourceLocation(); } + +// attributes + FunctionDeclaration *declaration; +}; + +class QML_PARSER_EXPORT StatementSourceElement: public SourceElement +{ +public: + QQMLJS_DECLARE_AST_NODE(StatementSourceElement) + + StatementSourceElement(Statement *stmt): + statement (stmt) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return statement->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + +// attributes + Statement *statement; +}; + +class QML_PARSER_EXPORT DebuggerStatement: public Statement +{ +public: + QQMLJS_DECLARE_AST_NODE(DebuggerStatement) + + DebuggerStatement() + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return debuggerToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + SourceLocation debuggerToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiQualifiedId: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiQualifiedId) + + UiQualifiedId(const QStringRef &name) + : next(this), name(name) + { kind = K; } + + UiQualifiedId(UiQualifiedId *previous, const QStringRef &name) + : name(name) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiQualifiedId *finish() + { + UiQualifiedId *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + +// attributes + UiQualifiedId *next; + QStringRef name; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiImport: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiImport) + + UiImport(const QStringRef &fileName) + : fileName(fileName), importUri(0) + { kind = K; } + + UiImport(UiQualifiedId *uri) + : importUri(uri) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return importToken; } + + virtual SourceLocation lastSourceLocation() const + { return semicolonToken; } + +// attributes + QStringRef fileName; + UiQualifiedId *importUri; + QStringRef importId; + SourceLocation importToken; + SourceLocation fileNameToken; + SourceLocation versionToken; + SourceLocation asToken; + SourceLocation importIdToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiImportList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiImportList) + + UiImportList(UiImport *import) + : import(import), + next(this) + { kind = K; } + + UiImportList(UiImportList *previous, UiImport *import) + : import(import) + { + kind = K; + next = previous->next; + previous->next = this; + } + + UiImportList *finish() + { + UiImportList *head = next; + next = 0; + return head; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return import->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : import->lastSourceLocation(); } + +// attributes + UiImport *import; + UiImportList *next; +}; + +class QML_PARSER_EXPORT UiObjectMember: public Node +{ +public: + virtual SourceLocation firstSourceLocation() const = 0; + virtual SourceLocation lastSourceLocation() const = 0; + + virtual UiObjectMember *uiObjectMemberCast(); +}; + +class QML_PARSER_EXPORT UiObjectMemberList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectMemberList) + + UiObjectMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiObjectMemberList(UiObjectMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return member->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : member->lastSourceLocation(); } + + UiObjectMemberList *finish() + { + UiObjectMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiObjectMemberList *next; + UiObjectMember *member; +}; + +class QML_PARSER_EXPORT UiProgram: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiProgram) + + UiProgram(UiImportList *imports, UiObjectMemberList *members) + : imports(imports), members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (imports) + return imports->firstSourceLocation(); + else if (members) + return members->firstSourceLocation(); + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (members) + return members->lastSourceLocation(); + else if (imports) + return imports->lastSourceLocation(); + return SourceLocation(); + } + +// attributes + UiImportList *imports; + UiObjectMemberList *members; +}; + +class QML_PARSER_EXPORT UiArrayMemberList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiArrayMemberList) + + UiArrayMemberList(UiObjectMember *member) + : next(this), member(member) + { kind = K; } + + UiArrayMemberList(UiArrayMemberList *previous, UiObjectMember *member) + : member(member) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return member->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : member->lastSourceLocation(); } + + UiArrayMemberList *finish() + { + UiArrayMemberList *head = next; + next = 0; + return head; + } + +// attributes + UiArrayMemberList *next; + UiObjectMember *member; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT UiObjectInitializer: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectInitializer) + + UiObjectInitializer(UiObjectMemberList *members) + : members(members) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return lbraceToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + SourceLocation lbraceToken; + UiObjectMemberList *members; + SourceLocation rbraceToken; +}; + +class QML_PARSER_EXPORT UiParameterList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(UiParameterList) + + UiParameterList(const QStringRef &t, const QStringRef &n): + type (t), name (n), next (this) + { kind = K; } + + UiParameterList(UiParameterList *previous, const QStringRef &t, const QStringRef &n): + type (t), name (n) + { + kind = K; + next = previous->next; + previous->next = this; + } + + virtual void accept0(Visitor *) {} + + virtual SourceLocation firstSourceLocation() const + { return propertyTypeToken; } + + virtual SourceLocation lastSourceLocation() const + { return next ? next->lastSourceLocation() : identifierToken; } + + inline UiParameterList *finish () + { + UiParameterList *front = next; + next = 0; + return front; + } + +// attributes + QStringRef type; + QStringRef name; + UiParameterList *next; + SourceLocation commaToken; + SourceLocation propertyTypeToken; + SourceLocation identifierToken; +}; + +class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiPublicMember) + + UiPublicMember(const QStringRef &memberType, + const QStringRef &name) + : type(Property), memberType(memberType), name(name), statement(0), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + UiPublicMember(const QStringRef &memberType, + const QStringRef &name, + Statement *statement) + : type(Property), memberType(memberType), name(name), statement(statement), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { + if (defaultToken.isValid()) + return defaultToken; + else if (readonlyToken.isValid()) + return readonlyToken; + + return propertyToken; + } + + virtual SourceLocation lastSourceLocation() const + { + if (binding) + return binding->lastSourceLocation(); + if (statement) + return statement->lastSourceLocation(); + + return semicolonToken; + } + +// attributes + enum { Signal, Property } type; + QStringRef typeModifier; + QStringRef memberType; + QStringRef name; + Statement *statement; // initialized with a JS expression + UiObjectMember *binding; // initialized with a QML object or array. + bool isDefaultMember; + bool isReadonlyMember; + UiParameterList *parameters; + SourceLocation defaultToken; + SourceLocation readonlyToken; + SourceLocation propertyToken; + SourceLocation typeModifierToken; + SourceLocation typeToken; + SourceLocation identifierToken; + SourceLocation colonToken; + SourceLocation semicolonToken; +}; + +class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectDefinition) + + UiObjectDefinition(UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedTypeNameId(qualifiedTypeNameId), initializer(initializer) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return qualifiedTypeNameId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + +// attributes + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; +}; + +class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiSourceElement) + + UiSourceElement(Node *sourceElement) + : sourceElement(sourceElement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement)) + return varStmt->lastSourceLocation(); + + return SourceLocation(); + } + + virtual void accept0(Visitor *visitor); + + +// attributes + Node *sourceElement; +}; + +class QML_PARSER_EXPORT UiObjectBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiObjectBinding) + + UiObjectBinding(UiQualifiedId *qualifiedId, + UiQualifiedId *qualifiedTypeNameId, + UiObjectInitializer *initializer) + : qualifiedId(qualifiedId), + qualifiedTypeNameId(qualifiedTypeNameId), + initializer(initializer), + hasOnToken(false) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { + if (hasOnToken && qualifiedTypeNameId) + return qualifiedTypeNameId->identifierToken; + + return qualifiedId->identifierToken; + } + + virtual SourceLocation lastSourceLocation() const + { return initializer->rbraceToken; } + + virtual void accept0(Visitor *visitor); + + +// attributes + UiQualifiedId *qualifiedId; + UiQualifiedId *qualifiedTypeNameId; + UiObjectInitializer *initializer; + SourceLocation colonToken; + bool hasOnToken; +}; + +class QML_PARSER_EXPORT UiScriptBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiScriptBinding) + + UiScriptBinding(UiQualifiedId *qualifiedId, + Statement *statement) + : qualifiedId(qualifiedId), + statement(statement) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return statement->lastSourceLocation(); } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + Statement *statement; + SourceLocation colonToken; +}; + +class QML_PARSER_EXPORT UiArrayBinding: public UiObjectMember +{ +public: + QQMLJS_DECLARE_AST_NODE(UiArrayBinding) + + UiArrayBinding(UiQualifiedId *qualifiedId, + UiArrayMemberList *members) + : qualifiedId(qualifiedId), + members(members) + { kind = K; } + + virtual SourceLocation firstSourceLocation() const + { return qualifiedId->identifierToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbracketToken; } + + virtual void accept0(Visitor *visitor); + +// attributes + UiQualifiedId *qualifiedId; + UiArrayMemberList *members; + SourceLocation colonToken; + SourceLocation lbracketToken; + SourceLocation rbracketToken; +}; + +} } // namespace AST + + + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlparser/qqmljsastfwd_p.h b/src/tools/qdoc/qmlparser/qqmljsastfwd_p.h new file mode 100644 index 0000000000..dec1cbc599 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsastfwd_p.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSAST_FWD_P_H +#define QQMLJSAST_FWD_P_H + +#include "qqmljsglobal_p.h" + +#include <QtCore/qglobal.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +class SourceLocation +{ +public: + SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) + : offset(offset), length(length), + startLine(line), startColumn(column) + { } + + bool isValid() const { return length != 0; } + + quint32 begin() const { return offset; } + quint32 end() const { return offset + length; } + +// attributes + // ### encode + quint32 offset; + quint32 length; + quint32 startLine; + quint32 startColumn; +}; + +class Visitor; +class Node; +class ExpressionNode; +class Statement; +class ThisExpression; +class IdentifierExpression; +class NullExpression; +class TrueLiteral; +class FalseLiteral; +class NumericLiteral; +class StringLiteral; +class RegExpLiteral; +class ArrayLiteral; +class ObjectLiteral; +class ElementList; +class Elision; +class PropertyNameAndValueList; +class PropertyName; +class IdentifierPropertyName; +class StringLiteralPropertyName; +class NumericLiteralPropertyName; +class ArrayMemberExpression; +class FieldMemberExpression; +class NewMemberExpression; +class NewExpression; +class CallExpression; +class ArgumentList; +class PostIncrementExpression; +class PostDecrementExpression; +class DeleteExpression; +class VoidExpression; +class TypeOfExpression; +class PreIncrementExpression; +class PreDecrementExpression; +class UnaryPlusExpression; +class UnaryMinusExpression; +class TildeExpression; +class NotExpression; +class BinaryExpression; +class ConditionalExpression; +class Expression; // ### rename +class Block; +class StatementList; +class VariableStatement; +class VariableDeclarationList; +class VariableDeclaration; +class EmptyStatement; +class ExpressionStatement; +class IfStatement; +class DoWhileStatement; +class WhileStatement; +class ForStatement; +class LocalForStatement; +class ForEachStatement; +class LocalForEachStatement; +class ContinueStatement; +class BreakStatement; +class ReturnStatement; +class WithStatement; +class SwitchStatement; +class CaseBlock; +class CaseClauses; +class CaseClause; +class DefaultClause; +class LabelledStatement; +class ThrowStatement; +class TryStatement; +class Catch; +class Finally; +class FunctionDeclaration; +class FunctionExpression; +class FormalParameterList; +class FunctionBody; +class Program; +class SourceElements; +class SourceElement; +class FunctionSourceElement; +class StatementSourceElement; +class DebuggerStatement; +class NestedExpression; + +// ui elements +class UiProgram; +class UiImportList; +class UiImport; +class UiPublicMember; +class UiObjectDefinition; +class UiObjectInitializer; +class UiObjectBinding; +class UiScriptBinding; +class UiSourceElement; +class UiArrayBinding; +class UiObjectMember; +class UiObjectMemberList; +class UiArrayMemberList; +class UiQualifiedId; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlparser/qqmljsastvisitor.cpp b/src/tools/qdoc/qmlparser/qqmljsastvisitor.cpp new file mode 100644 index 0000000000..2d854dc735 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsastvisitor.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 "qqmljsastvisitor_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +Visitor::Visitor() +{ +} + +Visitor::~Visitor() +{ +} + +} } // namespace QQmlJS::AST + +QT_QML_END_NAMESPACE diff --git a/src/tools/qdoc/qmlparser/qqmljsastvisitor_p.h b/src/tools/qdoc/qmlparser/qqmljsastvisitor_p.h new file mode 100644 index 0000000000..991580309d --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsastvisitor_p.h @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSASTVISITOR_P_H +#define QQMLJSASTVISITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmljsastfwd_p.h" +#include "qqmljsglobal_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +class QML_PARSER_EXPORT Visitor +{ +public: + Visitor(); + virtual ~Visitor(); + + virtual bool preVisit(Node *) { return true; } + virtual void postVisit(Node *) {} + + // Ui + virtual bool visit(UiProgram *) { return true; } + virtual bool visit(UiImportList *) { return true; } + virtual bool visit(UiImport *) { return true; } + virtual bool visit(UiPublicMember *) { return true; } + virtual bool visit(UiSourceElement *) { return true; } + virtual bool visit(UiObjectDefinition *) { return true; } + virtual bool visit(UiObjectInitializer *) { return true; } + virtual bool visit(UiObjectBinding *) { return true; } + virtual bool visit(UiScriptBinding *) { return true; } + virtual bool visit(UiArrayBinding *) { return true; } + virtual bool visit(UiObjectMemberList *) { return true; } + virtual bool visit(UiArrayMemberList *) { return true; } + virtual bool visit(UiQualifiedId *) { return true; } + + virtual void endVisit(UiProgram *) {} + virtual void endVisit(UiImportList *) {} + virtual void endVisit(UiImport *) {} + virtual void endVisit(UiPublicMember *) {} + virtual void endVisit(UiSourceElement *) {} + virtual void endVisit(UiObjectDefinition *) {} + virtual void endVisit(UiObjectInitializer *) {} + virtual void endVisit(UiObjectBinding *) {} + virtual void endVisit(UiScriptBinding *) {} + virtual void endVisit(UiArrayBinding *) {} + virtual void endVisit(UiObjectMemberList *) {} + virtual void endVisit(UiArrayMemberList *) {} + virtual void endVisit(UiQualifiedId *) {} + + // QQmlJS + virtual bool visit(ThisExpression *) { return true; } + virtual void endVisit(ThisExpression *) {} + + virtual bool visit(IdentifierExpression *) { return true; } + virtual void endVisit(IdentifierExpression *) {} + + virtual bool visit(NullExpression *) { return true; } + virtual void endVisit(NullExpression *) {} + + virtual bool visit(TrueLiteral *) { return true; } + virtual void endVisit(TrueLiteral *) {} + + virtual bool visit(FalseLiteral *) { return true; } + virtual void endVisit(FalseLiteral *) {} + + virtual bool visit(StringLiteral *) { return true; } + virtual void endVisit(StringLiteral *) {} + + virtual bool visit(NumericLiteral *) { return true; } + virtual void endVisit(NumericLiteral *) {} + + virtual bool visit(RegExpLiteral *) { return true; } + virtual void endVisit(RegExpLiteral *) {} + + virtual bool visit(ArrayLiteral *) { return true; } + virtual void endVisit(ArrayLiteral *) {} + + virtual bool visit(ObjectLiteral *) { return true; } + virtual void endVisit(ObjectLiteral *) {} + + virtual bool visit(ElementList *) { return true; } + virtual void endVisit(ElementList *) {} + + virtual bool visit(Elision *) { return true; } + virtual void endVisit(Elision *) {} + + virtual bool visit(PropertyNameAndValueList *) { return true; } + virtual void endVisit(PropertyNameAndValueList *) {} + + virtual bool visit(NestedExpression *) { return true; } + virtual void endVisit(NestedExpression *) {} + + virtual bool visit(IdentifierPropertyName *) { return true; } + virtual void endVisit(IdentifierPropertyName *) {} + + virtual bool visit(StringLiteralPropertyName *) { return true; } + virtual void endVisit(StringLiteralPropertyName *) {} + + virtual bool visit(NumericLiteralPropertyName *) { return true; } + virtual void endVisit(NumericLiteralPropertyName *) {} + + virtual bool visit(ArrayMemberExpression *) { return true; } + virtual void endVisit(ArrayMemberExpression *) {} + + virtual bool visit(FieldMemberExpression *) { return true; } + virtual void endVisit(FieldMemberExpression *) {} + + virtual bool visit(NewMemberExpression *) { return true; } + virtual void endVisit(NewMemberExpression *) {} + + virtual bool visit(NewExpression *) { return true; } + virtual void endVisit(NewExpression *) {} + + virtual bool visit(CallExpression *) { return true; } + virtual void endVisit(CallExpression *) {} + + virtual bool visit(ArgumentList *) { return true; } + virtual void endVisit(ArgumentList *) {} + + virtual bool visit(PostIncrementExpression *) { return true; } + virtual void endVisit(PostIncrementExpression *) {} + + virtual bool visit(PostDecrementExpression *) { return true; } + virtual void endVisit(PostDecrementExpression *) {} + + virtual bool visit(DeleteExpression *) { return true; } + virtual void endVisit(DeleteExpression *) {} + + virtual bool visit(VoidExpression *) { return true; } + virtual void endVisit(VoidExpression *) {} + + virtual bool visit(TypeOfExpression *) { return true; } + virtual void endVisit(TypeOfExpression *) {} + + virtual bool visit(PreIncrementExpression *) { return true; } + virtual void endVisit(PreIncrementExpression *) {} + + virtual bool visit(PreDecrementExpression *) { return true; } + virtual void endVisit(PreDecrementExpression *) {} + + virtual bool visit(UnaryPlusExpression *) { return true; } + virtual void endVisit(UnaryPlusExpression *) {} + + virtual bool visit(UnaryMinusExpression *) { return true; } + virtual void endVisit(UnaryMinusExpression *) {} + + virtual bool visit(TildeExpression *) { return true; } + virtual void endVisit(TildeExpression *) {} + + virtual bool visit(NotExpression *) { return true; } + virtual void endVisit(NotExpression *) {} + + virtual bool visit(BinaryExpression *) { return true; } + virtual void endVisit(BinaryExpression *) {} + + virtual bool visit(ConditionalExpression *) { return true; } + virtual void endVisit(ConditionalExpression *) {} + + virtual bool visit(Expression *) { return true; } + virtual void endVisit(Expression *) {} + + virtual bool visit(Block *) { return true; } + virtual void endVisit(Block *) {} + + virtual bool visit(StatementList *) { return true; } + virtual void endVisit(StatementList *) {} + + virtual bool visit(VariableStatement *) { return true; } + virtual void endVisit(VariableStatement *) {} + + virtual bool visit(VariableDeclarationList *) { return true; } + virtual void endVisit(VariableDeclarationList *) {} + + virtual bool visit(VariableDeclaration *) { return true; } + virtual void endVisit(VariableDeclaration *) {} + + virtual bool visit(EmptyStatement *) { return true; } + virtual void endVisit(EmptyStatement *) {} + + virtual bool visit(ExpressionStatement *) { return true; } + virtual void endVisit(ExpressionStatement *) {} + + virtual bool visit(IfStatement *) { return true; } + virtual void endVisit(IfStatement *) {} + + virtual bool visit(DoWhileStatement *) { return true; } + virtual void endVisit(DoWhileStatement *) {} + + virtual bool visit(WhileStatement *) { return true; } + virtual void endVisit(WhileStatement *) {} + + virtual bool visit(ForStatement *) { return true; } + virtual void endVisit(ForStatement *) {} + + virtual bool visit(LocalForStatement *) { return true; } + virtual void endVisit(LocalForStatement *) {} + + virtual bool visit(ForEachStatement *) { return true; } + virtual void endVisit(ForEachStatement *) {} + + virtual bool visit(LocalForEachStatement *) { return true; } + virtual void endVisit(LocalForEachStatement *) {} + + virtual bool visit(ContinueStatement *) { return true; } + virtual void endVisit(ContinueStatement *) {} + + virtual bool visit(BreakStatement *) { return true; } + virtual void endVisit(BreakStatement *) {} + + virtual bool visit(ReturnStatement *) { return true; } + virtual void endVisit(ReturnStatement *) {} + + virtual bool visit(WithStatement *) { return true; } + virtual void endVisit(WithStatement *) {} + + virtual bool visit(SwitchStatement *) { return true; } + virtual void endVisit(SwitchStatement *) {} + + virtual bool visit(CaseBlock *) { return true; } + virtual void endVisit(CaseBlock *) {} + + virtual bool visit(CaseClauses *) { return true; } + virtual void endVisit(CaseClauses *) {} + + virtual bool visit(CaseClause *) { return true; } + virtual void endVisit(CaseClause *) {} + + virtual bool visit(DefaultClause *) { return true; } + virtual void endVisit(DefaultClause *) {} + + virtual bool visit(LabelledStatement *) { return true; } + virtual void endVisit(LabelledStatement *) {} + + virtual bool visit(ThrowStatement *) { return true; } + virtual void endVisit(ThrowStatement *) {} + + virtual bool visit(TryStatement *) { return true; } + virtual void endVisit(TryStatement *) {} + + virtual bool visit(Catch *) { return true; } + virtual void endVisit(Catch *) {} + + virtual bool visit(Finally *) { return true; } + virtual void endVisit(Finally *) {} + + virtual bool visit(FunctionDeclaration *) { return true; } + virtual void endVisit(FunctionDeclaration *) {} + + virtual bool visit(FunctionExpression *) { return true; } + virtual void endVisit(FunctionExpression *) {} + + virtual bool visit(FormalParameterList *) { return true; } + virtual void endVisit(FormalParameterList *) {} + + virtual bool visit(FunctionBody *) { return true; } + virtual void endVisit(FunctionBody *) {} + + virtual bool visit(Program *) { return true; } + virtual void endVisit(Program *) {} + + virtual bool visit(SourceElements *) { return true; } + virtual void endVisit(SourceElements *) {} + + virtual bool visit(FunctionSourceElement *) { return true; } + virtual void endVisit(FunctionSourceElement *) {} + + virtual bool visit(StatementSourceElement *) { return true; } + virtual void endVisit(StatementSourceElement *) {} + + virtual bool visit(DebuggerStatement *) { return true; } + virtual void endVisit(DebuggerStatement *) {} +}; + +} } // namespace AST + +QT_QML_END_NAMESPACE + +#endif // QQMLJSASTVISITOR_P_H diff --git a/src/tools/qdoc/qmlparser/qqmljsengine_p.cpp b/src/tools/qdoc/qmlparser/qqmljsengine_p.cpp new file mode 100644 index 0000000000..459ba8d7dc --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsengine_p.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 "qqmljsengine_p.h" +#include "qqmljsglobal_p.h" + +#include <qnumeric.h> +#include <QHash> +#include <QDebug> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +static int toDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'z')) + return 10 + c - 'a'; + else if ((c >= 'A') && (c <= 'Z')) + return 10 + c - 'A'; + return -1; +} + +double integerFromString(const char *buf, int size, int radix) +{ + if (size == 0) + return qSNaN(); + + double sign = 1.0; + int i = 0; + if (buf[0] == '+') { + ++i; + } else if (buf[0] == '-') { + sign = -1.0; + ++i; + } + + if (((size-i) >= 2) && (buf[i] == '0')) { + if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) + && (radix < 34)) { + if ((radix != 0) && (radix != 16)) + return 0; + radix = 16; + i += 2; + } else { + if (radix == 0) { + radix = 8; + ++i; + } + } + } else if (radix == 0) { + radix = 10; + } + + int j = i; + for ( ; i < size; ++i) { + int d = toDigit(buf[i]); + if ((d == -1) || (d >= radix)) + break; + } + double result; + if (j == i) { + if (!qstrcmp(buf, "Infinity")) + result = qInf(); + else + result = qSNaN(); + } else { + result = 0; + double multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +double integerFromString(const QString &str, int radix) +{ + QByteArray ba = str.trimmed().toLatin1(); + return integerFromString(ba.constData(), ba.size(), radix); +} + + +Engine::Engine() + : _lexer(0) +{ } + +Engine::~Engine() +{ } + +void Engine::setCode(const QString &code) +{ _code = code; } + +void Engine::addComment(int pos, int len, int line, int col) +{ if (len > 0) _comments.append(QQmlJS::AST::SourceLocation(pos, len, line, col)); } + +QList<QQmlJS::AST::SourceLocation> Engine::comments() const +{ return _comments; } + +Lexer *Engine::lexer() const +{ return _lexer; } + +void Engine::setLexer(Lexer *lexer) +{ _lexer = lexer; } + +MemoryPool *Engine::pool() +{ return &_pool; } + +QStringRef Engine::newStringRef(const QString &text) +{ + const int pos = _extraCode.length(); + _extraCode += text; + return _extraCode.midRef(pos, text.length()); +} + +QStringRef Engine::newStringRef(const QChar *chars, int size) +{ return newStringRef(QString(chars, size)); } + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE diff --git a/src/tools/qdoc/qmlparser/qqmljsengine_p.h b/src/tools/qdoc/qmlparser/qqmljsengine_p.h new file mode 100644 index 0000000000..3cb78de4eb --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsengine_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSENGINE_P_H +#define QQMLJSENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmljsglobal_p.h" +#include "qqmljsastfwd_p.h" +#include "qqmljsmemorypool_p.h" + +#include <QString> +#include <QSet> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Lexer; +class MemoryPool; + +class QML_PARSER_EXPORT DiagnosticMessage +{ +public: + enum Kind { Warning, Error }; + + DiagnosticMessage() + : kind(Error) {} + + DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message) + : kind(kind), loc(loc), message(message) {} + + bool isWarning() const + { return kind == Warning; } + + bool isError() const + { return kind == Error; } + + Kind kind; + AST::SourceLocation loc; + QString message; +}; + +class QML_PARSER_EXPORT Engine +{ + Lexer *_lexer; + MemoryPool _pool; + QList<AST::SourceLocation> _comments; + QString _extraCode; + QString _code; + +public: + Engine(); + ~Engine(); + + void setCode(const QString &code); + + void addComment(int pos, int len, int line, int col); + QList<AST::SourceLocation> comments() const; + + Lexer *lexer() const; + void setLexer(Lexer *lexer); + + MemoryPool *pool(); + + inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); } + + QStringRef newStringRef(const QString &s); + QStringRef newStringRef(const QChar *chars, int size); +}; + +double integerFromString(const char *buf, int size, int radix); + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif // QQMLJSENGINE_P_H diff --git a/src/tools/qdoc/qmlparser/qqmljsglobal_p.h b/src/tools/qdoc/qmlparser/qqmljsglobal_p.h new file mode 100644 index 0000000000..81c90310ad --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsglobal_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ +#ifndef QQMLJSGLOBAL_P_H +#define QQMLJSGLOBAL_P_H + +#include <QtCore/qglobal.h> + +#ifdef QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE + +# ifdef QDECLARATIVEJS_BUILD_DIR +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# elif QML_BUILD_STATIC_LIB +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif // QQMLJS_BUILD_DIR + +#else // !QT_CREATOR +# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE +# define QT_QML_END_NAMESPACE QT_END_NAMESPACE +# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) + // QmlDevTools is a static library +# define QML_PARSER_EXPORT +# else +# define QML_PARSER_EXPORT Q_AUTOTEST_EXPORT +# endif +#endif // QT_CREATOR + +#endif // QQMLJSGLOBAL_P_H diff --git a/src/tools/qdoc/qmlparser/qqmljsgrammar.cpp b/src/tools/qdoc/qmlparser/qqmljsgrammar.cpp new file mode 100644 index 0000000000..f69f809ee3 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsgrammar.cpp @@ -0,0 +1,1013 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module 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$ +** +****************************************************************************/ + +// This file was generated by qlalr - DO NOT EDIT! +#include "qqmljsgrammar_p.h" + +QT_BEGIN_NAMESPACE + +const char *const QQmlJSGrammar::spell [] = { + "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue", + "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===", + "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier", + "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=", + "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=", + "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", + ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", + "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", + "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", "public", + "import", "as", "on", 0, 0, 0, 0, 0, 0, 0, + 0, 0}; + +const short QQmlJSGrammar::lhs [] = { + 102, 102, 102, 102, 102, 102, 103, 109, 109, 112, + 112, 114, 113, 113, 113, 113, 113, 113, 113, 113, + 116, 111, 110, 119, 119, 120, 120, 121, 121, 118, + 107, 107, 107, 107, 123, 123, 123, 123, 123, 123, + 123, 107, 131, 131, 131, 132, 132, 133, 133, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 117, 117, 117, 117, + 117, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 122, + 138, 138, 138, 138, 137, 137, 140, 140, 142, 142, + 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 144, 144, 115, 115, 115, + 115, 115, 147, 147, 148, 148, 148, 148, 146, 146, + 149, 149, 150, 150, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 153, 153, 153, + 153, 154, 154, 154, 155, 155, 155, 155, 156, 156, + 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, + 157, 158, 158, 158, 158, 158, 159, 159, 159, 159, + 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, + 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, + 169, 170, 170, 171, 171, 141, 141, 172, 172, 173, + 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, + 173, 105, 105, 174, 174, 175, 175, 176, 176, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 124, 185, 185, 184, 184, 135, + 135, 186, 186, 187, 187, 189, 189, 188, 190, 193, + 191, 191, 194, 192, 192, 125, 126, 126, 127, 127, + 177, 177, 177, 177, 177, 177, 177, 178, 178, 178, + 178, 179, 179, 179, 179, 180, 180, 128, 129, 195, + 195, 198, 198, 196, 196, 199, 197, 181, 181, 181, + 182, 182, 130, 130, 130, 200, 201, 183, 183, 134, + 145, 205, 205, 202, 202, 203, 203, 206, 108, 108, + 207, 207, 106, 106, 204, 204, 139, 139, 208}; + +const short QQmlJSGrammar::rhs [] = { + 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 1, 2, 2, 3, 3, 5, 5, 4, 4, + 2, 0, 1, 1, 2, 1, 3, 2, 3, 2, + 1, 5, 4, 4, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 0, 1, 2, 4, 6, + 6, 3, 3, 7, 7, 4, 4, 5, 5, 5, + 6, 6, 10, 6, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 3, 3, 4, 5, 3, 4, 3, 1, + 1, 2, 3, 4, 1, 2, 3, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 3, 5, 1, 2, 4, 4, 4, 3, 0, 1, + 1, 3, 1, 1, 1, 2, 2, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 1, 3, 3, + 3, 1, 3, 3, 1, 3, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, + 3, 1, 3, 3, 3, 3, 1, 3, 3, 3, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 5, 1, 5, 1, 3, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 0, 1, 1, 3, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 2, 0, 1, 3, + 3, 1, 1, 1, 3, 1, 3, 2, 2, 2, + 0, 1, 2, 0, 1, 1, 2, 2, 7, 5, + 7, 7, 5, 9, 10, 7, 8, 2, 2, 3, + 3, 2, 2, 3, 3, 3, 3, 5, 5, 3, + 5, 1, 2, 0, 1, 4, 3, 3, 3, 3, + 3, 3, 3, 3, 4, 5, 2, 2, 2, 8, + 8, 1, 3, 0, 1, 0, 1, 1, 1, 1, + 1, 2, 1, 1, 0, 1, 0, 1, 2}; + +const short QQmlJSGrammar::action_default [] = { + 0, 0, 22, 0, 0, 0, 22, 0, 175, 242, + 206, 214, 210, 154, 226, 202, 3, 139, 73, 155, + 218, 222, 143, 172, 153, 158, 138, 192, 179, 0, + 80, 81, 76, 345, 67, 347, 0, 0, 0, 0, + 78, 0, 0, 74, 77, 71, 0, 0, 68, 70, + 69, 79, 72, 0, 75, 0, 0, 168, 0, 0, + 155, 174, 157, 156, 0, 0, 0, 170, 171, 169, + 173, 0, 203, 0, 0, 0, 0, 193, 0, 0, + 0, 0, 0, 0, 183, 0, 0, 0, 177, 178, + 176, 181, 185, 184, 182, 180, 195, 194, 196, 0, + 211, 0, 207, 0, 0, 149, 136, 148, 137, 105, + 106, 107, 132, 108, 133, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 134, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 135, + 0, 0, 147, 243, 150, 0, 151, 0, 152, 146, + 0, 239, 232, 230, 237, 238, 236, 235, 241, 234, + 233, 231, 240, 227, 0, 215, 0, 0, 219, 0, + 0, 223, 0, 0, 149, 141, 0, 140, 0, 145, + 159, 0, 346, 334, 335, 0, 332, 0, 333, 0, + 336, 250, 257, 256, 264, 252, 0, 253, 337, 0, + 344, 254, 255, 260, 258, 341, 338, 343, 261, 0, + 272, 0, 0, 0, 0, 345, 67, 0, 347, 68, + 244, 286, 69, 0, 0, 0, 273, 0, 0, 262, + 263, 0, 251, 259, 287, 288, 331, 342, 0, 302, + 303, 304, 305, 0, 298, 299, 300, 301, 328, 329, + 0, 0, 0, 0, 0, 291, 292, 248, 246, 208, + 216, 212, 228, 204, 249, 0, 155, 220, 224, 197, + 186, 0, 0, 205, 0, 0, 0, 0, 198, 0, + 0, 0, 0, 0, 190, 188, 191, 189, 187, 200, + 199, 201, 0, 213, 0, 209, 0, 247, 155, 0, + 229, 244, 245, 0, 244, 0, 0, 294, 0, 0, + 0, 296, 0, 217, 0, 0, 221, 0, 0, 225, + 284, 0, 276, 285, 279, 0, 283, 0, 244, 277, + 0, 244, 0, 0, 295, 0, 0, 0, 297, 346, + 334, 0, 0, 336, 0, 330, 0, 320, 0, 0, + 0, 290, 0, 289, 0, 348, 0, 104, 266, 269, + 0, 105, 272, 108, 133, 110, 111, 76, 115, 116, + 67, 117, 120, 74, 77, 68, 244, 69, 79, 123, + 72, 125, 75, 127, 128, 273, 130, 131, 135, 0, + 97, 0, 0, 99, 103, 101, 88, 100, 102, 0, + 98, 87, 267, 265, 143, 144, 149, 0, 142, 0, + 319, 0, 306, 307, 0, 318, 0, 0, 0, 309, + 314, 312, 315, 0, 0, 313, 314, 0, 310, 0, + 311, 268, 317, 0, 268, 316, 0, 321, 322, 0, + 268, 323, 324, 0, 0, 325, 0, 0, 0, 326, + 327, 161, 160, 0, 0, 0, 293, 0, 0, 0, + 308, 281, 274, 0, 282, 278, 0, 280, 270, 0, + 271, 275, 91, 0, 0, 95, 82, 0, 84, 93, + 0, 85, 94, 96, 86, 92, 83, 0, 89, 165, + 163, 167, 164, 162, 166, 339, 6, 340, 4, 2, + 65, 90, 0, 0, 68, 70, 69, 31, 5, 0, + 66, 0, 45, 44, 43, 0, 0, 58, 0, 59, + 35, 36, 37, 38, 40, 41, 62, 39, 0, 45, + 0, 0, 0, 0, 0, 54, 0, 55, 0, 0, + 26, 0, 0, 63, 27, 0, 30, 28, 24, 0, + 29, 25, 0, 56, 0, 57, 143, 0, 60, 64, + 0, 0, 0, 0, 61, 0, 52, 46, 53, 47, + 0, 0, 0, 0, 49, 0, 50, 51, 48, 0, + 0, 143, 268, 0, 0, 42, 105, 272, 108, 133, + 110, 111, 76, 115, 116, 67, 117, 120, 74, 77, + 68, 244, 69, 79, 123, 72, 125, 75, 127, 128, + 273, 130, 131, 135, 0, 32, 33, 0, 34, 8, + 0, 10, 0, 9, 0, 1, 21, 12, 0, 13, + 0, 14, 0, 19, 20, 0, 15, 16, 0, 17, + 18, 11, 23, 7, 349}; + +const short QQmlJSGrammar::goto_default [] = { + 7, 625, 207, 196, 205, 508, 496, 624, 643, 495, + 623, 621, 626, 22, 622, 18, 507, 549, 539, 546, + 541, 526, 191, 195, 197, 201, 233, 208, 230, 530, + 570, 569, 200, 232, 26, 474, 473, 356, 355, 9, + 354, 357, 107, 17, 145, 24, 13, 144, 19, 25, + 57, 23, 8, 28, 27, 269, 15, 263, 10, 259, + 12, 261, 11, 260, 20, 267, 21, 268, 14, 262, + 258, 299, 411, 264, 265, 202, 193, 192, 204, 203, + 229, 194, 360, 359, 231, 463, 462, 321, 322, 465, + 324, 464, 323, 419, 423, 426, 422, 421, 441, 442, + 185, 199, 181, 184, 198, 206, 0}; + +const short QQmlJSGrammar::action_index [] = { + 404, 1275, 2411, 2411, 2509, 1000, 68, 92, 90, -102, + 88, 62, 60, 256, -102, 298, 86, -102, -102, 638, + 83, 134, 172, 219, -102, -102, -102, 454, 194, 1275, + -102, -102, -102, 381, -102, 2215, 1555, 1275, 1275, 1275, + -102, 790, 1275, -102, -102, -102, 1275, 1275, -102, -102, + -102, -102, -102, 1275, -102, 1275, 1275, -102, 1275, 1275, + 102, 217, -102, -102, 1275, 1275, 1275, -102, -102, -102, + 204, 1275, 304, 1275, 1275, 1275, 1275, 539, 1275, 1275, + 1275, 1275, 1275, 1275, 308, 1275, 1275, 1275, 103, 131, + 135, 308, 210, 225, 216, 308, 444, 390, 434, 1275, + 82, 1275, 100, 2117, 1275, 1275, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, -102, -102, -102, -102, -102, -102, + 139, 1275, -102, -102, 91, 10, -102, 1275, -102, -102, + 1275, -102, -102, -102, -102, -102, -102, -102, -102, -102, + -102, -102, -102, -102, 1275, 26, 1275, 1275, 69, 66, + 1275, -102, 2117, 1275, 1275, -102, 97, -102, 44, -102, + -102, 67, -102, 297, 78, 24, -102, 291, -102, 36, + 2411, -102, -102, -102, -102, -102, 234, -102, -102, 12, + -102, -102, -102, -102, -102, -102, 2411, -102, -102, 464, + -102, 461, 115, 2509, 42, 381, 58, 46, 2705, 70, + 1275, -102, 74, 57, 1275, 65, -102, 59, 61, -102, + -102, 367, -102, -102, -102, -102, -102, -102, 106, -102, + -102, -102, -102, 87, -102, -102, -102, -102, -102, -102, + 56, 55, 1275, 99, 84, -102, -102, 1461, -102, 75, + 48, 52, -102, 306, 72, 53, 579, 77, 110, 370, + 230, 381, 1275, 286, 1275, 1275, 1275, 1275, 380, 1275, + 1275, 1275, 1275, 1275, 184, 169, 166, 190, 198, 460, + 363, 353, 1275, 50, 1275, 63, 1275, -102, 638, 1275, + -102, 1275, 64, 39, 1275, 30, 2509, -102, 1275, 173, + 2509, -102, 1275, 79, 1275, 1275, 81, 80, 1275, -102, + 71, 149, 32, -102, -102, 1275, -102, 381, 1275, -102, + 73, 1275, 76, 2509, -102, 1275, 142, 2509, -102, -16, + 381, -42, -12, 2411, -39, -102, 2509, -102, 1275, 154, + 2509, 14, 2509, -102, 20, 16, -32, -102, -102, 2509, + -51, 519, -4, 511, 136, 1275, 2509, -2, -35, 395, + -1, -27, 908, 4, 6, -102, 1370, -102, 0, -36, + 27, 1275, 47, 22, 1275, 45, 1275, 21, 17, 1275, + -102, 2313, 144, -102, -102, -102, -102, -102, -102, 1275, + -102, -102, -102, -102, 274, -102, 1275, -21, -102, 2509, + -102, 138, -102, -102, 2509, -102, 1275, 132, 5, -102, + 40, -102, 41, 101, 1275, -102, 38, 34, -102, -38, + -102, 2509, -102, 105, 2509, -102, 245, -102, -102, 96, + 2509, 11, -102, -7, -11, -102, 352, 8, 18, -102, + -102, -102, -102, 1275, 129, 2509, -102, 1275, 130, 2509, + -102, 49, -102, 226, -102, -102, 1275, -102, -102, 362, + -102, -102, -102, 107, 1837, -102, -102, 1649, -102, -102, + 1743, -102, -102, -102, -102, -102, -102, 114, -102, -102, + -102, -102, -102, -102, -102, -102, -102, 2411, -102, -102, + -102, 94, 9, 818, 189, -10, 31, -102, -102, 223, + -102, 191, -102, -102, -102, 300, 178, -102, 1928, -102, + -102, -102, -102, -102, -102, -102, -102, -102, 257, -25, + 381, 195, -22, 305, 240, -102, -6, -102, 818, 127, + -102, -18, 818, -102, -102, 1184, -102, -102, -102, 1092, + -102, -102, 237, -102, 1928, -102, 294, -8, -102, -102, + 176, 381, 19, 1928, -102, 165, -102, 174, -102, 2, + -52, 381, 183, 381, -102, 117, -102, -102, -102, 2019, + 880, 285, 2607, 1555, 3, -102, 522, 35, 453, 108, + 1275, 2509, 51, 23, 475, 54, -17, 700, 7, 43, + -102, 1370, -102, 28, -3, 33, 1275, 37, 15, 1275, + 25, 1275, 1, 13, 124, -102, -102, 29, -102, -102, + 728, -102, 250, -43, 627, -102, -102, 231, 372, -102, + 222, -102, 111, -102, -102, 381, -102, -102, 104, -102, + -102, -102, -102, -102, -102, + + -107, 9, -103, 2, 5, 266, 1, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -39, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 86, + -107, -107, -107, 8, -107, -107, -22, 19, 71, 174, + -107, 186, 171, -107, -107, -107, 184, 178, -107, -107, + -107, -107, -107, 144, -107, 124, 150, -107, 165, 161, + -107, -107, -107, -107, 156, 160, 157, -107, -107, -107, + -107, 147, -107, 142, 135, 179, 166, -107, 177, 170, + 117, 72, 134, 92, -107, 75, 94, 66, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, 181, + -107, 106, -107, 143, 78, 55, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -5, -107, -107, -107, -107, -107, 54, -107, -107, + 51, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, 114, -107, 113, 38, -107, -107, + 41, -107, 231, 63, 112, -107, -107, -107, -107, -107, + -107, -107, -107, 30, -107, -107, -107, 52, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, 36, -107, -107, 45, + -107, 42, -107, 40, -107, 80, -107, -107, 77, -107, + 88, -107, -107, -107, 83, 74, -107, -107, -107, -107, + -107, -10, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, 23, -107, -107, -107, -107, 100, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, 4, 223, -107, 230, 236, 222, 205, -107, 127, + 125, 115, 96, 102, -107, -107, -107, -107, -107, -107, + -107, -107, 234, -107, 215, -107, 199, -107, -107, 197, + -107, 190, -107, -107, 163, -107, 90, -107, 0, -107, + -1, -107, 203, -107, 189, 211, -107, -107, 195, -107, + -107, -107, -107, -107, -107, 191, -107, 98, 119, -107, + -107, 95, -107, 81, -107, 79, -107, 82, -107, -107, + 101, -107, -107, -16, -107, -107, 53, -107, 46, -107, + 57, -107, 59, -107, -107, -107, -107, -107, -107, 35, + -107, 33, -107, 39, -107, 89, 67, -107, -107, 58, + -107, -107, 84, -107, -107, -107, 73, -107, -107, -107, + -107, 65, -107, 43, 93, -107, 109, -107, -107, 49, + -107, 47, -107, -107, -107, -107, -107, -107, -107, 50, + -107, -107, -107, -107, -107, -107, 108, -107, -107, 61, + -107, -107, -107, -107, 62, -107, 68, -107, -107, -107, + -107, -107, -23, -107, 69, -107, -19, -107, -107, -107, + -107, 97, -107, -107, 99, -107, -107, -107, -107, -107, + 60, -61, -107, -107, 34, -107, 37, -107, 29, -107, + -107, -107, -107, 32, -107, 76, -107, 44, -107, 56, + -107, -107, -107, -107, -107, -107, 31, -107, -107, 116, + -107, -107, -107, -107, -6, -107, -107, 70, -107, -107, + 64, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, 193, -107, -107, + -107, -107, -107, 7, -107, -107, -107, -107, -107, -107, + -107, -20, -107, -107, -107, -7, -107, -107, 290, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -2, -25, -107, -15, -107, -107, -107, -107, 172, -107, + -107, -107, 287, -107, -107, 288, -107, -107, -107, 291, + -107, -107, -107, -107, 336, -107, -107, 20, -107, -107, + 15, 3, -107, 304, -107, -107, -107, 24, -107, -107, + -107, 28, 21, 26, -107, -107, -107, -107, -107, 320, + 104, -107, 13, 381, -3, -107, 6, -107, 10, -107, + 167, 22, -107, -107, 12, -107, -107, 87, -107, -107, + -107, 25, -107, -107, -107, -107, 11, -107, 14, 85, + -107, 121, -107, -107, -107, -107, -107, 27, -107, -107, + 17, -107, -107, 18, 91, -107, -107, -107, 16, -107, + -107, -107, -107, -107, -107, -4, -107, -107, -107, -107, + -107, -107, -107, -107, -107}; + +const short QQmlJSGrammar::action_info [] = { + 416, 257, 533, -132, 403, -113, 346, -102, 575, 348, + 572, -121, 531, -103, -121, 545, 345, 430, 342, 348, + 340, 343, 440, 401, 391, 545, 563, 389, 538, 446, + 352, 444, -129, 416, -124, -102, 545, 453, 420, 408, + -124, 431, -132, 424, -126, 424, 424, 620, 440, 457, + -103, 440, -129, 457, -126, 440, 560, 453, -113, 257, + 565, 346, 545, 335, 272, 346, 466, 236, 448, 190, + 149, 164, 141, 170, 99, 511, 272, 409, 257, 312, + 296, 414, 348, 312, 189, 164, 187, 318, 325, 71, + 306, 252, 644, 416, 141, 453, 292, 457, 440, 147, + 304, 71, 443, 183, 179, 141, 0, 141, 0, 172, + 99, 427, 434, 141, 301, 477, 444, 0, 0, 0, + 0, 0, 141, 0, 0, 0, 0, 292, 173, 294, + 58, 294, 542, 251, 331, 542, 333, 141, 141, 101, + 141, 59, 0, 58, 62, 256, 255, 141, 247, 246, + 141, 399, 0, 177, 59, 63, 428, 327, 620, 254, + 314, 101, 141, 478, 315, 640, 639, 242, 241, 249, + 248, 58, 634, 633, 488, 58, 249, 248, 577, 576, + 615, 141, 59, 543, 166, 518, 59, 172, 167, 455, + 459, 85, 418, 86, 85, 142, 86, 249, 248, 413, + 412, 567, 337, 512, 87, 512, 173, 87, 174, 85, + 328, 86, 512, 0, 350, 85, 64, 86, 529, 85, + 512, 86, 87, 85, 512, 86, 568, 566, 87, 64, + 579, 64, 87, 310, 469, 85, 87, 86, 0, 519, + 517, 85, 141, 86, 554, 0, 172, 536, 87, 514, + 85, 514, 86, 141, 87, 85, 545, 86, 514, 0, + 513, 65, 513, 87, 514, 173, 514, 66, 87, 513, + 514, 103, 172, 0, 65, 513, 65, 513, 0, 0, + 66, 513, 66, 637, 636, 0, 0, 470, 468, 172, + 104, 173, 105, 406, 0, 235, 234, 630, 555, 553, + 172, 537, 535, 0, 274, 275, 438, 437, 173, 172, + 406, 631, 629, 635, 0, 580, 73, 74, -90, 173, + 34, 174, 73, 74, 274, 275, 34, -90, 173, 34, + 174, 276, 277, 85, 34, 86, 0, 0, 0, 0, + 0, 628, 0, 75, 76, 0, 87, 0, 0, 75, + 76, 276, 277, 0, 0, 0, 0, 48, 50, 49, + 0, 0, 0, 48, 50, 49, 48, 50, 49, 0, + 0, 48, 50, 49, 0, 0, 279, 280, 0, 0, + 0, 34, 0, 45, 0, 281, 279, 280, 282, 45, + 283, 34, 45, 279, 280, 281, 34, 45, 282, 0, + 283, 34, 281, 279, 280, 282, 0, 283, 0, 0, + 34, 0, 281, 78, 79, 282, 0, 283, 48, 50, + 49, 80, 81, 0, 34, 82, 0, 83, 48, 50, + 49, -345, 0, 48, 50, 49, 0, 0, 48, 50, + 49, 0, 0, 0, 45, 0, 0, 48, 50, 49, + 0, 0, 0, 0, 45, 0, 0, 78, 79, 45, + 0, 48, 50, 49, 45, 80, 81, 78, 79, 82, + 0, 83, 0, 45, 0, 80, 81, 78, 79, 82, + 0, 83, 34, 279, 280, 80, 81, 45, 0, 82, + 34, 83, 281, 34, 0, 282, 0, 283, 6, 5, + 4, 1, 3, 2, 34, 0, 0, 0, 0, 0, + 0, -345, 0, 0, 245, 244, 0, 0, 0, 48, + 50, 49, 245, 244, 0, 240, 239, 48, 50, 49, + 48, 50, 49, 0, 0, 0, 0, 0, 0, 0, + 34, 48, 50, 49, 0, 45, 0, 0, 34, 0, + 0, 34, 0, 45, 0, 0, 45, 0, 0, 0, + 0, 0, 78, 79, 0, 0, 0, 45, 0, 0, + 80, 81, 245, 244, 82, 0, 83, 48, 50, 49, + 240, 239, 151, 240, 239, 48, 50, 49, 48, 50, + 49, 0, 152, 0, 0, 0, 153, 0, 0, 0, + 0, 0, 0, 45, 0, 154, 0, 155, 0, 0, + 308, 45, 0, 0, 45, 0, 0, 0, 156, 0, + 157, 62, 0, 0, 0, 0, 0, 0, 158, 0, + 0, 159, 63, 0, 0, 0, 0, 160, 0, 30, + 31, 151, 0, 161, 0, 0, 0, 0, 0, 33, + 0, 152, 0, 0, 0, 153, 34, 0, 0, 162, + 35, 36, 0, 37, 154, 0, 155, 0, 0, 0, + 503, 0, 0, 0, 44, 0, 0, 156, 0, 157, + 62, 0, 0, 0, 0, 0, 0, 158, 0, 0, + 159, 63, 51, 48, 50, 49, 160, 52, 0, 0, + 0, 0, 161, 0, 0, 0, 0, 0, 43, 54, + 32, 0, 30, 31, 40, 0, 0, 0, 162, 45, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 41, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 503, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 43, + 54, 32, 33, 0, 0, 40, 0, 0, 0, 34, + 45, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 41, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 503, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 43, + 54, 32, 33, 0, 0, 40, 0, 0, 0, 34, + 45, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 30, 31, 0, 503, 0, 0, 0, 44, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, + 0, 35, 36, 0, 37, 51, 48, 50, 49, 0, + 52, 41, 0, 0, 0, 44, 0, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 51, 48, 50, 49, 0, 52, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 502, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 215, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 0, 0, 0, 503, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 51, 504, 506, 505, 0, + 52, 0, 0, 0, 0, 226, 0, 0, 0, 0, + 0, 43, 54, 32, 210, 0, 0, 40, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 502, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, + 0, 34, 0, 0, 0, 35, 36, 0, 37, 0, + 0, 0, 0, 0, 0, 503, 0, 0, 0, 44, + 0, 0, 0, 0, 0, 0, 0, 550, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 51, 504, 506, + 505, 0, 52, 0, 0, 0, 0, 226, 0, 0, + 0, 0, 0, 43, 54, 32, 210, 0, 0, 40, + 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 502, 0, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, + 37, 0, 0, 0, 0, 0, 0, 503, 0, 0, + 0, 44, 0, 0, 0, 0, 0, 0, 0, 547, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, + 504, 506, 505, 0, 52, 0, 0, 0, 0, 226, + 0, 0, 0, 0, 0, 43, 54, 32, 210, 0, + 0, 40, 0, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -122, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, + 38, 0, 39, 41, 42, 0, 0, 44, 0, 0, + 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 51, 48, 50, 49, 0, + 52, 0, 53, 0, 55, 0, 56, 0, 0, 0, + 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, + 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, + 0, 52, 0, 53, 0, 55, 271, 56, 0, 0, + 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 475, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 34, 0, 0, 0, 35, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 41, 42, + 0, 0, 44, 0, 0, 0, 46, 0, 47, 0, + 0, 476, 0, 0, 0, 0, 0, 0, 0, 0, + 51, 48, 50, 49, 0, 52, 0, 53, 0, 55, + 0, 56, 0, 0, 0, 0, 43, 54, 32, 0, + 0, 0, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 475, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, + 0, 0, 35, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 481, 0, 0, 0, 0, + 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, + 0, 53, 0, 55, 0, 56, 0, 0, 0, 0, + 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 483, 0, 0, 29, 30, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, + 0, 0, 34, 0, 0, 0, 35, 36, 0, 37, + 0, 0, 0, 38, 0, 39, 41, 42, 0, 0, + 44, 0, 0, 0, 46, 0, 47, 0, 0, 484, + 0, 0, 0, 0, 0, 0, 0, 0, 51, 48, + 50, 49, 0, 52, 0, 53, 0, 55, 0, 56, + 0, 0, 0, 0, 43, 54, 32, 0, 0, 0, + 40, 0, 0, 0, 0, 45, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 483, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, + 35, 36, 0, 37, 0, 0, 0, 38, 0, 39, + 41, 42, 0, 0, 44, 0, 0, 0, 46, 0, + 47, 0, 0, 486, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 48, 50, 49, 0, 52, 0, 53, + 0, 55, 0, 56, 0, 0, 0, 0, 43, 54, + 32, 0, 0, 0, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 34, 217, 0, + 0, 218, 36, 0, 37, 0, 0, 0, 38, 0, + 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, + 0, 47, 0, 0, 0, 0, 0, 0, 0, 221, + 0, 0, 0, 51, 48, 50, 49, 223, 52, 0, + 53, 225, 55, 0, 56, 0, 228, 0, 0, 43, + 54, 32, 0, 0, 0, 40, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 34, 217, + 0, 0, 582, 583, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, + 221, 0, 0, 0, 51, 48, 50, 49, 223, 52, + 0, 53, 225, 55, 0, 56, 0, 228, 0, 0, + 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 109, 110, 111, 0, 0, 113, 115, 116, 0, + 0, 117, 0, 118, 0, 0, 0, 120, 121, 122, + 0, 0, 0, 0, 0, 0, 34, 123, 124, 125, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 129, 0, 0, 0, + 0, 0, 0, 48, 50, 49, 130, 131, 132, 0, + 134, 135, 136, 137, 138, 139, 0, 0, 127, 133, + 119, 112, 114, 128, 0, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, + 110, 111, 0, 0, 113, 115, 116, 0, 0, 117, + 0, 118, 0, 0, 0, 120, 121, 122, 0, 0, + 0, 0, 0, 0, 393, 123, 124, 125, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 126, 0, + 0, 0, 394, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 0, 0, 0, 0, 0, + 398, 395, 397, 0, 130, 131, 132, 0, 134, 135, + 136, 137, 138, 139, 0, 0, 127, 133, 119, 112, + 114, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 109, 110, 111, + 0, 0, 113, 115, 116, 0, 0, 117, 0, 118, + 0, 0, 0, 120, 121, 122, 0, 0, 0, 0, + 0, 0, 393, 123, 124, 125, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, + 394, 0, 0, 0, 0, 0, 0, 0, 396, 0, + 0, 0, 129, 0, 0, 0, 0, 0, 398, 395, + 397, 0, 130, 131, 132, 0, 134, 135, 136, 137, + 138, 139, 0, 0, 127, 133, 119, 112, 114, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, + 211, 0, 29, 30, 31, 213, 0, 0, 0, 0, + 0, 0, 214, 215, 0, 0, 0, 0, 0, 0, + 216, 217, 0, 0, 218, 36, 0, 37, 0, 0, + 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, + 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, + 220, 0, 221, 0, 0, 0, 51, 219, 222, 49, + 223, 52, 224, 53, 225, 55, 226, 56, 227, 228, + 0, 0, 43, 54, 32, 210, 212, 0, 40, 0, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 209, 0, 0, 0, 0, 211, 0, + 29, 30, 31, 213, 0, 0, 0, 0, 0, 0, + 214, 33, 0, 0, 0, 0, 0, 0, 216, 217, + 0, 0, 218, 36, 0, 37, 0, 0, 0, 38, + 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, + 46, 0, 47, 0, 0, 0, 0, 0, 220, 0, + 221, 0, 0, 0, 51, 219, 222, 49, 223, 52, + 224, 53, 225, 55, 226, 56, 227, 228, 0, 0, + 43, 54, 32, 210, 212, 0, 40, 0, 0, 0, + 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 586, 110, 111, 0, 0, 588, 115, 590, 30, + 31, 591, 0, 118, 0, 0, 0, 120, 593, 594, + 0, 0, 0, 0, 0, 0, 595, 596, 124, 125, + 218, 36, 0, 37, 0, 0, 0, 38, 0, 39, + 597, 42, 0, 0, 599, 0, 0, 0, 46, 0, + 47, 0, 0, 0, 0, 0, 601, 0, 221, 0, + 0, 0, 603, 600, 602, 49, 604, 605, 606, 53, + 608, 609, 610, 611, 612, 613, 0, 0, 598, 607, + 592, 587, 589, 128, 40, 0, 0, 0, 0, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 361, + 110, 111, 0, 0, 363, 115, 365, 30, 31, 366, + 0, 118, 0, 0, 0, 120, 368, 369, 0, 0, + 0, 0, 0, 0, 370, 371, 124, 125, 218, 36, + 0, 37, 0, 0, 0, 38, 0, 39, 372, 42, + 0, 0, 374, 0, 0, 0, 46, 0, 47, 0, + -268, 0, 0, 0, 376, 0, 221, 0, 0, 0, + 378, 375, 377, 49, 379, 380, 381, 53, 383, 384, + 385, 386, 387, 388, 0, 0, 373, 382, 367, 362, + 364, 128, 40, 0, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + + 534, 311, 497, 309, 532, 461, 498, 499, 516, 515, + 619, 638, 16, 552, 436, 358, 616, 472, 562, 320, + 528, 238, 487, 182, 250, 243, 253, 182, 302, 641, + 627, 632, 150, 485, 143, 454, 439, 402, 445, 559, + 237, 574, 250, 578, 561, 186, 618, 458, 238, 349, + 573, 449, 447, 571, 243, 347, 450, 243, 460, 351, + 238, 353, 358, 410, 415, 439, 176, 188, 436, 250, + 467, 417, 433, 182, 425, 429, 302, 169, 456, 358, + 171, 140, 336, 334, 338, 344, 436, 392, 390, 400, + 163, 302, 307, 148, 146, 339, 439, 404, 302, 358, + 404, 358, 0, 482, 501, 480, 0, 642, 0, 479, + 0, 0, 0, 320, 60, 0, 186, 501, 90, 60, + 60, 489, 302, 60, 617, 93, 0, 88, 0, 405, + 0, 461, 405, 60, 60, 451, 180, 60, 0, 180, + 60, 60, 60, 451, 60, 95, 89, 146, 266, 287, + 60, 146, 407, 270, 60, 288, 178, 60, 106, 452, + 0, 60, 60, 60, 102, 60, 302, 332, 286, 60, + 92, 452, 60, 60, 451, 60, 165, 168, 285, 432, + 284, 435, 60, 60, 108, 501, 329, 94, 540, 96, + 60, 330, 60, 302, 494, 60, 77, 237, 60, 404, + 452, 341, 471, 72, 60, 60, 67, 69, 60, 60, + 68, 0, 70, 60, 60, 60, 61, 180, 60, 60, + 98, 491, 60, 91, 490, 60, 60, 60, 493, 60, + 84, 405, 60, 97, 492, 305, 0, 60, 0, 298, + 0, 100, 270, 298, 270, 298, 106, 298, 270, 0, + 270, 60, 270, 60, 316, 0, 270, 0, 270, 298, + 291, 326, 303, 60, 270, 319, 313, 300, 270, 297, + 60, 60, 108, 175, 295, 270, 270, 290, 60, 501, + 273, 317, 60, 270, 60, 278, 509, 270, 0, 270, + 0, 289, 0, 548, 0, 293, 551, 0, 500, 510, + 501, 501, 0, 544, 501, 0, 0, 0, 509, 0, + 0, 509, 520, 521, 522, 523, 527, 524, 525, 0, + 500, 510, 0, 500, 510, 564, 520, 521, 522, 523, + 527, 524, 525, 581, 0, 0, 0, 0, 0, 0, + 584, 585, 520, 521, 522, 523, 527, 524, 525, 556, + 0, 0, 0, 0, 0, 0, 557, 558, 520, 521, + 522, 523, 527, 524, 525, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 556, 0, 0, 540, 0, 614, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 472, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + +const short QQmlJSGrammar::action_check [] = { + 36, 36, 24, 7, 55, 7, 7, 7, 60, 36, + 8, 7, 37, 7, 7, 33, 55, 55, 60, 36, + 36, 33, 33, 55, 8, 33, 7, 7, 34, 36, + 16, 20, 7, 36, 7, 7, 33, 36, 33, 60, + 7, 7, 7, 5, 7, 5, 5, 90, 33, 36, + 7, 33, 7, 36, 7, 33, 66, 36, 7, 36, + 29, 7, 33, 31, 1, 7, 17, 55, 60, 33, + 60, 2, 8, 7, 48, 66, 1, 7, 36, 2, + 8, 7, 36, 2, 60, 2, 8, 7, 17, 1, + 60, 36, 0, 36, 8, 36, 48, 36, 33, 8, + 61, 1, 6, 36, 60, 8, -1, 8, -1, 15, + 48, 10, 7, 8, 61, 8, 20, -1, -1, -1, + -1, -1, 8, -1, -1, -1, -1, 48, 34, 79, + 40, 79, 8, 77, 61, 8, 60, 8, 8, 79, + 8, 51, -1, 40, 42, 61, 62, 8, 61, 62, + 8, 7, -1, 56, 51, 53, 55, 8, 90, 60, + 50, 79, 8, 56, 54, 61, 62, 61, 62, 61, + 62, 40, 61, 62, 60, 40, 61, 62, 61, 62, + 56, 8, 51, 56, 50, 7, 51, 15, 54, 60, + 60, 25, 60, 27, 25, 56, 27, 61, 62, 61, + 62, 36, 60, 29, 38, 29, 34, 38, 36, 25, + 61, 27, 29, -1, 60, 25, 12, 27, 29, 25, + 29, 27, 38, 25, 29, 27, 61, 62, 38, 12, + 7, 12, 38, 60, 8, 25, 38, 27, -1, 61, + 62, 25, 8, 27, 7, -1, 15, 7, 38, 75, + 25, 75, 27, 8, 38, 25, 33, 27, 75, -1, + 86, 57, 86, 38, 75, 34, 75, 63, 38, 86, + 75, 15, 15, -1, 57, 86, 57, 86, -1, -1, + 63, 86, 63, 61, 62, -1, -1, 61, 62, 15, + 34, 34, 36, 36, -1, 61, 62, 47, 61, 62, + 15, 61, 62, -1, 18, 19, 61, 62, 34, 15, + 36, 61, 62, 91, -1, 92, 18, 19, 33, 34, + 29, 36, 18, 19, 18, 19, 29, 33, 34, 29, + 36, 45, 46, 25, 29, 27, -1, -1, -1, -1, + -1, 91, -1, 45, 46, -1, 38, -1, -1, 45, + 46, 45, 46, -1, -1, -1, -1, 66, 67, 68, + -1, -1, -1, 66, 67, 68, 66, 67, 68, -1, + -1, 66, 67, 68, -1, -1, 23, 24, -1, -1, + -1, 29, -1, 92, -1, 32, 23, 24, 35, 92, + 37, 29, 92, 23, 24, 32, 29, 92, 35, -1, + 37, 29, 32, 23, 24, 35, -1, 37, -1, -1, + 29, -1, 32, 23, 24, 35, -1, 37, 66, 67, + 68, 31, 32, -1, 29, 35, -1, 37, 66, 67, + 68, 36, -1, 66, 67, 68, -1, -1, 66, 67, + 68, -1, -1, -1, 92, -1, -1, 66, 67, 68, + -1, -1, -1, -1, 92, -1, -1, 23, 24, 92, + -1, 66, 67, 68, 92, 31, 32, 23, 24, 35, + -1, 37, -1, 92, -1, 31, 32, 23, 24, 35, + -1, 37, 29, 23, 24, 31, 32, 92, -1, 35, + 29, 37, 32, 29, -1, 35, -1, 37, 94, 95, + 96, 97, 98, 99, 29, -1, -1, -1, -1, -1, + -1, 36, -1, -1, 61, 62, -1, -1, -1, 66, + 67, 68, 61, 62, -1, 61, 62, 66, 67, 68, + 66, 67, 68, -1, -1, -1, -1, -1, -1, -1, + 29, 66, 67, 68, -1, 92, -1, -1, 29, -1, + -1, 29, -1, 92, -1, -1, 92, -1, -1, -1, + -1, -1, 23, 24, -1, -1, -1, 92, -1, -1, + 31, 32, 61, 62, 35, -1, 37, 66, 67, 68, + 61, 62, 3, 61, 62, 66, 67, 68, 66, 67, + 68, -1, 13, -1, -1, -1, 17, -1, -1, -1, + -1, -1, -1, 92, -1, 26, -1, 28, -1, -1, + 31, 92, -1, -1, 92, -1, -1, -1, 39, -1, + 41, 42, -1, -1, -1, -1, -1, -1, 49, -1, + -1, 52, 53, -1, -1, -1, -1, 58, -1, 12, + 13, 3, -1, 64, -1, -1, -1, -1, -1, 22, + -1, 13, -1, -1, -1, 17, 29, -1, -1, 80, + 33, 34, -1, 36, 26, -1, 28, -1, -1, -1, + 43, -1, -1, -1, 47, -1, -1, 39, -1, 41, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + 52, 53, 65, 66, 67, 68, 58, 70, -1, -1, + -1, -1, 64, -1, -1, -1, -1, -1, 81, 82, + 83, -1, 12, 13, 87, -1, -1, -1, 80, 92, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, 81, + 82, 83, 22, -1, -1, 87, -1, -1, -1, 29, + 92, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, 81, + 82, 83, 22, -1, -1, 87, -1, -1, -1, 29, + 92, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 12, 13, -1, 43, -1, -1, -1, 47, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, 65, 66, 67, 68, -1, + 70, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, 65, 66, 67, 68, -1, 70, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, + -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, + -1, -1, 92, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, + -1, -1, -1, -1, -1, -1, -1, 55, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, + -1, -1, -1, 81, 82, 83, 84, -1, -1, 87, + -1, -1, -1, -1, 92, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 10, -1, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, -1, -1, -1, 75, + -1, -1, -1, -1, -1, 81, 82, 83, 84, -1, + -1, 87, -1, -1, -1, -1, 92, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, + -1, -1, 92, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, 72, -1, 74, 75, 76, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, 56, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, + -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, + -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, 56, + -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, + 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, + -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, + 87, -1, -1, -1, -1, 92, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, + -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, + -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, 61, + -1, -1, -1, 65, 66, 67, 68, 69, 70, -1, + 72, 73, 74, -1, 76, -1, 78, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, 30, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, -1, 76, -1, 78, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, + -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 43, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 59, -1, -1, -1, + -1, -1, -1, 66, 67, 68, 69, 70, 71, -1, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, -1, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + 65, 66, 67, -1, 69, 70, 71, -1, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, + -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, + -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, + 47, -1, -1, -1, -1, -1, -1, -1, 55, -1, + -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, + 67, -1, 69, 70, 71, -1, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, + 9, -1, 11, 12, 13, 14, -1, -1, -1, -1, + -1, -1, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, -1, 87, -1, + -1, -1, -1, 92, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, -1, -1, -1, -1, 9, -1, + 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, + 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, -1, 87, -1, -1, -1, + -1, 92, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, 5, 6, -1, -1, 9, 10, 11, 12, + 13, 14, -1, 16, -1, -1, -1, 20, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, 87, -1, -1, -1, -1, 92, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, 12, 13, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + 55, -1, -1, -1, 59, -1, 61, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, -1, 92, -1, -1, + -1, -1, -1, -1, -1, -1, -1, + + 15, 2, 105, 3, 29, 15, 4, 2, 15, 29, + 9, 15, 3, 15, 3, 2, 19, 39, 15, 15, + 13, 15, 3, 15, 2, 15, 3, 15, 3, 11, + 13, 15, 71, 39, 39, 3, 22, 2, 99, 19, + 4, 15, 2, 15, 29, 15, 19, 3, 15, 3, + 29, 22, 15, 29, 15, 2, 22, 15, 2, 2, + 15, 2, 2, 2, 2, 22, 3, 15, 3, 2, + 39, 3, 3, 15, 97, 94, 3, 39, 2, 2, + 39, 3, 3, 2, 2, 101, 3, 40, 39, 39, + 39, 3, 2, 39, 39, 15, 22, 13, 3, 2, + 13, 2, -1, 39, 13, 35, -1, 16, -1, 39, + -1, -1, -1, 15, 48, -1, 15, 13, 52, 48, + 48, 50, 3, 48, 20, 53, -1, 52, -1, 45, + -1, 15, 45, 48, 48, 50, 50, 48, -1, 50, + 48, 48, 48, 50, 48, 53, 52, 39, 48, 53, + 48, 39, 44, 53, 48, 53, 44, 48, 15, 50, + -1, 48, 48, 48, 58, 48, 3, 72, 53, 48, + 53, 50, 48, 48, 50, 48, 62, 64, 53, 82, + 53, 82, 48, 48, 41, 13, 88, 53, 16, 54, + 48, 72, 48, 3, 50, 48, 54, 4, 48, 13, + 50, 100, 86, 56, 48, 48, 50, 50, 48, 48, + 50, -1, 51, 48, 48, 48, 51, 50, 48, 48, + 54, 50, 48, 53, 50, 48, 48, 48, 50, 48, + 53, 45, 48, 54, 50, 72, -1, 48, -1, 48, + -1, 60, 53, 48, 53, 48, 15, 48, 53, -1, + 53, 48, 53, 48, 65, -1, 53, -1, 53, 48, + 55, 70, 72, 48, 53, 70, 63, 70, 53, 70, + 48, 48, 41, 42, 59, 53, 53, 55, 48, 13, + 57, 70, 48, 53, 48, 55, 20, 53, -1, 53, + -1, 55, -1, 5, -1, 61, 5, -1, 32, 33, + 13, 13, -1, 16, 13, -1, -1, -1, 20, -1, + -1, 20, 22, 23, 24, 25, 26, 27, 28, -1, + 32, 33, -1, 32, 33, 21, 22, 23, 24, 25, + 26, 27, 28, 13, -1, -1, -1, -1, -1, -1, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 13, + -1, -1, -1, -1, -1, -1, 20, 21, 22, 23, + 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 13, -1, -1, 16, -1, 18, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 39, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1}; + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qmlparser/qqmljsgrammar_p.h b/src/tools/qdoc/qmlparser/qqmljsgrammar_p.h new file mode 100644 index 0000000000..455391a862 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsgrammar_p.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module 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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// This file was generated by qlalr - DO NOT EDIT! +#ifndef QQMLJSGRAMMAR_P_H +#define QQMLJSGRAMMAR_P_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +class QQmlJSGrammar +{ +public: + enum VariousConstants { + EOF_SYMBOL = 0, + REDUCE_HERE = 101, + SHIFT_THERE = 100, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_AS = 91, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 88, + T_CONST = 84, + T_CONTINUE = 9, + T_DEBUGGER = 85, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELSE = 16, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_ERROR = 93, + T_FALSE = 83, + T_FEED_JS_EXPRESSION = 97, + T_FEED_JS_PROGRAM = 99, + T_FEED_JS_SOURCE_ELEMENT = 98, + T_FEED_JS_STATEMENT = 96, + T_FEED_UI_OBJECT_MEMBER = 95, + T_FEED_UI_PROGRAM = 94, + T_FINALLY = 20, + T_FOR = 21, + T_FUNCTION = 22, + T_GE = 23, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 90, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 87, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NULL = 81, + T_NUMERIC_LITERAL = 47, + T_ON = 92, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PROPERTY = 66, + T_PUBLIC = 89, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 68, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 86, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SIGNAL = 67, + T_STAR = 63, + T_STAR_EQ = 64, + T_STRING_LITERAL = 65, + T_SWITCH = 69, + T_THIS = 70, + T_THROW = 71, + T_TILDE = 72, + T_TRUE = 82, + T_TRY = 73, + T_TYPEOF = 74, + T_VAR = 75, + T_VOID = 76, + T_WHILE = 77, + T_WITH = 78, + T_XOR = 79, + T_XOR_EQ = 80, + + ACCEPT_STATE = 644, + RULE_COUNT = 349, + STATE_COUNT = 645, + TERMINAL_COUNT = 102, + NON_TERMINAL_COUNT = 107, + + GOTO_INDEX_OFFSET = 645, + GOTO_INFO_OFFSET = 2807, + GOTO_CHECK_OFFSET = 2807 + }; + + static const char *const spell []; + static const short lhs []; + static const short rhs []; + static const short goto_default []; + static const short action_default []; + static const short action_index []; + static const short action_info []; + static const short action_check []; + + static inline int nt_action (int state, int nt) + { + const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt; + if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt) + return goto_default [nt]; + + return action_info [GOTO_INFO_OFFSET + yyn]; + } + + static inline int t_action (int state, int token) + { + const int yyn = action_index [state] + token; + + if (yyn < 0 || action_check [yyn] != token) + return - action_default [state]; + + return action_info [yyn]; + } +}; + + +QT_END_NAMESPACE +#endif // QQMLJSGRAMMAR_P_H + diff --git a/src/tools/qdoc/qmlparser/qqmljskeywords_p.h b/src/tools/qdoc/qmlparser/qqmljskeywords_p.h new file mode 100644 index 0000000000..bbcc4855a3 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljskeywords_p.h @@ -0,0 +1,860 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSKEYWORDS_P_H +#define QQMLJSKEYWORDS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +static inline int classify2(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'a') { + if (s[1].unicode() == 's') { + return qmlMode ? Lexer::T_AS : Lexer::T_RESERVED_WORD; + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'o') { + return Lexer::T_DO; + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'f') { + return Lexer::T_IF; + } + else if (s[1].unicode() == 'n') { + return Lexer::T_IN; + } + } + else if (qmlMode && s[0].unicode() == 'o') { + if (s[1].unicode() == 'n') { + return Lexer::T_ON; + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify3(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'r') { + return Lexer::T_FOR; + } + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'n') { + if (s[2].unicode() == 't') { + return Lexer::T_INT; + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'w') { + return Lexer::T_NEW; + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'y') { + return Lexer::T_TRY; + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'r') { + return Lexer::T_VAR; + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify4(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'y') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + return Lexer::T_BYTE; + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 'e') { + return Lexer::T_CASE; + } + } + } + else if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'r') { + return Lexer::T_CHAR; + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'l') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 'e') { + return Lexer::T_ELSE; + } + } + } + else if (s[1].unicode() == 'n') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'm') { + return Lexer::T_ENUM; + } + } + } + } + else if (s[0].unicode() == 'g') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'o') { + return Lexer::T_GOTO; + } + } + } + } + else if (s[0].unicode() == 'l') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'g') { + return Lexer::T_LONG; + } + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'l') { + return Lexer::T_NULL; + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 's') { + return Lexer::T_THIS; + } + } + } + else if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'e') { + return Lexer::T_TRUE; + } + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'd') { + return Lexer::T_VOID; + } + } + } + } + else if (s[0].unicode() == 'w') { + if (s[1].unicode() == 'i') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'h') { + return Lexer::T_WITH; + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify5(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'e') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'k') { + return Lexer::T_BREAK; + } + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 'h') { + return Lexer::T_CATCH; + } + } + } + } + else if (s[1].unicode() == 'l') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 's') { + return Lexer::T_CLASS; + } + } + } + } + else if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 't') { + return Lexer::T_CONST; + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 's') { + if (s[4].unicode() == 'e') { + return Lexer::T_FALSE; + } + } + } + } + else if (s[1].unicode() == 'i') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'l') { + return Lexer::T_FINAL; + } + } + } + } + else if (s[1].unicode() == 'l') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 't') { + return Lexer::T_FLOAT; + } + } + } + } + } + else if (s[0].unicode() == 's') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'r') { + if (s[4].unicode() == 't') { + return Lexer::T_SHORT; + } + } + } + } + else if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'r') { + return Lexer::T_SUPER; + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'r') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'w') { + return Lexer::T_THROW; + } + } + } + } + } + else if (s[0].unicode() == 'w') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + return Lexer::T_WHILE; + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify6(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'e') { + return Lexer::T_DELETE; + } + } + } + } + } + else if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'u') { + if (s[3].unicode() == 'b') { + if (s[4].unicode() == 'l') { + if (s[5].unicode() == 'e') { + return Lexer::T_DOUBLE; + } + } + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'x') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 't') { + return Lexer::T_EXPORT; + } + } + } + } + } + } + else if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'm') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 't') { + return qmlMode ? Lexer::T_IMPORT : Lexer::T_RESERVED_WORD; + } + } + } + } + } + } + else if (s[0].unicode() == 'n') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'i') { + if (s[4].unicode() == 'v') { + if (s[5].unicode() == 'e') { + return Lexer::T_NATIVE; + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'b') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'c') { + return qmlMode ? Lexer::T_PUBLIC : Lexer::T_RESERVED_WORD; + } + } + } + } + } + } + else if (s[0].unicode() == 'r') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'u') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'n') { + return Lexer::T_RETURN; + } + } + } + } + } + } + else if (s[0].unicode() == 's') { + if (qmlMode && s[1].unicode() == 'i') { + if (s[2].unicode() == 'g') { + if (s[3].unicode() == 'n') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'l') { + return Lexer::T_SIGNAL; + } + } + } + } + } + else if (s[1].unicode() == 't') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'c') { + return Lexer::T_STATIC; + } + } + } + } + } + else if (s[1].unicode() == 'w') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'c') { + if (s[5].unicode() == 'h') { + return Lexer::T_SWITCH; + } + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'h') { + if (s[2].unicode() == 'r') { + if (s[3].unicode() == 'o') { + if (s[4].unicode() == 'w') { + if (s[5].unicode() == 's') { + return Lexer::T_THROWS; + } + } + } + } + } + else if (s[1].unicode() == 'y') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'o') { + if (s[5].unicode() == 'f') { + return Lexer::T_TYPEOF; + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify7(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'b') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'a') { + if (s[6].unicode() == 'n') { + return Lexer::T_BOOLEAN; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'f') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'u') { + if (s[5].unicode() == 'l') { + if (s[6].unicode() == 't') { + return Lexer::T_DEFAULT; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'e') { + if (s[1].unicode() == 'x') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'n') { + if (s[5].unicode() == 'd') { + if (s[6].unicode() == 's') { + return Lexer::T_EXTENDS; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'i') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 'l') { + if (s[5].unicode() == 'l') { + if (s[6].unicode() == 'y') { + return Lexer::T_FINALLY; + } + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'a') { + if (s[2].unicode() == 'c') { + if (s[3].unicode() == 'k') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'g') { + if (s[6].unicode() == 'e') { + return Lexer::T_PACKAGE; + } + } + } + } + } + } + else if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'i') { + if (s[3].unicode() == 'v') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 't') { + if (s[6].unicode() == 'e') { + return Lexer::T_PRIVATE; + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify8(const QChar *s, bool qmlMode) { + if (s[0].unicode() == 'a') { + if (s[1].unicode() == 'b') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'a') { + if (s[6].unicode() == 'c') { + if (s[7].unicode() == 't') { + return Lexer::T_ABSTRACT; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'c') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'i') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'u') { + if (s[7].unicode() == 'e') { + return Lexer::T_CONTINUE; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'd') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'b') { + if (s[3].unicode() == 'u') { + if (s[4].unicode() == 'g') { + if (s[5].unicode() == 'g') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'r') { + return Lexer::T_DEBUGGER; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'f') { + if (s[1].unicode() == 'u') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'o') { + if (s[7].unicode() == 'n') { + return Lexer::T_FUNCTION; + } + } + } + } + } + } + } + } + else if (qmlMode && s[0].unicode() == 'p') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 'p') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'r') { + if (s[6].unicode() == 't') { + if (s[7].unicode() == 'y') { + return Lexer::T_PROPERTY; + } + } + } + } + } + } + } + } + else if (qmlMode && s[0].unicode() == 'r') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'd') { + if (s[4].unicode() == 'o') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'l') { + if (s[7].unicode() == 'y') { + return Lexer::T_READONLY; + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'v') { + if (s[1].unicode() == 'o') { + if (s[2].unicode() == 'l') { + if (s[3].unicode() == 'a') { + if (s[4].unicode() == 't') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'l') { + if (s[7].unicode() == 'e') { + return Lexer::T_VOLATILE; + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify9(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'n') { + if (s[2].unicode() == 't') { + if (s[3].unicode() == 'e') { + if (s[4].unicode() == 'r') { + if (s[5].unicode() == 'f') { + if (s[6].unicode() == 'a') { + if (s[7].unicode() == 'c') { + if (s[8].unicode() == 'e') { + return Lexer::T_INTERFACE; + } + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 'p') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'o') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'c') { + if (s[6].unicode() == 't') { + if (s[7].unicode() == 'e') { + if (s[8].unicode() == 'd') { + return Lexer::T_PROTECTED; + } + } + } + } + } + } + } + } + } + else if (s[0].unicode() == 't') { + if (s[1].unicode() == 'r') { + if (s[2].unicode() == 'a') { + if (s[3].unicode() == 'n') { + if (s[4].unicode() == 's') { + if (s[5].unicode() == 'i') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 't') { + return Lexer::T_TRANSIENT; + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify10(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 'i') { + if (s[1].unicode() == 'm') { + if (s[2].unicode() == 'p') { + if (s[3].unicode() == 'l') { + if (s[4].unicode() == 'e') { + if (s[5].unicode() == 'm') { + if (s[6].unicode() == 'e') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 't') { + if (s[9].unicode() == 's') { + return Lexer::T_IMPLEMENTS; + } + } + } + } + } + } + } + } + } + else if (s[1].unicode() == 'n') { + if (s[2].unicode() == 's') { + if (s[3].unicode() == 't') { + if (s[4].unicode() == 'a') { + if (s[5].unicode() == 'n') { + if (s[6].unicode() == 'c') { + if (s[7].unicode() == 'e') { + if (s[8].unicode() == 'o') { + if (s[9].unicode() == 'f') { + return Lexer::T_INSTANCEOF; + } + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +static inline int classify12(const QChar *s, bool /*qmlMode*/) { + if (s[0].unicode() == 's') { + if (s[1].unicode() == 'y') { + if (s[2].unicode() == 'n') { + if (s[3].unicode() == 'c') { + if (s[4].unicode() == 'h') { + if (s[5].unicode() == 'r') { + if (s[6].unicode() == 'o') { + if (s[7].unicode() == 'n') { + if (s[8].unicode() == 'i') { + if (s[9].unicode() == 'z') { + if (s[10].unicode() == 'e') { + if (s[11].unicode() == 'd') { + return Lexer::T_SYNCHRONIZED; + } + } + } + } + } + } + } + } + } + } + } + } + return Lexer::T_IDENTIFIER; +} + +int Lexer::classify(const QChar *s, int n, bool qmlMode) { + switch (n) { + case 2: return classify2(s, qmlMode); + case 3: return classify3(s, qmlMode); + case 4: return classify4(s, qmlMode); + case 5: return classify5(s, qmlMode); + case 6: return classify6(s, qmlMode); + case 7: return classify7(s, qmlMode); + case 8: return classify8(s, qmlMode); + case 9: return classify9(s, qmlMode); + case 10: return classify10(s, qmlMode); + case 12: return classify12(s, qmlMode); + default: return Lexer::T_IDENTIFIER; + } // switch +} + +#endif // QQMLJSKEYWORDS_P_H diff --git a/src/tools/qdoc/qmlparser/qqmljslexer.cpp b/src/tools/qdoc/qmlparser/qqmljslexer.cpp new file mode 100644 index 0000000000..4c75c6e30e --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljslexer.cpp @@ -0,0 +1,1171 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 "qqmljslexer_p.h" +#include "qqmljsengine_p.h" +#include "qqmljsmemorypool_p.h" + +#ifdef QT_BOOTSTRAPPED +#define tr(x, y) QString(QLatin1String(y)) +#else +#include <QtCore/QCoreApplication> +#define tr(x, y) QCoreApplication::translate(x, y) +#endif +#include <QtCore/QVarLengthArray> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE +Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); +QT_END_NAMESPACE + +using namespace QQmlJS; + +static int regExpFlagFromChar(const QChar &ch) +{ + switch (ch.unicode()) { + case 'g': return Lexer::RegExp_Global; + case 'i': return Lexer::RegExp_IgnoreCase; + case 'm': return Lexer::RegExp_Multiline; + } + return 0; +} + +static unsigned char convertHex(ushort c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else + return (c - 'A' + 10); +} + +static QChar convertHex(QChar c1, QChar c2) +{ + return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); +} + +static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) +{ + return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()), + (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); +} + +Lexer::Lexer(Engine *engine) + : _engine(engine) + , _codePtr(0) + , _lastLinePtr(0) + , _tokenLinePtr(0) + , _tokenStartPtr(0) + , _char(QLatin1Char('\n')) + , _errorCode(NoError) + , _currentLineNumber(0) + , _tokenValue(0) + , _parenthesesState(IgnoreParentheses) + , _parenthesesCount(0) + , _stackToken(-1) + , _patternFlags(0) + , _tokenKind(0) + , _tokenLength(0) + , _tokenLine(0) + , _validTokenText(false) + , _prohibitAutomaticSemicolon(false) + , _restrictedKeyword(false) + , _terminator(false) + , _followsClosingBrace(false) + , _delimited(true) + , _qmlMode(true) +{ + if (engine) + engine->setLexer(this); +} + +bool Lexer::qmlMode() const +{ + return _qmlMode; +} + +QString Lexer::code() const +{ + return _code; +} + +void Lexer::setCode(const QString &code, int lineno, bool qmlMode) +{ + if (_engine) + _engine->setCode(code); + + _qmlMode = qmlMode; + _code = code; + _tokenText.clear(); + _tokenText.reserve(1024); + _errorMessage.clear(); + _tokenSpell = QStringRef(); + + _codePtr = code.unicode(); + _lastLinePtr = _codePtr; + _tokenLinePtr = _codePtr; + _tokenStartPtr = _codePtr; + + _char = QLatin1Char('\n'); + _errorCode = NoError; + + _currentLineNumber = lineno; + _tokenValue = 0; + + // parentheses state + _parenthesesState = IgnoreParentheses; + _parenthesesCount = 0; + + _stackToken = -1; + + _patternFlags = 0; + _tokenLength = 0; + _tokenLine = lineno; + + _validTokenText = false; + _prohibitAutomaticSemicolon = false; + _restrictedKeyword = false; + _terminator = false; + _followsClosingBrace = false; + _delimited = true; +} + +void Lexer::scanChar() +{ + _char = *_codePtr++; + + if (_char == QLatin1Char('\n')) { + _lastLinePtr = _codePtr; // points to the first character after the newline + ++_currentLineNumber; + } +} + +int Lexer::lex() +{ + const int previousTokenKind = _tokenKind; + + _tokenSpell = QStringRef(); + _tokenKind = scanToken(); + _tokenLength = _codePtr - _tokenStartPtr - 1; + + _delimited = false; + _restrictedKeyword = false; + _followsClosingBrace = (previousTokenKind == T_RBRACE); + + // update the flags + switch (_tokenKind) { + case T_LBRACE: + case T_SEMICOLON: + case T_COLON: + _delimited = true; + break; + + case T_IF: + case T_FOR: + case T_WHILE: + case T_WITH: + _parenthesesState = CountParentheses; + _parenthesesCount = 0; + break; + + case T_DO: + _parenthesesState = BalancedParentheses; + break; + + case T_CONTINUE: + case T_BREAK: + case T_RETURN: + case T_THROW: + _restrictedKeyword = true; + break; + } // switch + + // update the parentheses state + switch (_parenthesesState) { + case IgnoreParentheses: + break; + + case CountParentheses: + if (_tokenKind == T_RPAREN) { + --_parenthesesCount; + if (_parenthesesCount == 0) + _parenthesesState = BalancedParentheses; + } else if (_tokenKind == T_LPAREN) { + ++_parenthesesCount; + } + break; + + case BalancedParentheses: + _parenthesesState = IgnoreParentheses; + break; + } // switch + + return _tokenKind; +} + +bool Lexer::isUnicodeEscapeSequence(const QChar *chars) +{ + if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3])) + return true; + + return false; +} + +QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) +{ + if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) { + scanChar(); // skip u + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + const QChar c3 = _char; + scanChar(); + + const QChar c4 = _char; + scanChar(); + + if (ok) + *ok = true; + + return convertUnicode(c1, c2, c3, c4); + } + + *ok = false; + return QChar(); +} + +int Lexer::scanToken() +{ + if (_stackToken != -1) { + int tk = _stackToken; + _stackToken = -1; + return tk; + } + + _terminator = false; + +again: + _validTokenText = false; + _tokenLinePtr = _lastLinePtr; + + while (_char.isSpace()) { + if (_char == QLatin1Char('\n')) { + _tokenLinePtr = _codePtr; + + if (_restrictedKeyword) { + // automatic semicolon insertion + _tokenLine = _currentLineNumber; + _tokenStartPtr = _codePtr - 1; // ### TODO: insert it before the optional \r sequence. + return T_SEMICOLON; + } else { + _terminator = true; + syncProhibitAutomaticSemicolon(); + } + } + + scanChar(); + } + + _tokenStartPtr = _codePtr - 1; + _tokenLine = _currentLineNumber; + + if (_char.isNull()) + return EOF_SYMBOL; + + const QChar ch = _char; + scanChar(); + + switch (ch.unicode()) { + case '~': return T_TILDE; + case '}': return T_RBRACE; + + case '|': + if (_char == QLatin1Char('|')) { + scanChar(); + return T_OR_OR; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_OR_EQ; + } + return T_OR; + + case '{': return T_LBRACE; + + case '^': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_XOR_EQ; + } + return T_XOR; + + case ']': return T_RBRACKET; + case '[': return T_LBRACKET; + case '?': return T_QUESTION; + + case '>': + if (_char == QLatin1Char('>')) { + scanChar(); + if (_char == QLatin1Char('>')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_GT_GT_GT_EQ; + } + return T_GT_GT_GT; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_GT_GT_EQ; + } + return T_GT_GT; + } else if (_char == QLatin1Char('=')) { + scanChar(); + return T_GE; + } + return T_GT; + + case '=': + if (_char == QLatin1Char('=')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_EQ_EQ_EQ; + } + return T_EQ_EQ; + } + return T_EQ; + + case '<': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_LE; + } else if (_char == QLatin1Char('<')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_LT_LT_EQ; + } + return T_LT_LT; + } + return T_LT; + + case ';': return T_SEMICOLON; + case ':': return T_COLON; + + case '/': + if (_char == QLatin1Char('*')) { + scanChar(); + while (!_char.isNull()) { + if (_char == QLatin1Char('*')) { + scanChar(); + if (_char == QLatin1Char('/')) { + scanChar(); + + if (_engine) { + _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4, + tokenStartLine(), tokenStartColumn() + 2); + } + + goto again; + } + } else { + scanChar(); + } + } + } else if (_char == QLatin1Char('/')) { + while (!_char.isNull() && _char != QLatin1Char('\n')) { + scanChar(); + } + if (_engine) { + _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2, + tokenStartLine(), tokenStartColumn() + 2); + } + goto again; + } if (_char == QLatin1Char('=')) { + scanChar(); + return T_DIVIDE_EQ; + } + return T_DIVIDE_; + + case '.': + if (_char.isDigit()) { + QVarLengthArray<char,32> chars; + + chars.append(ch.unicode()); // append the `.' + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + + chars.append('\0'); + + const char *begin = chars.constData(); + const char *end = 0; + bool ok = false; + + _tokenValue = qstrtod(begin, &end, &ok); + + if (end - begin != chars.size() - 1) { + _errorCode = IllegalExponentIndicator; + _errorMessage = tr("QQmlParser", "Illegal syntax for exponential number"); + return T_ERROR; + } + + return T_NUMERIC_LITERAL; + } + return T_DOT; + + case '-': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_MINUS_EQ; + } else if (_char == QLatin1Char('-')) { + scanChar(); + + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + _stackToken = T_MINUS_MINUS; + return T_SEMICOLON; + } + + return T_MINUS_MINUS; + } + return T_MINUS; + + case ',': return T_COMMA; + + case '+': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_PLUS_EQ; + } else if (_char == QLatin1Char('+')) { + scanChar(); + + if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) { + _stackToken = T_PLUS_PLUS; + return T_SEMICOLON; + } + + return T_PLUS_PLUS; + } + return T_PLUS; + + case '*': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_STAR_EQ; + } + return T_STAR; + + case ')': return T_RPAREN; + case '(': return T_LPAREN; + + case '&': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_AND_EQ; + } else if (_char == QLatin1Char('&')) { + scanChar(); + return T_AND_AND; + } + return T_AND; + + case '%': + if (_char == QLatin1Char('=')) { + scanChar(); + return T_REMAINDER_EQ; + } + return T_REMAINDER; + + case '!': + if (_char == QLatin1Char('=')) { + scanChar(); + if (_char == QLatin1Char('=')) { + scanChar(); + return T_NOT_EQ_EQ; + } + return T_NOT_EQ; + } + return T_NOT; + + case '\'': + case '"': { + const QChar quote = ch; + bool multilineStringLiteral = false; + + const QChar *startCode = _codePtr; + + if (_engine) { + while (!_char.isNull()) { + if (_char == QLatin1Char('\n') || _char == QLatin1Char('\\')) { + break; + } else if (_char == quote) { + _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); + scanChar(); + + return T_STRING_LITERAL; + } + scanChar(); + } + } + + _validTokenText = true; + _tokenText.resize(0); + startCode--; + while (startCode != _codePtr - 1) + _tokenText += *startCode++; + + while (! _char.isNull()) { + if (_char == QLatin1Char('\n')) { + multilineStringLiteral = true; + _tokenText += _char; + scanChar(); + } else if (_char == quote) { + scanChar(); + + if (_engine) + _tokenSpell = _engine->newStringRef(_tokenText); + + return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL; + } else if (_char == QLatin1Char('\\')) { + scanChar(); + + QChar u; + bool ok = false; + + switch (_char.unicode()) { + // unicode escape sequence + case 'u': + u = decodeUnicodeEscapeCharacter(&ok); + if (! ok) + u = _char; + break; + + // hex escape sequence + case 'x': + case 'X': + if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { + scanChar(); + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + u = convertHex(c1, c2); + } else { + u = _char; + } + break; + + // single character escape sequence + case '\\': u = QLatin1Char('\\'); scanChar(); break; + case '\'': u = QLatin1Char('\''); scanChar(); break; + case '\"': u = QLatin1Char('\"'); scanChar(); break; + case 'b': u = QLatin1Char('\b'); scanChar(); break; + case 'f': u = QLatin1Char('\f'); scanChar(); break; + case 'n': u = QLatin1Char('\n'); scanChar(); break; + case 'r': u = QLatin1Char('\r'); scanChar(); break; + case 't': u = QLatin1Char('\t'); scanChar(); break; + case 'v': u = QLatin1Char('\v'); scanChar(); break; + + case '0': + if (! _codePtr[1].isDigit()) { + scanChar(); + u = QLatin1Char('\0'); + } else { + // ### parse deprecated octal escape sequence ? + u = _char; + } + break; + + case '\r': + while (_char == QLatin1Char('\r')) + scanChar(); + + if (_char == QLatin1Char('\n')) { + u = _char; + scanChar(); + } else { + u = QLatin1Char('\n'); + } + + break; + + case '\n': + u = _char; + scanChar(); + break; + + default: + // non escape character + u = _char; + scanChar(); + } + + _tokenText += u; + } else { + _tokenText += _char; + scanChar(); + } + } + + _errorCode = UnclosedStringLiteral; + _errorMessage = tr("QQmlParser", "Unclosed string at end of line"); + return T_ERROR; + } + + default: + if (ch.isLetter() || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) { + bool identifierWithEscapeChars = false; + if (ch == QLatin1Char('\\')) { + identifierWithEscapeChars = true; + _tokenText.resize(0); + bool ok = false; + _tokenText += decodeUnicodeEscapeCharacter(&ok); + _validTokenText = true; + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = tr("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } + } + while (true) { + if (_char.isLetterOrNumber() || _char == QLatin1Char('$') || _char == QLatin1Char('_')) { + if (identifierWithEscapeChars) + _tokenText += _char; + + scanChar(); + } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { + if (! identifierWithEscapeChars) { + identifierWithEscapeChars = true; + _tokenText.resize(0); + _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1); + _validTokenText = true; + } + + scanChar(); // skip '\\' + bool ok = false; + _tokenText += decodeUnicodeEscapeCharacter(&ok); + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = tr("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } + } else { + _tokenLength = _codePtr - _tokenStartPtr - 1; + + int kind = T_IDENTIFIER; + + if (! identifierWithEscapeChars) + kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); + + if (_engine) { + if (kind == T_IDENTIFIER && identifierWithEscapeChars) + _tokenSpell = _engine->newStringRef(_tokenText); + else + _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); + } + + return kind; + } + } + } else if (ch.isDigit()) { + if (ch != QLatin1Char('0')) { + double integer = ch.unicode() - '0'; + + QChar n = _char; + const QChar *code = _codePtr; + while (n.isDigit()) { + integer = integer * 10 + (n.unicode() - '0'); + n = *code++; + } + + if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) { + if (code != _codePtr) { + _codePtr = code - 1; + scanChar(); + } + _tokenValue = integer; + return T_NUMERIC_LITERAL; + } + } + + QVarLengthArray<char,32> chars; + chars.append(ch.unicode()); + + if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) { + // parse hex integer literal + + chars.append(_char.unicode()); + scanChar(); // consume `x' + + while (isHexDigit(_char)) { + chars.append(_char.unicode()); + scanChar(); + } + + _tokenValue = integerFromString(chars.constData(), chars.size(), 16); + return T_NUMERIC_LITERAL; + } + + // decimal integer literal + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); // consume the digit + } + + if (_char == QLatin1Char('.')) { + chars.append(_char.unicode()); + scanChar(); // consume `.' + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && + _codePtr[1].isDigit())) { + + chars.append(_char.unicode()); + scanChar(); // consume `e' + + if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { + chars.append(_char.unicode()); + scanChar(); // consume the sign + } + + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } + } + } + + chars.append('\0'); + + const char *begin = chars.constData(); + const char *end = 0; + bool ok = false; + + _tokenValue = qstrtod(begin, &end, &ok); + + if (end - begin != chars.size() - 1) { + _errorCode = IllegalExponentIndicator; + _errorMessage = tr("QQmlParser", "Illegal syntax for exponential number"); + return T_ERROR; + } + + return T_NUMERIC_LITERAL; + } + + break; + } + + return T_ERROR; +} + +bool Lexer::scanRegExp(RegExpBodyPrefix prefix) +{ + _tokenText.resize(0); + _validTokenText = true; + _patternFlags = 0; + + if (prefix == EqualPrefix) + _tokenText += QLatin1Char('='); + + while (true) { + switch (_char.unicode()) { + case 0: // eof + case '\n': case '\r': // line terminator + _errorMessage = tr("QQmlParser", "Unterminated regular expression literal"); + return false; + + case '/': + scanChar(); + + // scan the flags + _patternFlags = 0; + while (isIdentLetter(_char)) { + int flag = regExpFlagFromChar(_char); + if (flag == 0) { + _errorMessage = tr("QQmlParser", "Invalid regular expression flag '%0'") + .arg(QChar(_char)); + return false; + } + _patternFlags |= flag; + scanChar(); + } + + _tokenLength = _codePtr - _tokenStartPtr - 1; + return true; + + case '\\': + // regular expression backslash sequence + _tokenText += _char; + scanChar(); + + if (_char.isNull() || isLineTerminator()) { + _errorMessage = tr("QQmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + _tokenText += _char; + scanChar(); + break; + + case '[': + // regular expression class + _tokenText += _char; + scanChar(); + + while (! _char.isNull() && ! isLineTerminator()) { + if (_char == QLatin1Char(']')) + break; + else if (_char == QLatin1Char('\\')) { + // regular expression backslash sequence + _tokenText += _char; + scanChar(); + + if (_char.isNull() || isLineTerminator()) { + _errorMessage = tr("QQmlParser", "Unterminated regular expression backslash sequence"); + return false; + } + + _tokenText += _char; + scanChar(); + } else { + _tokenText += _char; + scanChar(); + } + } + + if (_char != QLatin1Char(']')) { + _errorMessage = tr("QQmlParser", "Unterminated regular expression class"); + return false; + } + + _tokenText += _char; + scanChar(); // skip ] + break; + + default: + _tokenText += _char; + scanChar(); + } // switch + } // while + + return false; +} + +bool Lexer::isLineTerminator() const +{ + return (_char == QLatin1Char('\n') || _char == QLatin1Char('\r')); +} + +bool Lexer::isIdentLetter(QChar ch) +{ + // ASCII-biased, since all reserved words are ASCII, aand hence the + // bulk of content to be parsed. + if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) + || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z')) + || ch == QLatin1Char('$') + || ch == QLatin1Char('_')) + return true; + if (ch.unicode() < 128) + return false; + return ch.isLetterOrNumber(); +} + +bool Lexer::isDecimalDigit(ushort c) +{ + return (c >= '0' && c <= '9'); +} + +bool Lexer::isHexDigit(QChar c) +{ + return ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) + || (c >= QLatin1Char('a') && c <= QLatin1Char('f')) + || (c >= QLatin1Char('A') && c <= QLatin1Char('F'))); +} + +bool Lexer::isOctalDigit(ushort c) +{ + return (c >= '0' && c <= '7'); +} + +int Lexer::tokenKind() const +{ + return _tokenKind; +} + +int Lexer::tokenOffset() const +{ + return _tokenStartPtr - _code.unicode(); +} + +int Lexer::tokenLength() const +{ + return _tokenLength; +} + +int Lexer::tokenStartLine() const +{ + return _tokenLine; +} + +int Lexer::tokenStartColumn() const +{ + return _tokenStartPtr - _tokenLinePtr + 1; +} + +int Lexer::tokenEndLine() const +{ + return _currentLineNumber; +} + +int Lexer::tokenEndColumn() const +{ + return _codePtr - _lastLinePtr; +} + +QStringRef Lexer::tokenSpell() const +{ + return _tokenSpell; +} + +double Lexer::tokenValue() const +{ + return _tokenValue; +} + +QString Lexer::tokenText() const +{ + if (_validTokenText) + return _tokenText; + + if (_tokenKind == T_STRING_LITERAL) + return QString(_tokenStartPtr + 1, _tokenLength - 2); + + return QString(_tokenStartPtr, _tokenLength); +} + +Lexer::Error Lexer::errorCode() const +{ + return _errorCode; +} + +QString Lexer::errorMessage() const +{ + return _errorMessage; +} + +void Lexer::syncProhibitAutomaticSemicolon() +{ + if (_parenthesesState == BalancedParentheses) { + // we have seen something like "if (foo)", which means we should + // never insert an automatic semicolon at this point, since it would + // then be expanded into an empty statement (ECMA-262 7.9.1) + _prohibitAutomaticSemicolon = true; + _parenthesesState = IgnoreParentheses; + } else { + _prohibitAutomaticSemicolon = false; + } +} + +bool Lexer::prevTerminator() const +{ + return _terminator; +} + +bool Lexer::followsClosingBrace() const +{ + return _followsClosingBrace; +} + +bool Lexer::canInsertAutomaticSemicolon(int token) const +{ + return token == T_RBRACE + || token == EOF_SYMBOL + || _terminator + || _followsClosingBrace; +} + +bool Lexer::scanDirectives(Directives *directives) +{ + if (_qmlMode) { + // the directives are a Javascript-only extension. + return false; + } + + lex(); // fetch the first token + + if (_tokenKind != T_DOT) + return true; + + do { + lex(); // skip T_DOT + + const int lineNumber = tokenStartLine(); + + if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD)) + return false; // expected a valid QML/JS directive + + const QString directiveName = tokenText(); + + if (! (directiveName == QLatin1String("pragma") || + directiveName == QLatin1String("import"))) + return false; // not a valid directive name + + // it must be a pragma or an import directive. + if (directiveName == QLatin1String("pragma")) { + // .pragma library + if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library"))) + return false; // expected `library + + // we found a .pragma library directive + directives->pragmaLibrary(); + + } else { + Q_ASSERT(directiveName == QLatin1String("import")); + lex(); // skip .import + + QString pathOrUri; + QString version; + bool fileImport = false; // file or uri import + + if (_tokenKind == T_STRING_LITERAL) { + // .import T_STRING_LITERAL as T_IDENTIFIER + + fileImport = true; + pathOrUri = tokenText(); + + } else if (_tokenKind == T_IDENTIFIER) { + // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER + + pathOrUri = tokenText(); + + lex(); // skip the first T_IDENTIFIER + for (; _tokenKind == T_DOT; lex()) { + if (lex() != T_IDENTIFIER) + return false; + + pathOrUri += QLatin1Char('.'); + pathOrUri += tokenText(); + } + + if (_tokenKind != T_NUMERIC_LITERAL) + return false; // expected the module version number + + version = tokenText(); + } + + // + // recognize the mandatory `as' followed by the module name + // + if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as"))) + return false; // expected `as' + + if (lex() != T_IDENTIFIER) + return false; // expected module name + + const QString module = tokenText(); + + if (fileImport) + directives->importFile(pathOrUri, module); + else + directives->importModule(pathOrUri, version, module); + } + + if (tokenStartLine() != lineNumber) + return false; // the directives cannot span over multiple lines + + // fetch the first token after the .pragma/.import directive + lex(); + } while (_tokenKind == T_DOT); + + return true; +} + +#include "qqmljskeywords_p.h" diff --git a/src/tools/qdoc/qmlparser/qqmljslexer_p.h b/src/tools/qdoc/qmlparser/qqmljslexer_p.h new file mode 100644 index 0000000000..6b51852f5f --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljslexer_p.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSLEXER_P_H +#define QQMLJSLEXER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Directives { +public: + virtual ~Directives() {} + + virtual void pragmaLibrary() + { + } + + virtual void importFile(const QString &jsfile, const QString &module) + { + Q_UNUSED(jsfile); + Q_UNUSED(module); + } + + virtual void importModule(const QString &uri, const QString &version, const QString &module) + { + Q_UNUSED(uri); + Q_UNUSED(version); + Q_UNUSED(module); + } +}; + +class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar +{ +public: + enum { + T_ABSTRACT = T_RESERVED_WORD, + T_BOOLEAN = T_RESERVED_WORD, + T_BYTE = T_RESERVED_WORD, + T_CHAR = T_RESERVED_WORD, + T_CLASS = T_RESERVED_WORD, + T_DOUBLE = T_RESERVED_WORD, + T_ENUM = T_RESERVED_WORD, + T_EXPORT = T_RESERVED_WORD, + T_EXTENDS = T_RESERVED_WORD, + T_FINAL = T_RESERVED_WORD, + T_FLOAT = T_RESERVED_WORD, + T_GOTO = T_RESERVED_WORD, + T_IMPLEMENTS = T_RESERVED_WORD, + T_INT = T_RESERVED_WORD, + T_INTERFACE = T_RESERVED_WORD, + T_LET = T_RESERVED_WORD, + T_LONG = T_RESERVED_WORD, + T_NATIVE = T_RESERVED_WORD, + T_PACKAGE = T_RESERVED_WORD, + T_PRIVATE = T_RESERVED_WORD, + T_PROTECTED = T_RESERVED_WORD, + T_SHORT = T_RESERVED_WORD, + T_STATIC = T_RESERVED_WORD, + T_SUPER = T_RESERVED_WORD, + T_SYNCHRONIZED = T_RESERVED_WORD, + T_THROWS = T_RESERVED_WORD, + T_TRANSIENT = T_RESERVED_WORD, + T_VOLATILE = T_RESERVED_WORD, + T_YIELD = T_RESERVED_WORD + }; + + enum Error { + NoError, + IllegalCharacter, + UnclosedStringLiteral, + IllegalEscapeSequence, + IllegalUnicodeEscapeSequence, + UnclosedComment, + IllegalExponentIndicator, + IllegalIdentifier + }; + + enum RegExpBodyPrefix { + NoPrefix, + EqualPrefix + }; + + enum RegExpFlag { + RegExp_Global = 0x01, + RegExp_IgnoreCase = 0x02, + RegExp_Multiline = 0x04 + }; + +public: + Lexer(Engine *engine); + + bool qmlMode() const; + + QString code() const; + void setCode(const QString &code, int lineno, bool qmlMode = true); + + int lex(); + + bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix); + bool scanDirectives(Directives *directives); + + int regExpFlags() const { return _patternFlags; } + QString regExpPattern() const { return _tokenText; } + + int tokenKind() const; + int tokenOffset() const; + int tokenLength() const; + + int tokenStartLine() const; + int tokenStartColumn() const; + + int tokenEndLine() const; + int tokenEndColumn() const; + + QStringRef tokenSpell() const; + double tokenValue() const; + QString tokenText() const; + + Error errorCode() const; + QString errorMessage() const; + + bool prevTerminator() const; + bool followsClosingBrace() const; + bool canInsertAutomaticSemicolon(int token) const; + + enum ParenthesesState { + IgnoreParentheses, + CountParentheses, + BalancedParentheses + }; + +protected: + int classify(const QChar *s, int n, bool qmlMode); + +private: + inline void scanChar(); + int scanToken(); + + bool isLineTerminator() const; + static bool isIdentLetter(QChar c); + static bool isDecimalDigit(ushort c); + static bool isHexDigit(QChar c); + static bool isOctalDigit(ushort c); + static bool isUnicodeEscapeSequence(const QChar *chars); + + void syncProhibitAutomaticSemicolon(); + QChar decodeUnicodeEscapeCharacter(bool *ok); + +private: + Engine *_engine; + + QString _code; + QString _tokenText; + QString _errorMessage; + QStringRef _tokenSpell; + + const QChar *_codePtr; + const QChar *_lastLinePtr; + const QChar *_tokenLinePtr; + const QChar *_tokenStartPtr; + + QChar _char; + Error _errorCode; + + int _currentLineNumber; + double _tokenValue; + + // parentheses state + ParenthesesState _parenthesesState; + int _parenthesesCount; + + int _stackToken; + + int _patternFlags; + int _tokenKind; + int _tokenLength; + int _tokenLine; + + bool _validTokenText; + bool _prohibitAutomaticSemicolon; + bool _restrictedKeyword; + bool _terminator; + bool _followsClosingBrace; + bool _delimited; + bool _qmlMode; +}; + +} // end of namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif // LEXER_H diff --git a/src/tools/qdoc/qmlparser/qqmljsmemorypool_p.h b/src/tools/qdoc/qmlparser/qqmljsmemorypool_p.h new file mode 100644 index 0000000000..fd52fd25e4 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsmemorypool_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + +#ifndef QQMLJSMEMORYPOOL_P_H +#define QQMLJSMEMORYPOOL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmljsglobal_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qdebug.h> + +#include <cstring> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class QML_PARSER_EXPORT MemoryPool : public QSharedData +{ + MemoryPool(const MemoryPool &other); + void operator =(const MemoryPool &other); + +public: + MemoryPool() + : _blocks(0), + _allocatedBlocks(0), + _blockCount(-1), + _ptr(0), + _end(0) + { } + + ~MemoryPool() + { + if (_blocks) { + for (int i = 0; i < _allocatedBlocks; ++i) { + if (char *b = _blocks[i]) + qFree(b); + } + + qFree(_blocks); + } + } + + inline void *allocate(size_t size) + { + size = (size + 7) & ~7; + if (_ptr && (_ptr + size < _end)) { + void *addr = _ptr; + _ptr += size; + return addr; + } + return allocate_helper(size); + } + + void reset() + { + _blockCount = -1; + _ptr = _end = 0; + } + +private: + void *allocate_helper(size_t size) + { + Q_ASSERT(size < BLOCK_SIZE); + + if (++_blockCount == _allocatedBlocks) { + if (! _allocatedBlocks) + _allocatedBlocks = DEFAULT_BLOCK_COUNT; + else + _allocatedBlocks *= 2; + + _blocks = (char **) qRealloc(_blocks, sizeof(char *) * _allocatedBlocks); + + for (int index = _blockCount; index < _allocatedBlocks; ++index) + _blocks[index] = 0; + } + + char *&block = _blocks[_blockCount]; + + if (! block) + block = (char *) qMalloc(BLOCK_SIZE); + + _ptr = block; + _end = _ptr + BLOCK_SIZE; + + void *addr = _ptr; + _ptr += size; + return addr; + } + +private: + char **_blocks; + int _allocatedBlocks; + int _blockCount; + char *_ptr; + char *_end; + + enum + { + BLOCK_SIZE = 8 * 1024, + DEFAULT_BLOCK_COUNT = 8 + }; +}; + +class QML_PARSER_EXPORT Managed +{ + Managed(const Managed &other); + void operator = (const Managed &other); + +public: + Managed() {} + ~Managed() {} + + void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); } + void operator delete(void *) {} + void operator delete(void *, MemoryPool *) {} +}; + +} // namespace QQmlJS + +QT_QML_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/qmlparser/qqmljsparser.cpp b/src/tools/qdoc/qmlparser/qqmljsparser.cpp new file mode 100644 index 0000000000..431351b8b2 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsparser.cpp @@ -0,0 +1,1817 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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 <QtCore/QtDebug> +#ifdef QT_BOOTSTRAPPED +#define tr(x, y) QString(QLatin1String(y)) +#else +#include <QtCore/QCoreApplication> +#define tr(x, y) QCoreApplication::translate(x, y) +#endif + +#include <string.h> + +#include "qqmljsengine_p.h" +#include "qqmljslexer_p.h" +#include "qqmljsast_p.h" +#include "qqmljsmemorypool_p.h" + + + +#include "qqmljsparser_p.h" +#include <QVarLengthArray> + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +using namespace QQmlJS; + +QT_QML_BEGIN_NAMESPACE + +void Parser::reallocateStack() +{ + if (! stack_size) + stack_size = 128; + else + stack_size <<= 1; + + sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef))); +} + +Parser::Parser(Engine *engine): + driver(engine), + pool(engine->pool()), + tos(0), + stack_size(0), + sym_stack(0), + state_stack(0), + location_stack(0), + string_stack(0), + first_token(0), + last_token(0) +{ +} + +Parser::~Parser() +{ + if (stack_size) { + free(sym_stack); + free(state_stack); + free(location_stack); + free(string_stack); + } +} + +static inline AST::SourceLocation location(Lexer *lexer) +{ + AST::SourceLocation loc; + loc.offset = lexer->tokenOffset(); + loc.length = lexer->tokenLength(); + loc.startLine = lexer->tokenStartLine(); + loc.startColumn = lexer->tokenStartColumn(); + return loc; +} + +AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr) +{ + QVarLengthArray<QStringRef, 4> nameIds; + QVarLengthArray<AST::SourceLocation, 4> locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) { + AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name); + q->identifierToken = idExpr->identifierToken; + + AST::UiQualifiedId *currentId = q; + for (int i = nameIds.size() - 1; i != -1; --i) { + currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]); + currentId->identifierToken = locations[i]; + } + + return currentId->finish(); + } + + return 0; +} + +bool Parser::parse(int startToken) +{ + Lexer *lexer = driver->lexer(); + bool hadErrors = false; + int yytoken = -1; + int action = 0; + + token_buffer[0].token = startToken; + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + tos = -1; + program = 0; + + do { + if (++tos == stack_size) + reallocateStack(); + + state_stack[tos] = action; + + _Lcheck_token: + if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) { + yyprevlloc = yylloc; + + if (first_token == last_token) { + yytoken = lexer->lex(); + yylval = lexer->tokenValue(); + yytokenspell = lexer->tokenSpell(); + yylloc = location(lexer); + } else { + yytoken = first_token->token; + yylval = first_token->dval; + yytokenspell = first_token->spell; + yylloc = first_token->loc; + ++first_token; + } + } + + action = t_action(action, yytoken); + if (action > 0) { + if (action != ACCEPT_STATE) { + yytoken = -1; + sym(1).dval = yylval; + stringRef(1) = yytokenspell; + loc(1) = yylloc; + } else { + --tos; + return ! hadErrors; + } + } else if (action < 0) { + const int r = -action - 1; + tos -= rhs[r]; + + switch (r) { + +case 0: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 1: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 2: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 3: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 4: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 5: { + sym(1).Node = sym(2).Node; + program = sym(1).Node; +} break; + +case 6: { + sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList, + sym(2).UiObjectMemberList->finish()); +} break; + +case 8: { + sym(1).Node = sym(1).UiImportList->finish(); +} break; + +case 9: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport); +} break; + +case 10: { + sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport); +} break; + +case 13: { + sym(1).UiImport->semicolonToken = loc(2); +} break; + +case 15: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->semicolonToken = loc(3); +} break; + +case 17: { + sym(1).UiImport->versionToken = loc(2); + sym(1).UiImport->asToken = loc(3); + sym(1).UiImport->importIdToken = loc(4); + sym(1).UiImport->importId = stringRef(4); + sym(1).UiImport->semicolonToken = loc(5); +} break; + +case 19: { + sym(1).UiImport->asToken = loc(2); + sym(1).UiImport->importIdToken = loc(3); + sym(1).UiImport->importId = stringRef(3); + sym(1).UiImport->semicolonToken = loc(4); +} break; + +case 20: { + AST::UiImport *node = 0; + + if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) { + node = new (pool) AST::UiImport(importIdLiteral->value); + node->fileNameToken = loc(2); + } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) { + node = new (pool) AST::UiImport(qualifiedId); + node->fileNameToken = loc(2); + } + + sym(1).Node = node; + + if (node) { + node->importToken = loc(1); + } else { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id or a string literal"))); + + return false; // ### remove me + } +} break; + +case 21: { + sym(1).Node = 0; +} break; + +case 22: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; + +case 23: { + sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember); +} break; + +case 24: { + AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList( + sym(1).UiObjectMemberList, sym(2).UiObjectMember); + sym(1).Node = node; +} break; + +case 25: { + sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember); +} break; + +case 26: { + AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList( + sym(1).UiArrayMemberList, sym(3).UiObjectMember); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 27: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0); + node->lbraceToken = loc(1); + node->rbraceToken = loc(2); + sym(1).Node = node; +} break; + +case 28: { + AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 29: { + AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, + sym(2).UiObjectInitializer); + sym(1).Node = node; +} break; + +case 31: { + AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding( + sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish()); + node->colonToken = loc(2); + node->lbracketToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 32: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 33: { + AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding( + sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer); + node->colonToken = loc(2); + node->hasOnToken = true; + sym(1).Node = node; +} break; + +case 41: +{ + AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding( + sym(1).UiQualifiedId, sym(3).Statement); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 45: { + sym(1).Node = 0; +} break; + +case 46: { + sym(1).Node = sym(1).UiParameterList->finish (); +} break; + +case 47: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2)); + node->propertyTypeToken = loc(1); + node->identifierToken = loc(2); + sym(1).Node = node; +} break; + +case 48: { + AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4)); + node->commaToken = loc(2); + node->identifierToken = loc(4); + sym(1).Node = node; +} break; + +case 50: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->parameters = sym(4).UiParameterList; + node->semicolonToken = loc(6); + sym(1).Node = node; +} break; + +case 52: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2)); + node->type = AST::UiPublicMember::Signal; + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 54: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 56: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); + sym(1).Node = node; +} break; + +case 58: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4)); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->semicolonToken = loc(5); + sym(1).Node = node; +} break; + +case 59: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3), + sym(5).Statement); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 60: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 61: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4), + sym(6).Statement); + node->isDefaultMember = true; + node->defaultToken = loc(1); + node->propertyToken = loc(2); + node->typeToken = loc(3); + node->identifierToken = loc(4); + node->colonToken = loc(5); + sym(1).Node = node; +} break; + +case 62: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6)); + node->typeModifier = stringRef(2); + node->propertyToken = loc(1); + node->typeModifierToken = loc(2); + node->typeToken = loc(4); + node->identifierToken = loc(6); + node->semicolonToken = loc(7); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6)); + propertyName->identifierToken = loc(6); + propertyName->next = 0; + + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding( + propertyName, sym(9).UiArrayMemberList->finish()); + binding->colonToken = loc(7); + binding->lbracketToken = loc(8); + binding->rbracketToken = loc(10); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 63: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3)); + node->propertyToken = loc(1); + node->typeToken = loc(2); + node->identifierToken = loc(3); + node->semicolonToken = loc(4); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3)); + propertyName->identifierToken = loc(3); + propertyName->next = 0; + + AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding( + propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer); + binding->colonToken = loc(4); + + node->binding = binding; + + sym(1).Node = node; +} break; + +case 64: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; + +case 65: { + sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); +} break; + +case 71: { + AST::ThisExpression *node = new (pool) AST::ThisExpression(); + node->thisToken = loc(1); + sym(1).Node = node; +} break; + +case 72: { + AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 73: { + AST::NullExpression *node = new (pool) AST::NullExpression(); + node->nullToken = loc(1); + sym(1).Node = node; +} break; + +case 74: { + AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); + node->trueToken = loc(1); + sym(1).Node = node; +} break; + +case 75: { + AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); + node->falseToken = loc(1); + sym(1).Node = node; +} break; + +case 76: { + AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); + node->literalToken = loc(1); + sym(1).Node = node; +} break; +case 77: +case 78: { + AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 79: { + bool rx = lexer->scanRegExp(Lexer::NoPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; // ### remove me + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 80: { + bool rx = lexer->scanRegExp(Lexer::EqualPrefix); + if (!rx) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); + return false; + } + + loc(1).length = lexer->tokenLength(); + yylloc = loc(1); // adjust the location of the current token + + AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral( + driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags()); + node->literalToken = loc(1); + sym(1).Node = node; +} break; + +case 81: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); + node->lbracketToken = loc(1); + node->rbracketToken = loc(2); + sym(1).Node = node; +} break; + +case 82: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 83: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); + node->lbracketToken = loc(1); + node->rbracketToken = loc(3); + sym(1).Node = node; +} break; + +case 84: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + (AST::Elision *) 0); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 85: { + AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), + sym(4).Elision->finish()); + node->lbracketToken = loc(1); + node->commaToken = loc(3); + node->rbracketToken = loc(5); + sym(1).Node = node; +} break; + +case 86: { + AST::ObjectLiteral *node = 0; + if (sym(2).Node) + node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + else + node = new (pool) AST::ObjectLiteral(); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 87: { + AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( + sym(2).PropertyNameAndValueList->finish ()); + node->lbraceToken = loc(1); + node->rbraceToken = loc(4); + sym(1).Node = node; +} break; + +case 88: { + AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); + node->lparenToken = loc(1); + node->rparenToken = loc(3); + sym(1).Node = node; +} break; + +case 89: { + if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) { + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, + QLatin1String("Ignored annotation"))); + + sym(1).Expression = mem->base; + } + + if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) { + sym(1).UiQualifiedId = qualifiedId; + } else { + sym(1).UiQualifiedId = 0; + + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1), + QLatin1String("Expected a qualified name id"))); + + return false; // ### recover + } +} break; + +case 90: { + sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); +} break; + +case 91: { + sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); +} break; + +case 92: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, + (AST::Elision *) 0, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 93: { + AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), + sym(4).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 94: { + AST::Elision *node = new (pool) AST::Elision(); + node->commaToken = loc(1); + sym(1).Node = node; +} break; + +case 95: { + AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 96: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyName, sym(3).Expression); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 97: { + AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); + node->commaToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 98: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; +case 99: +case 100: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 101: { + AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 102: { + AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 103: { + AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); + node->propertyNameToken = loc(1); + sym(1).Node = node; +} break; + +case 139: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 140: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 141: { + AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); + node->newToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + sym(1).Node = node; +} break; + +case 143: { + AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); + node->newToken = loc(1); + sym(1).Node = node; +} break; + +case 144: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 145: { + AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 146: { + AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); + node->lbracketToken = loc(2); + node->rbracketToken = loc(4); + sym(1).Node = node; +} break; + +case 147: { + AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); + node->dotToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 148: { + sym(1).Node = 0; +} break; + +case 149: { + sym(1).Node = sym(1).ArgumentList->finish(); +} break; + +case 150: { + sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); +} break; + +case 151: { + AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 155: { + AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); + node->incrementToken = loc(2); + sym(1).Node = node; +} break; + +case 156: { + AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); + node->decrementToken = loc(2); + sym(1).Node = node; +} break; + +case 158: { + AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); + node->deleteToken = loc(1); + sym(1).Node = node; +} break; + +case 159: { + AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); + node->voidToken = loc(1); + sym(1).Node = node; +} break; + +case 160: { + AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); + node->typeofToken = loc(1); + sym(1).Node = node; +} break; + +case 161: { + AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); + node->incrementToken = loc(1); + sym(1).Node = node; +} break; + +case 162: { + AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); + node->decrementToken = loc(1); + sym(1).Node = node; +} break; + +case 163: { + AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); + node->plusToken = loc(1); + sym(1).Node = node; +} break; + +case 164: { + AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); + node->minusToken = loc(1); + sym(1).Node = node; +} break; + +case 165: { + AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); + node->tildeToken = loc(1); + sym(1).Node = node; +} break; + +case 166: { + AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); + node->notToken = loc(1); + sym(1).Node = node; +} break; + +case 168: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mul, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 169: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Div, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 170: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Mod, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 172: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Add, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 173: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Sub, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 175: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::LShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 176: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::RShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 177: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::URShift, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 179: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 180: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 181: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 182: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 183: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 184: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::In, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 186: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Lt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 187: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Gt, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 188: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Le, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 189: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Ge, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 190: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::InstanceOf, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 192: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 193: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 194: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 195: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 197: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Equal, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 198: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::NotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 199: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 200: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::StrictNotEqual, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 202: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 204: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitAnd, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 206: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 208: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitXor, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 210: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 212: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::BitOr, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 214: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 216: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::And, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 218: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 220: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + QSOperator::Or, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 222: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 224: { + AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, + sym(3).Expression, sym(5).Expression); + node->questionToken = loc(2); + node->colonToken = loc(4); + sym(1).Node = node; +} break; + +case 226: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 228: { + AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, + sym(2).ival, sym(3).Expression); + node->operatorToken = loc(2); + sym(1).Node = node; +} break; + +case 229: { + sym(1).ival = QSOperator::Assign; +} break; + +case 230: { + sym(1).ival = QSOperator::InplaceMul; +} break; + +case 231: { + sym(1).ival = QSOperator::InplaceDiv; +} break; + +case 232: { + sym(1).ival = QSOperator::InplaceMod; +} break; + +case 233: { + sym(1).ival = QSOperator::InplaceAdd; +} break; + +case 234: { + sym(1).ival = QSOperator::InplaceSub; +} break; + +case 235: { + sym(1).ival = QSOperator::InplaceLeftShift; +} break; + +case 236: { + sym(1).ival = QSOperator::InplaceRightShift; +} break; + +case 237: { + sym(1).ival = QSOperator::InplaceURightShift; +} break; + +case 238: { + sym(1).ival = QSOperator::InplaceAnd; +} break; + +case 239: { + sym(1).ival = QSOperator::InplaceXor; +} break; + +case 240: { + sym(1).ival = QSOperator::InplaceOr; +} break; + +case 242: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 243: { + sym(1).Node = 0; +} break; + +case 246: { + AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 247: { + sym(1).Node = 0; +} break; + +case 264: { + AST::Block *node = new (pool) AST::Block(sym(2).StatementList); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 265: { + sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); +} break; + +case 266: { + sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); +} break; + +case 267: { + sym(1).Node = 0; +} break; + +case 268: { + sym(1).Node = sym(1).StatementList->finish (); +} break; + +case 270: { + AST::VariableStatement *node = new (pool) AST::VariableStatement( + sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + node->declarationKindToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 271: { + sym(1).ival = T_CONST; +} break; + +case 272: { + sym(1).ival = T_VAR; +} break; + +case 273: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; + +case 274: { + AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( + sym(1).VariableDeclarationList, sym(3).VariableDeclaration); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 275: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); +} break; + +case 276: { + sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); +} break; + +case 277: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 278: { + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 279: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 280: { + sym(1).Node = 0; +} break; + +case 282: { + // ### TODO: AST for initializer + sym(1) = sym(2); +} break; + +case 283: { + sym(1).Node = 0; +} break; + +case 285: { + AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); + node->semicolonToken = loc(1); + sym(1).Node = node; +} break; + +case 287: { + AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 288: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->elseToken = loc(6); + sym(1).Node = node; +} break; + +case 289: { + AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); + node->ifToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 291: { + AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); + node->doToken = loc(1); + node->whileToken = loc(3); + node->lparenToken = loc(4); + node->rparenToken = loc(6); + node->semicolonToken = loc(7); + sym(1).Node = node; +} break; + +case 292: { + AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); + node->whileToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 293: { + AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, + sym(5).Expression, sym(7).Expression, sym(9).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->firstSemicolonToken = loc(4); + node->secondSemicolonToken = loc(6); + node->rparenToken = loc(8); + sym(1).Node = node; +} break; + +case 294: { + AST::LocalForStatement *node = new (pool) AST::LocalForStatement( + sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(8).Expression, sym(10).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->firstSemicolonToken = loc(5); + node->secondSemicolonToken = loc(7); + node->rparenToken = loc(9); + sym(1).Node = node; +} break; + +case 295: { + AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, + sym(5).Expression, sym(7).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->inToken = loc(4); + node->rparenToken = loc(6); + sym(1).Node = node; +} break; + +case 296: { + AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( + sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); + node->forToken = loc(1); + node->lparenToken = loc(2); + node->varToken = loc(3); + node->inToken = loc(5); + node->rparenToken = loc(7); + sym(1).Node = node; +} break; + +case 298: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); + node->continueToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 300: { + AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); + node->continueToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 302: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); + node->breakToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 304: { + AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); + node->breakToken = loc(1); + node->identifierToken = loc(2); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 306: { + AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); + node->returnToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 307: { + AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); + node->withToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 308: { + AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); + node->switchToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 309: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(3); + sym(1).Node = node; +} break; + +case 310: { + AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); + node->lbraceToken = loc(1); + node->rbraceToken = loc(5); + sym(1).Node = node; +} break; + +case 311: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); +} break; + +case 312: { + sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); +} break; + +case 313: { + sym(1).Node = 0; +} break; + +case 314: { + sym(1).Node = sym(1).CaseClauses->finish (); +} break; + +case 315: { + AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); + node->caseToken = loc(1); + node->colonToken = loc(3); + sym(1).Node = node; +} break; + +case 316: { + AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); + node->defaultToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; +case 317: +case 318: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 319: { + AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); + node->identifierToken = loc(1); + node->colonToken = loc(2); + sym(1).Node = node; +} break; + +case 321: { + AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); + node->throwToken = loc(1); + node->semicolonToken = loc(3); + sym(1).Node = node; +} break; + +case 322: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 323: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 324: { + AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); + node->tryToken = loc(1); + sym(1).Node = node; +} break; + +case 325: { + AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); + node->catchToken = loc(1); + node->lparenToken = loc(2); + node->identifierToken = loc(3); + node->rparenToken = loc(4); + sym(1).Node = node; +} break; + +case 326: { + AST::Finally *node = new (pool) AST::Finally(sym(2).Block); + node->finallyToken = loc(1); + sym(1).Node = node; +} break; + +case 328: { + AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); + node->debuggerToken = loc(1); + node->semicolonToken = loc(2); + sym(1).Node = node; +} break; + +case 329: { + AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 330: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); + node->functionToken = loc(1); + if (! stringRef(2).isNull()) + node->identifierToken = loc(2); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); + sym(1).Node = node; +} break; + +case 331: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); + node->identifierToken = loc(1); + sym(1).Node = node; +} break; + +case 332: { + AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); + node->commaToken = loc(2); + node->identifierToken = loc(3); + sym(1).Node = node; +} break; + +case 333: { + sym(1).Node = 0; +} break; + +case 334: { + sym(1).Node = sym(1).FormalParameterList->finish (); +} break; + +case 335: { + sym(1).Node = 0; +} break; + +case 337: { + sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); +} break; + +case 339: { + sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); +} break; + +case 340: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); +} break; + +case 341: { + sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); +} break; + +case 342: { + sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); +} break; + +case 343: { + sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); +} break; + +case 344: { + stringRef(1) = QStringRef(); +} break; + +case 346: { + sym(1).Node = 0; +} break; + + } // switch + action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT); + } // if + } while (action != 0); + + if (first_token == last_token) { + const int errorState = state_stack[tos]; + + // automatic insertion of `;' + if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) { + SavedToken &tk = token_buffer[0]; + tk.token = yytoken; + tk.dval = yylval; + tk.spell = yytokenspell; + tk.loc = yylloc; + + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; + + //const QString msg = tr("QQmlParser", "Missing `;'"); + //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg)); + + first_token = &token_buffer[0]; + last_token = &token_buffer[1]; + + yytoken = T_SEMICOLON; + yylval = 0; + + action = errorState; + + goto _Lcheck_token; + } + + hadErrors = true; + + token_buffer[0].token = yytoken; + token_buffer[0].dval = yylval; + token_buffer[0].spell = yytokenspell; + token_buffer[0].loc = yylloc; + + token_buffer[1].token = yytoken = lexer->lex(); + token_buffer[1].dval = yylval = lexer->tokenValue(); + token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); + token_buffer[1].loc = yylloc = location(lexer); + + if (t_action(errorState, yytoken)) { + QString msg; + int token = token_buffer[0].token; + if (token < 0 || token >= TERMINAL_COUNT) + msg = tr("QQmlParser", "Syntax error"); + else + msg = tr("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + action = errorState; + goto _Lcheck_token; + } + + static int tokens[] = { + T_PLUS, + T_EQ, + + T_COMMA, + T_COLON, + T_SEMICOLON, + + T_RPAREN, T_RBRACKET, T_RBRACE, + + T_NUMERIC_LITERAL, + T_IDENTIFIER, + + T_LPAREN, T_LBRACKET, T_LBRACE, + + EOF_SYMBOL + }; + + for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) { + int a = t_action(errorState, *tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = tr("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = *tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + first_token = &token_buffer[0]; + last_token = &token_buffer[2]; + + action = errorState; + goto _Lcheck_token; + } + } + + for (int tk = 1; tk < TERMINAL_COUNT; ++tk) { + if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM || + tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION || + tk == T_FEED_JS_SOURCE_ELEMENT) + continue; + + int a = t_action(errorState, tk); + if (a > 0 && t_action(a, yytoken)) { + const QString msg = tr("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk])); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + + yytoken = tk; + yylval = 0; + yylloc = token_buffer[0].loc; + yylloc.length = 0; + + action = errorState; + goto _Lcheck_token; + } + } + + const QString msg = tr("QQmlParser", "Syntax error"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + } + + return false; +} + +QT_QML_END_NAMESPACE + + diff --git a/src/tools/qdoc/qmlparser/qqmljsparser_p.h b/src/tools/qdoc/qmlparser/qqmljsparser_p.h new file mode 100644 index 0000000000..ad532c32c7 --- /dev/null +++ b/src/tools/qdoc/qmlparser/qqmljsparser_p.h @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module 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$ +** +****************************************************************************/ + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// This file is automatically generated from qmljs.g. +// Changes will be lost. +// + +#ifndef QQMLJSPARSER_P_H +#define QQMLJSPARSER_P_H + +#include "qqmljsglobal_p.h" +#include "qqmljsgrammar_p.h" +#include "qqmljsast_p.h" +#include "qqmljsengine_p.h" + +#include <QtCore/QList> +#include <QtCore/QString> + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + +class Engine; + +class QML_PARSER_EXPORT Parser: protected QQmlJSGrammar +{ +public: + union Value { + int ival; + double dval; + AST::ArgumentList *ArgumentList; + AST::CaseBlock *CaseBlock; + AST::CaseClause *CaseClause; + AST::CaseClauses *CaseClauses; + AST::Catch *Catch; + AST::DefaultClause *DefaultClause; + AST::ElementList *ElementList; + AST::Elision *Elision; + AST::ExpressionNode *Expression; + AST::Finally *Finally; + AST::FormalParameterList *FormalParameterList; + AST::FunctionBody *FunctionBody; + AST::FunctionDeclaration *FunctionDeclaration; + AST::Node *Node; + AST::PropertyName *PropertyName; + AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::SourceElement *SourceElement; + AST::SourceElements *SourceElements; + AST::Statement *Statement; + AST::StatementList *StatementList; + AST::Block *Block; + AST::VariableDeclaration *VariableDeclaration; + AST::VariableDeclarationList *VariableDeclarationList; + + AST::UiProgram *UiProgram; + AST::UiImportList *UiImportList; + AST::UiImport *UiImport; + AST::UiParameterList *UiParameterList; + AST::UiPublicMember *UiPublicMember; + AST::UiObjectDefinition *UiObjectDefinition; + AST::UiObjectInitializer *UiObjectInitializer; + AST::UiObjectBinding *UiObjectBinding; + AST::UiScriptBinding *UiScriptBinding; + AST::UiArrayBinding *UiArrayBinding; + AST::UiObjectMember *UiObjectMember; + AST::UiObjectMemberList *UiObjectMemberList; + AST::UiArrayMemberList *UiArrayMemberList; + AST::UiQualifiedId *UiQualifiedId; + }; + +public: + Parser(Engine *engine); + ~Parser(); + + // parse a UI program + bool parse() { return parse(T_FEED_UI_PROGRAM); } + bool parseStatement() { return parse(T_FEED_JS_STATEMENT); } + bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); } + bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); } + bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); } + bool parseProgram() { return parse(T_FEED_JS_PROGRAM); } + + AST::UiProgram *ast() const + { return AST::cast<AST::UiProgram *>(program); } + + AST::Statement *statement() const + { + if (! program) + return 0; + + return program->statementCast(); + } + + AST::ExpressionNode *expression() const + { + if (! program) + return 0; + + return program->expressionCast(); + } + + AST::UiObjectMember *uiObjectMember() const + { + if (! program) + return 0; + + return program->uiObjectMemberCast(); + } + + AST::Node *rootNode() const + { return program; } + + QList<DiagnosticMessage> diagnosticMessages() const + { return diagnostic_messages; } + + inline DiagnosticMessage diagnosticMessage() const + { + foreach (const DiagnosticMessage &d, diagnostic_messages) { + if (! d.kind == DiagnosticMessage::Warning) + return d; + } + + return DiagnosticMessage(); + } + + inline QString errorMessage() const + { return diagnosticMessage().message; } + + inline int errorLineNumber() const + { return diagnosticMessage().loc.startLine; } + + inline int errorColumnNumber() const + { return diagnosticMessage().loc.startColumn; } + +protected: + bool parse(int startToken); + + void reallocateStack(); + + inline Value &sym(int index) + { return sym_stack [tos + index - 1]; } + + inline QStringRef &stringRef(int index) + { return string_stack [tos + index - 1]; } + + inline AST::SourceLocation &loc(int index) + { return location_stack [tos + index - 1]; } + + AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); + +protected: + Engine *driver; + MemoryPool *pool; + int tos; + int stack_size; + Value *sym_stack; + int *state_stack; + AST::SourceLocation *location_stack; + QStringRef *string_stack; + + AST::Node *program; + + // error recovery + enum { TOKEN_BUFFER_SIZE = 3 }; + + struct SavedToken { + int token; + double dval; + AST::SourceLocation loc; + QStringRef spell; + }; + + double yylval; + QStringRef yytokenspell; + AST::SourceLocation yylloc; + AST::SourceLocation yyprevlloc; + + SavedToken token_buffer[TOKEN_BUFFER_SIZE]; + SavedToken *first_token; + SavedToken *last_token; + + QList<DiagnosticMessage> diagnostic_messages; +}; + +} // end of namespace QQmlJS + + + +#define J_SCRIPT_REGEXPLITERAL_RULE1 79 + +#define J_SCRIPT_REGEXPLITERAL_RULE2 80 + +QT_QML_END_NAMESPACE + + + +#endif // QQMLJSPARSER_P_H diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp new file mode 100644 index 0000000000..f04b220f32 --- /dev/null +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -0,0 +1,619 @@ +/**************************************************************************** +** +** 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 <QFileInfo> +#include <QStringList> +#include <QtGlobal> +#include "qqmljsast_p.h" +#include "qqmljsastfwd_p.h" +#include "qqmljsengine_p.h" +#include <qdebug.h> +#include "node.h" +#include "codeparser.h" +#include "qmlvisitor.h" + +QT_BEGIN_NAMESPACE + +#define COMMAND_DEPRECATED Doc::alias(QLatin1String("deprecated")) // ### don't document +#define COMMAND_INGROUP Doc::alias(QLatin1String("ingroup")) +#define COMMAND_INTERNAL Doc::alias(QLatin1String("internal")) +#define COMMAND_OBSOLETE Doc::alias(QLatin1String("obsolete")) +#define COMMAND_PAGEKEYWORDS Doc::alias(QLatin1String("pagekeywords")) +#define COMMAND_PRELIMINARY Doc::alias(QLatin1String("preliminary")) +#define COMMAND_SINCE Doc::alias(QLatin1String("since")) + +#define COMMAND_QMLABSTRACT Doc::alias(QLatin1String("qmlabstract")) +#define COMMAND_QMLCLASS Doc::alias(QLatin1String("qmlclass")) +#define COMMAND_QMLMODULE Doc::alias(QLatin1String("qmlmodule")) +#define COMMAND_QMLPROPERTY Doc::alias(QLatin1String("qmlproperty")) +#define COMMAND_QMLATTACHEDPROPERTY Doc::alias(QLatin1String("qmlattachedproperty")) +#define COMMAND_QMLINHERITS Doc::alias(QLatin1String("inherits")) +#define COMMAND_INQMLMODULE Doc::alias(QLatin1String("inqmlmodule")) +#define COMMAND_QMLSIGNAL Doc::alias(QLatin1String("qmlsignal")) +#define COMMAND_QMLATTACHEDSIGNAL Doc::alias(QLatin1String("qmlattachedsignal")) +#define COMMAND_QMLMETHOD Doc::alias(QLatin1String("qmlmethod")) +#define COMMAND_QMLATTACHEDMETHOD Doc::alias(QLatin1String("qmlattachedmethod")) +#define COMMAND_QMLDEFAULT Doc::alias(QLatin1String("default")) +#define COMMAND_QMLREADONLY Doc::alias(QLatin1String("readonly")) +#define COMMAND_QMLBASICTYPE Doc::alias(QLatin1String("qmlbasictype")) +#define COMMAND_QMLMODULE Doc::alias(QLatin1String("qmlmodule")) + +/*! + The constructor stores all the parameters in local data members. + */ +QmlDocVisitor::QmlDocVisitor(const QString &filePath, + const QString &code, + QQmlJS::Engine *engine, + Tree *tree, + QSet<QString> &commands, + QSet<QString> &topics) + : nestingLevel(0) +{ + this->filePath = filePath; + this->name = QFileInfo(filePath).baseName(); + document = code; + this->engine = engine; + this->tree = tree; + this->commands = commands; + this->topics = topics; + current = tree->root(); +} + +/*! + The destructor does nothing. + */ +QmlDocVisitor::~QmlDocVisitor() +{ + // nothing. +} + +/*! + Returns the location of thre nearest comment above the \a offset. + */ +QQmlJS::AST::SourceLocation QmlDocVisitor::precedingComment(quint32 offset) const +{ + QListIterator<QQmlJS::AST::SourceLocation> it(engine->comments()); + it.toBack(); + + while (it.hasPrevious()) { + + QQmlJS::AST::SourceLocation loc = it.previous(); + + if (loc.begin() <= lastEndOffset) + // Return if we reach the end of the preceding structure. + break; + + else if (usedComments.contains(loc.begin())) + // Return if we encounter a previously used comment. + break; + + else if (loc.begin() > lastEndOffset && loc.end() < offset) { + + // Only examine multiline comments in order to avoid snippet markers. + if (document.mid(loc.offset - 1, 1) == "*") { + QString comment = document.mid(loc.offset, loc.length); + if (comment.startsWith(QLatin1Char('!')) || comment.startsWith(QLatin1Char('*'))) + return loc; + } + } + } + + return QQmlJS::AST::SourceLocation(); +} + +/*! + Finds the nearest unused qdoc comment above the QML entity + represented by the \a node and processes the qdoc commands + in that comment. The proceesed documentation is stored in + the \a node. + + If a qdoc comment is found about \a location, true is returned. + If a comment is not found there, false is returned. + */ +bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Node* node) +{ + QQmlJS::AST::SourceLocation loc = precedingComment(location.begin()); + + if (loc.isValid()) { + QString source = document.mid(loc.offset, loc.length); + Location start(filePath); + start.setLineNo(loc.startLine); + start.setColumnNo(loc.startColumn); + Location finish(filePath); + finish.setLineNo(loc.startLine); + finish.setColumnNo(loc.startColumn); + + Doc doc(start, finish, source.mid(1), commands, topics); + node->setDoc(doc); + applyMetacommands(loc, node, doc); + usedComments.insert(loc.offset); + if (doc.isEmpty()) + return false; + return true; + } + Location codeLoc(filePath); + codeLoc.setLineNo(location.startLine); + node->setLocation(codeLoc); + return false; +} + +/*! + A QML property argument has the form... + + <type> <component>::<name> + <type> <QML-module>::<component>::<name> + + 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 <QML-module> is the QML equivalent of a + C++ namespace. So this function splits \a arg on "::" + and stores the parts in the \e {type}, \e {module}, + \e {component}, and \a {name}, fields of \a qpa. If it + is successful, it returns true. If not enough parts + are found, a qdoc warning is emitted and false is + returned. + */ +bool QmlDocVisitor::splitQmlPropertyArg(const Doc& doc, + const QString& arg, + QmlPropArgs& qpa) +{ + qpa.clear(); + QStringList blankSplit = arg.split(QLatin1Char(' ')); + if (blankSplit.size() > 1) { + qpa.type_ = blankSplit[0]; + QStringList colonSplit(blankSplit[1].split("::")); + if (colonSplit.size() == 3) { + qpa.module_ = colonSplit[0]; + qpa.component_ = colonSplit[1]; + qpa.name_ = colonSplit[2]; + return true; + } + else if (colonSplit.size() == 2) { + qpa.component_ = colonSplit[0]; + qpa.name_ = colonSplit[1]; + return true; + } + else if (colonSplit.size() == 1) { + qpa.name_ = colonSplit[0]; + 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; +} + +/*! + Applies the metacommands found in the comment. + */ +void QmlDocVisitor::applyMetacommands(QQmlJS::AST::SourceLocation, + Node* node, + Doc& doc) +{ + const TopicList& topicsUsed = doc.topicsUsed(); + if (topicsUsed.size() > 0) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + for (int i=0; i<topicsUsed.size(); ++i) { + if (topicsUsed.at(i).topic == "qmlproperty") { + QmlPropArgs qpa; + if (splitQmlPropertyArg(doc, topicsUsed.at(i).args, qpa)) { + QmlPropertyNode* n = new QmlPropertyNode(qpn, qpa.name_, qpa.type_, false); + qpn->appendQmlPropNode(n); + } + else + qDebug() << " FAILED TO PARSE QML PROPERTY:" + << topicsUsed.at(i).topic << topicsUsed.at(i).args; + } + } + } + } + QSet<QString> metacommands = doc.metaCommandsUsed(); + if (metacommands.count() > 0) { + QString topic; + QStringList args; + QSet<QString>::iterator i = metacommands.begin(); + while (i != metacommands.end()) { + if (topics.contains(*i)) { + topic = *i; + break; + } + ++i; + } + if (!topic.isEmpty()) { + args = doc.metaCommandArgs(topic); + if (topic == COMMAND_QMLCLASS) { + } + else if (topic == COMMAND_QMLPROPERTY) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + qpn->setReadOnly(0); + if (qpn->dataType() == "alias") { + QStringList part = args[0].split(QLatin1Char(' ')); + qpn->setDataType(part[0]); + } + } + } + else if (topic == COMMAND_QMLMODULE) { + } + else if (topic == COMMAND_QMLATTACHEDPROPERTY) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + qpn->setReadOnly(0); + } + } + else if (topic == COMMAND_QMLSIGNAL) { + } + else if (topic == COMMAND_QMLATTACHEDSIGNAL) { + } + else if (topic == COMMAND_QMLMETHOD) { + } + else if (topic == COMMAND_QMLATTACHEDMETHOD) { + } + else if (topic == COMMAND_QMLBASICTYPE) { + } + } + metacommands.subtract(topics); + i = metacommands.begin(); + while (i != metacommands.end()) { + QString command = *i; + args = doc.metaCommandArgs(command); + if (command == COMMAND_QMLABSTRACT) { + if ((node->type() == Node::Fake) && (node->subType() == Node::QmlClass)) { + node->setAbstract(true); + } + } + else if (command == COMMAND_DEPRECATED) { + node->setStatus(Node::Deprecated); + } + else if (command == COMMAND_INQMLMODULE) { + node->setQmlModuleName(args[0]); + tree->addToQmlModule(node,args[0]); + QString qmid = node->qmlModuleIdentifier(); + QmlClassNode* qcn = static_cast<QmlClassNode*>(node); + QmlClassNode::moduleMap.insert(qmid + "::" + node->name(), qcn); + } + else if (command == COMMAND_QMLINHERITS) { + if (node->name() == args[0]) + doc.location().warning(tr("%1 tries to inherit itself").arg(args[0])); + else { + qDebug() << "QML Component:" << node->name() << "inherits:" << args[0]; + CodeParser::setLink(node, Node::InheritsLink, args[0]); + if (node->subType() == Node::QmlClass) { + QmlClassNode::addInheritedBy(args[0],node); + } + } + } + else if (command == COMMAND_QMLDEFAULT) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + qpn->setDefault(); + } + } + else if (command == COMMAND_QMLREADONLY) { + if (node->type() == Node::QmlProperty) { + QmlPropertyNode* qpn = static_cast<QmlPropertyNode*>(node); + qpn->setReadOnly(1); + } + } + else if (command == COMMAND_INGROUP) { + tree->addToGroup(node, args[0]); + } + else if (command == COMMAND_INTERNAL) { + node->setAccess(Node::Private); + node->setStatus(Node::Internal); + } + else if (command == COMMAND_OBSOLETE) { + if (node->status() != Node::Compat) + node->setStatus(Node::Obsolete); + } + else if (command == COMMAND_PAGEKEYWORDS) { + // Not done yet. Do we need this? + } + else if (command == COMMAND_PRELIMINARY) { + node->setStatus(Node::Preliminary); + } + else if (command == COMMAND_SINCE) { + QString arg = args.join(" "); + node->setSince(arg); + } + else { + doc.location().warning(tr("The \\%1 command is ignored in QML files").arg(command)); + } + ++i; + } + } +} + +/*! + Begin the visit of the object \a definition, recording it in a tree + structure. Increment the object nesting level, which is used to + test whether we are at the public API level. The public level is + level 1. +*/ +bool QmlDocVisitor::visit(QQmlJS::AST::UiObjectDefinition *definition) +{ + QString type = definition->qualifiedTypeNameId->name.toString(); + nestingLevel++; + + if (current->type() == Node::Namespace) { + QmlClassNode *component = new QmlClassNode(current, name, 0); + component->setTitle(name); + component->setImportList(importList); + + if (applyDocumentation(definition->firstSourceLocation(), component)) { + QmlClassNode::addInheritedBy(type, component); + if (!component->links().contains(Node::InheritsLink)) + component->setLink(Node::InheritsLink, type, type); + } + current = component; + } + + return true; +} + +/*! + End the visit of the object \a definition. In particular, + decrement the object nesting level, which is used to test + whether we are at the public API level. The public API + level is level 1. It won't decrement below 0. + */ +void QmlDocVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *definition) +{ + if (nestingLevel > 0) + --nestingLevel; + lastEndOffset = definition->lastSourceLocation().end(); +} + +/*! + Note that the imports list can be traversed by iteration to obtain + all the imports in the document at once, having found just one: + + *it = imports; it; it = it->next + + */ +bool QmlDocVisitor::visit(QQmlJS::AST::UiImportList *imports) +{ + QQmlJS::AST::UiImport* imp = imports->import; + quint32 length = imp->versionToken.offset - imp->fileNameToken.offset - 1; + QString module = document.mid(imp->fileNameToken.offset,length); + QString version = document.mid(imp->versionToken.offset, imp->versionToken.length); + if (version.size() > 1) { + int dot = version.lastIndexOf(QChar('.')); + if (dot > 0) + version = version.left(dot); + } + importList.append(QPair<QString, QString>(module, version)); + + return true; +} + +/*! + End the visit of the imports list. + */ +void QmlDocVisitor::endVisit(QQmlJS::AST::UiImportList *definition) +{ + lastEndOffset = definition->lastSourceLocation().end(); +} + +typedef QQmlJS::AST::ExpressionNode EN; +typedef QQmlJS::AST::IdentifierExpression IE; +typedef QQmlJS::AST::FieldMemberExpression FME; + +static QString reconstituteFieldMemberExpression(EN* en) +{ + QString s; + if (en) { + qDebug() << " There is an expression" << en->kind; + if (en->kind == QQmlJS::AST::Node::Kind_FieldMemberExpression) { + FME* fme = (FME*) en; + s = reconstituteFieldMemberExpression(fme->base); + s += QLatin1Char('.') + fme->name.toString(); + } + else if (en->kind == QQmlJS::AST::Node::Kind_IdentifierExpression) { + IE* ie = (IE*) en; + s = ie->name.toString(); + } + else { + qDebug() << " But it wasn't a recognized expression kind"; + } + } + return s; +} + +/*! + Visits the public \a member declaration, which can be a + signal or a property. It is a custom signal or property. + Only visit the \a member if the nestingLevel is 1. +*/ +bool QmlDocVisitor::visit(QQmlJS::AST::UiPublicMember *member) +{ + if (nestingLevel > 1) + return true; + switch (member->type) { + case QQmlJS::AST::UiPublicMember::Signal: + { + if (current->type() == Node::Fake) { + QmlClassNode *qmlClass = static_cast<QmlClassNode *>(current); + if (qmlClass) { + + QString name = member->name.toString(); + FunctionNode *qmlSignal = new FunctionNode(Node::QmlSignal, current, name, false); + + QList<Parameter> parameters; + for (QQmlJS::AST::UiParameterList *it = member->parameters; it; it = it->next) { + if (!it->type.isEmpty() && !it->name.isEmpty()) + parameters.append(Parameter(it->type.toString(), "", it->name.toString())); + } + + qmlSignal->setParameters(parameters); + applyDocumentation(member->firstSourceLocation(), qmlSignal); + } + } + break; + } + case QQmlJS::AST::UiPublicMember::Property: + { + QString type = member->memberType.toString(); + QString name = member->name.toString(); + if (current->type() == Node::Fake) { + QmlClassNode *qmlClass = static_cast<QmlClassNode *>(current); + if (qmlClass) { + QString name = member->name.toString(); + QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlClass, name, type, false); + qmlPropNode->setReadOnly(member->isReadonlyMember); + if (member->isDefaultMember) + qmlPropNode->setDefault(); + applyDocumentation(member->firstSourceLocation(), qmlPropNode); + } +#if 0 + if (qmlClass) { + QString name = member->name->asString(); + QmlPropGroupNode *qmlPropGroup = new QmlPropGroupNode(qmlClass, name, false); + if (member->isDefaultMember) + qmlPropGroup->setDefault(); + QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup, name, type, false); + qmlPropNode->setWritable(!member->isReadonlyMember); + applyDocumentation(member->firstSourceLocation(), qmlPropGroup); + } +#endif + } + break; + } + default: + return false; + } + + return true; +} + +/*! + End the visit of the \a member. + */ +void QmlDocVisitor::endVisit(QQmlJS::AST::UiPublicMember* member) +{ + lastEndOffset = member->lastSourceLocation().end(); +} + +bool QmlDocVisitor::visit(QQmlJS::AST::IdentifierPropertyName *) +{ + return true; +} + +/*! + Begin the visit of the function declaration \a fd, but only + if the nesting level is 1. + */ +bool QmlDocVisitor::visit(QQmlJS::AST::FunctionDeclaration* fd) +{ + if (nestingLevel > 1) + return true; + if (current->type() == Node::Fake) { + QmlClassNode* qmlClass = static_cast<QmlClassNode*>(current); + if (qmlClass) { + QString name = fd->name.toString(); + FunctionNode* qmlMethod = new FunctionNode(Node::QmlMethod, current, name, false); + QList<Parameter> parameters; + QQmlJS::AST::FormalParameterList* formals = fd->formals; + if (formals) { + QQmlJS::AST::FormalParameterList* fpl = formals; + do { + parameters.append(Parameter(QString(""), QString(""), fpl->name.toString())); + fpl = fpl->next; + } while (fpl && fpl != formals); + qmlMethod->setParameters(parameters); + } + applyDocumentation(fd->firstSourceLocation(), qmlMethod); + } + } + return true; +} + +/*! + End the visit of the function declaration, \a fd. + */ +void QmlDocVisitor::endVisit(QQmlJS::AST::FunctionDeclaration* fd) +{ + lastEndOffset = fd->lastSourceLocation().end(); +} + +/*! + Begin the visit of the signal handler declaration \a sb, but only + if the nesting level is 1. + */ +bool QmlDocVisitor::visit(QQmlJS::AST::UiScriptBinding* sb) +{ + if (nestingLevel > 1) + return true; + if (current->type() == Node::Fake) { + QString handler = sb->qualifiedId->name.toString(); + if (handler.length() > 2 && handler.startsWith("on") && handler.at(2).isUpper()) { + QmlClassNode* qmlClass = static_cast<QmlClassNode*>(current); + if (qmlClass) { + FunctionNode* qmlSH = new FunctionNode(Node::QmlSignalHandler,current,handler,false); + applyDocumentation(sb->firstSourceLocation(), qmlSH); + } + } + } + return true; +} + +void QmlDocVisitor::endVisit(QQmlJS::AST::UiScriptBinding* sb) +{ + lastEndOffset = sb->lastSourceLocation().end(); +} + +bool QmlDocVisitor::visit(QQmlJS::AST::UiQualifiedId* ) +{ + return true; +} + +void QmlDocVisitor::endVisit(QQmlJS::AST::UiQualifiedId* ) +{ + // nothing. +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/qmlvisitor.h b/src/tools/qdoc/qmlvisitor.h new file mode 100644 index 0000000000..bb3d6ac799 --- /dev/null +++ b/src/tools/qdoc/qmlvisitor.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QMLVISITOR_H +#define QMLVISITOR_H + +#include <QString> +#include "qqmljsastvisitor_p.h" +#include "node.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +struct QmlPropArgs +{ + QString type_; + QString module_; + QString component_; + QString name_; + + void clear() { + type_.clear(); + module_.clear(); + component_.clear(); + name_.clear(); + } +}; + +class QmlDocVisitor : public QQmlJS::AST::Visitor +{ +public: + QmlDocVisitor(const QString &filePath, + const QString &code, + QQmlJS::Engine *engine, + Tree *tree, + QSet<QString> &commands, + QSet<QString> &topics); + virtual ~QmlDocVisitor(); + + bool visit(QQmlJS::AST::UiImportList *imports); + void endVisit(QQmlJS::AST::UiImportList *definition); + + bool visit(QQmlJS::AST::UiObjectDefinition *definition); + void endVisit(QQmlJS::AST::UiObjectDefinition *definition); + + bool visit(QQmlJS::AST::UiPublicMember *member); + void endVisit(QQmlJS::AST::UiPublicMember *definition); + + bool visit(QQmlJS::AST::IdentifierPropertyName *idproperty); + + bool visit(QQmlJS::AST::FunctionDeclaration *); + void endVisit(QQmlJS::AST::FunctionDeclaration *); + + bool visit(QQmlJS::AST::UiScriptBinding *); + void endVisit(QQmlJS::AST::UiScriptBinding *); + + bool visit(QQmlJS::AST::UiQualifiedId *); + void endVisit(QQmlJS::AST::UiQualifiedId *); + +private: + QQmlJS::AST::SourceLocation precedingComment(quint32 offset) const; + bool applyDocumentation(QQmlJS::AST::SourceLocation location, Node *node); + void applyMetacommands(QQmlJS::AST::SourceLocation location, Node* node, Doc& doc); + bool splitQmlPropertyArg(const Doc& doc, + const QString& arg, + QmlPropArgs& qpa); + + QQmlJS::Engine *engine; + quint32 lastEndOffset; + quint32 nestingLevel; + QString filePath; + QString name; + QString document; + QList<QPair<QString, QString> > importList; + QSet<QString> commands; + QSet<QString> topics; + QSet<quint32> usedComments; + Tree *tree; + InnerNode *current; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/quoter.cpp b/src/tools/qdoc/quoter.cpp new file mode 100644 index 0000000000..0c16acbe73 --- /dev/null +++ b/src/tools/qdoc/quoter.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** 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 <qfileinfo.h> +#include <qregexp.h> +#include <qdebug.h> + +#include "quoter.h" + +QT_BEGIN_NAMESPACE + +static void replaceMultipleNewlines(QString &s) +{ + const int n = s.size(); + bool slurping = false; + int j = -1; + const QChar newLine = QLatin1Char('\n'); + QChar *d = s.data(); + for (int i = 0; i != n; ++i) { + const QChar c = d[i]; + bool hit = (c == newLine); + if (slurping && hit) + continue; + d[++j] = c; + slurping = hit; + } + s.resize(++j); +} + +// This is equivalent to line.split( QRegExp("\n(?!\n|$)") ) but much faster +QStringList Quoter::splitLines(const QString &line) +{ + QStringList result; + int i = line.size(); + while (true) { + int j = i - 1; + while (j >= 0 && line.at(j) == QLatin1Char('\n')) + --j; + while (j >= 0 && line.at(j) != QLatin1Char('\n')) + --j; + result.prepend(line.mid(j + 1, i - j - 1)); + if (j < 0) + break; + i = j; + } + return result; +} + +/* + Transforms 'int x = 3 + 4' into 'int x=3+4'. A white space is kept + between 'int' and 'x' because it is meaningful in C++. +*/ +static void trimWhiteSpace( QString& str ) +{ + enum { Normal, MetAlnum, MetSpace } state = Normal; + const int n = str.length(); + + int j = -1; + QChar *d = str.data(); + for ( int i = 0; i != n; ++i ) { + const QChar c = d[i]; + if ( c.isLetterOrNumber() ) { + if ( state == Normal ) { + state = MetAlnum; + } else { + if ( state == MetSpace ) + str[++j] = c; + state = Normal; + } + str[++j] = c; + } else if ( c.isSpace() ) { + if ( state == MetAlnum ) + state = MetSpace; + } else { + state = Normal; + str[++j] = c; + } + } + str.resize(++j); +} + +Quoter::Quoter() + : silent( false ) +{ + /* We're going to hard code these delimiters: + * C++, Qt, Qt Script, Java: + //! [<id>] + * .pro, .py files: + #! [<id>] + * .html, .qrc, .ui, .xq, .xml files: + <!-- [<id>] --> + */ + commentHash["pro"] = "#!"; + commentHash["py"] = "#!"; + commentHash["html"] = "<!--"; + commentHash["qrc"] = "<!--"; + commentHash["ui"] = "<!--"; + commentHash["xml"] = "<!--"; + commentHash["xq"] = "<!--"; +} + +void Quoter::reset() +{ + silent = false; + plainLines.clear(); + markedLines.clear(); + codeLocation = Location::null; +} + +void Quoter::quoteFromFile( const QString& userFriendlyFilePath, + const QString& plainCode, + const QString& markedCode ) +{ + silent = false; + + /* + Split the source code into logical lines. Empty lines are + treated specially. Before: + + p->alpha(); + p->beta(); + + p->gamma(); + + + p->delta(); + + After: + + p->alpha(); + p->beta();\n + p->gamma();\n\n + p->delta(); + + Newlines are preserved because they affect codeLocation. + */ + codeLocation = Location( userFriendlyFilePath ); + + plainLines = splitLines(plainCode); + markedLines = splitLines(markedCode); + if (markedLines.count() != plainLines.count()) { + codeLocation.warning(tr("Something is wrong with qdoc's handling of marked code")); + markedLines = plainLines; + } + + /* + Squeeze blanks (cat -s). + */ + QStringList::Iterator m = markedLines.begin(); + while ( m != markedLines.end() ) { + replaceMultipleNewlines( *m ); + ++m; + } + codeLocation.start(); +} + +QString Quoter::quoteLine( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + if ( plainLines.isEmpty() ) { + failedAtEnd( docLocation, command ); + return QString(); + } + + if ( pattern.isEmpty() ) { + docLocation.warning( tr("Missing pattern after '\\%1'").arg(command) ); + return QString(); + } + + if ( match(docLocation, pattern, plainLines.first()) ) + return getLine(); + + if ( !silent ) { + docLocation.warning( tr("Command '\\%1' failed").arg(command) ); + codeLocation.warning( tr("Pattern '%1' didn't match here") + .arg(pattern) ); + silent = true; + } + return QString(); +} + +QString Quoter::quoteSnippet(const Location &docLocation, const QString &identifier) +{ + QString comment = commentForCode(); + QString delimiter = comment + QString(" [%1]").arg(identifier); + QString t; + int indent = 0; + + while (!plainLines.isEmpty()) { + if (match(docLocation, delimiter, plainLines.first())) { + QString startLine = getLine(); + while (indent < startLine.length() && startLine[indent] == QLatin1Char(' ')) + indent++; + break; + } + getLine(); + } + while (!plainLines.isEmpty()) { + QString line = plainLines.first(); + if (match(docLocation, delimiter, line)) { + QString lastLine = getLine(indent); + int dIndex = lastLine.indexOf(delimiter); + if (dIndex > 0) { + // The delimiter might be preceded on the line by other + // delimeters, so look for the first comment on the line. + QString leading = lastLine.left(dIndex); + dIndex = leading.indexOf(comment); + if (dIndex != -1) + leading = leading.left(dIndex); + if (leading.endsWith(QLatin1String("<@comment>"))) + leading.chop(10); + if (!leading.trimmed().isEmpty()) + t += leading; + } + return t; + } + + t += removeSpecialLines(line, comment, indent); + } + failedAtEnd(docLocation, QString("snippet (%1)").arg(delimiter)); + return t; +} + +QString Quoter::quoteTo( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + QString t; + QString comment = commentForCode(); + + if ( pattern.isEmpty() ) { + while ( !plainLines.isEmpty() ) { + QString line = plainLines.first(); + t += removeSpecialLines(line, comment); + } + } else { + while ( !plainLines.isEmpty() ) { + if ( match(docLocation, pattern, plainLines.first()) ) { + return t; + } + t += getLine(); + } + failedAtEnd( docLocation, command ); + } + return t; +} + +QString Quoter::quoteUntil( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + QString t = quoteTo( docLocation, command, pattern ); + t += getLine(); + return t; +} + +QString Quoter::getLine(int unindent) +{ + if ( plainLines.isEmpty() ) + return QString(); + + plainLines.removeFirst(); + + QString t = markedLines.takeFirst(); + int i = 0; + while (i < unindent && i < t.length() && t[i] == QLatin1Char(' ')) + i++; + + t = t.mid(i); + t += QLatin1Char('\n'); + codeLocation.advanceLines( t.count( QLatin1Char('\n') ) ); + return t; +} + +bool Quoter::match( const Location& docLocation, const QString& pattern0, + const QString& line ) +{ + QString str = line; + while ( str.endsWith(QLatin1Char('\n')) ) + str.truncate( str.length() - 1 ); + + QString pattern = pattern0; + if ( pattern.startsWith(QLatin1Char('/')) + && pattern.endsWith(QLatin1Char('/')) + && pattern.length() > 2 ) { + QRegExp rx( pattern.mid(1, pattern.length() - 2) ); + if ( !silent && !rx.isValid() ) { + docLocation.warning( tr("Invalid regular expression '%1'") + .arg(rx.pattern()) ); + silent = true; + } + return str.indexOf( rx ) != -1; + } + trimWhiteSpace(str); + trimWhiteSpace(pattern); + return str.indexOf(pattern) != -1; +} + +void Quoter::failedAtEnd( const Location& docLocation, const QString& command ) +{ + if (!silent && !command.isEmpty()) { + if ( codeLocation.filePath().isEmpty() ) { + docLocation.warning( tr("Unexpected '\\%1'").arg(command) ); + } else { + docLocation.warning( tr("Command '\\%1' failed at end of file '%2'") + .arg(command).arg(codeLocation.filePath()) ); + } + silent = true; + } +} + +QString Quoter::commentForCode() const +{ + QString suffix = QFileInfo(codeLocation.fileName()).suffix(); + return commentHash.value(suffix, "//!"); +} + +QString Quoter::removeSpecialLines(const QString &line, const QString &comment, int unindent) +{ + QString t; + + // Remove special macros to support Qt namespacing. + QString trimmed = line.trimmed(); + if (trimmed.startsWith("QT_BEGIN_NAMESPACE")) { + getLine(); + } else if (trimmed.startsWith("QT_END_NAMESPACE")) { + getLine(); + t += QLatin1Char('\n'); + } else if (!trimmed.startsWith(comment)) { + // Ordinary code + t += getLine(unindent); + } else { + // Comments + if (line.contains(QLatin1Char('\n'))) + t += QLatin1Char('\n'); + getLine(); + } + return t; +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/quoter.h b/src/tools/qdoc/quoter.h new file mode 100644 index 0000000000..54817eabac --- /dev/null +++ b/src/tools/qdoc/quoter.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + quoter.h +*/ + +#ifndef QUOTER_H +#define QUOTER_H + +#include <qhash.h> +#include <qstringlist.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Quoter +{ +public: + Quoter(); + + void reset(); + void quoteFromFile( const QString& userFriendlyFileName, + const QString& plainCode, const QString& markedCode ); + QString quoteLine( const Location& docLocation, const QString& command, + const QString& pattern ); + QString quoteTo( const Location& docLocation, const QString& command, + const QString& pattern ); + QString quoteUntil( const Location& docLocation, const QString& command, + const QString& pattern ); + QString quoteSnippet(const Location &docLocation, const QString &identifier); + + static QStringList splitLines(const QString &line); + +private: + QString getLine(int unindent = 0); + void failedAtEnd( const Location& docLocation, const QString& command ); + bool match( const Location& docLocation, const QString& pattern, + const QString& line ); + QString commentForCode() const; + QString removeSpecialLines(const QString &line, const QString &comment, + int unindent = 0); + + bool silent; + bool validRegExp; + QStringList plainLines; + QStringList markedLines; + Location codeLocation; + QHash<QString,QString> commentHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/separator.cpp b/src/tools/qdoc/separator.cpp new file mode 100644 index 0000000000..4c2b2009ec --- /dev/null +++ b/src/tools/qdoc/separator.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + separator.cpp +*/ + +#include "separator.h" +#include "tr.h" + +QT_BEGIN_NAMESPACE + +QString separator(int index, int count) +{ + if (index == count - 1) + return tr(".", "terminator"); + if (count == 2) + return tr(" and ", "separator when N = 2"); + if (index == 0) + return tr(", ", "first separator when N > 2"); + if (index < count - 2) + return tr(", ", "general separator when N > 2"); + return tr(", and ", "last separator when N > 2"); +} + +QString comma(int index, int count) +{ + if (index == count - 1) + return QString(""); + if (count == 2) + return tr(" and ", "separator when N = 2"); + if (index == 0) + return tr(", ", "first separator when N > 2"); + if (index < count - 2) + return tr(", ", "general separator when N > 2"); + return tr(", and ", "last separator when N > 2"); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/separator.h b/src/tools/qdoc/separator.h new file mode 100644 index 0000000000..df7284ea1a --- /dev/null +++ b/src/tools/qdoc/separator.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + separator.h +*/ + +#ifndef SEPARATOR_H +#define SEPARATOR_H + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +QString separator( int index, int count ); +QString comma( int index, int count ); + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/text.cpp b/src/tools/qdoc/text.cpp new file mode 100644 index 0000000000..275716ad77 --- /dev/null +++ b/src/tools/qdoc/text.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + text.cpp +*/ + +#include <qregexp.h> +#include "text.h" +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +Text::Text() + : first(0), last(0) +{ +} + +Text::Text(const QString &str) + : first(0), last(0) +{ + operator<<(str); +} + +Text::Text(const Text& text) + : first(0), last(0) +{ + operator=(text); +} + +Text::~Text() +{ + clear(); +} + +Text& Text::operator=(const Text& text) +{ + if (this != &text) { + clear(); + operator<<(text); + } + return *this; +} + +Text& Text::operator<<(Atom::Type atomType) +{ + return operator<<(Atom(atomType)); +} + +Text& Text::operator<<(const QString& string) +{ + return operator<<(Atom(Atom::String, string)); +} + +Text& Text::operator<<(const Atom& atom) +{ + if (atom.count() < 2) { + if (first == 0) { + first = new Atom(atom.type(), atom.string()); + last = first; + } + else + last = new Atom(last, atom.type(), atom.string()); + } + else { + if (first == 0) { + first = new Atom(atom.type(), atom.string(), atom.string(1)); + last = first; + } + else + last = new Atom(last, atom.type(), atom.string(), atom.string(1)); + } + return *this; +} + +Text& Text::operator<<(const Text& text) +{ + const Atom* atom = text.firstAtom(); + while (atom != 0) { + operator<<(*atom); + atom = atom->next(); + } + return *this; +} + +void Text::stripFirstAtom() +{ + if (first != 0) { + if (first == last) + last = 0; + Atom* oldFirst = first; + first = first->next(); + delete oldFirst; + } +} + +void Text::stripLastAtom() +{ + if (last != 0) { + Atom* oldLast = last; + if (first == last) { + first = 0; + last = 0; + } else { + last = first; + while (last->next() != oldLast) + last = last->next(); + last->setNext(0); + } + delete oldLast; + } +} + +QString Text::toString() const +{ + QString str; + const Atom* atom = firstAtom(); + while (atom != 0) { + if (atom->type() == Atom::String || + atom->type() == Atom::AutoLink || + atom->type() == Atom::C || + atom->type() == Atom::GuidLink) + str += atom->string(); + atom = atom->next(); + } + return str; +} + +Text Text::subText(Atom::Type left, Atom::Type right, const Atom* from, bool inclusive) const +{ + const Atom* begin = from ? from : firstAtom(); + const Atom* end; + + while (begin != 0 && begin->type() != left) + begin = begin->next(); + if (begin != 0) { + if (!inclusive) + begin = begin->next(); + } + + end = begin; + while (end != 0 && end->type() != right) + end = end->next(); + if (end == 0) + begin = 0; + else if (inclusive) + end = end->next(); + return subText(begin, end); +} + +Text Text::sectionHeading(const Atom* sectionLeft) +{ + if (sectionLeft != 0) { + const Atom* begin = sectionLeft; + while (begin != 0 && begin->type() != Atom::SectionHeadingLeft) + begin = begin->next(); + if (begin != 0) + begin = begin->next(); + + const Atom* end = begin; + while (end != 0 && end->type() != Atom::SectionHeadingRight) + end = end->next(); + + if (end != 0) + return subText(begin, end); + } + return Text(); +} + +const Atom* Text::sectionHeadingAtom(const Atom* sectionLeft) +{ + if (sectionLeft != 0) { + const Atom* begin = sectionLeft; + while (begin != 0 && begin->type() != Atom::SectionHeadingLeft) + begin = begin->next(); + if (begin != 0) + begin = begin->next(); + + return begin; + } + return 0; +} + +void Text::dump() const +{ + const Atom* atom = firstAtom(); + while (atom != 0) { + QString str = atom->string(); + str.replace("\\", "\\\\"); + str.replace("\"", "\\\""); + str.replace("\n", "\\n"); + str.replace(QRegExp("[^\x20-\x7e]"), "?"); + if (!str.isEmpty()) + str = " \"" + str + "\""; + fprintf(stderr, " %-15s%s\n", atom->typeString().toLatin1().data(), str.toLatin1().data()); + atom = atom->next(); + } +} + +Text Text::subText(const Atom* begin, const Atom* end) +{ + Text text; + if (begin != 0) { + while (begin != end) { + text << *begin; + begin = begin->next(); + } + } + return text; +} + +void Text::clear() +{ + while (first != 0) { + Atom* atom = first; + first = first->next(); + delete atom; + } + first = 0; + last = 0; +} + +int Text::compare(const Text &text1, const Text &text2) +{ + if (text1.isEmpty()) + return text2.isEmpty() ? 0 : -1; + if (text2.isEmpty()) + return 1; + + const Atom* atom1 = text1.firstAtom(); + const Atom* atom2 = text2.firstAtom(); + + for (;;) { + if (atom1->type() != atom2->type()) + return (int)atom1->type() - (int)atom2->type(); + int cmp = QString::compare(atom1->string(), atom2->string()); + if (cmp != 0) + return cmp; + + if (atom1 == text1.lastAtom()) + return atom2 == text2.lastAtom() ? 0 : -1; + if (atom2 == text2.lastAtom()) + return 1; + atom1 = atom1->next(); + atom2 = atom2->next(); + } +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/text.h b/src/tools/qdoc/text.h new file mode 100644 index 0000000000..07ccb0edb8 --- /dev/null +++ b/src/tools/qdoc/text.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + text.h +*/ + +#ifndef TEXT_H +#define TEXT_H + +#include "atom.h" + +QT_BEGIN_NAMESPACE + +class Text +{ +public: + Text(); + explicit Text(const QString &str); + Text(const Text& text); + ~Text(); + + Text& operator=(const Text& text); + + Atom *firstAtom() { return first; } + Atom *lastAtom() { return last; } + Text& operator<<(Atom::Type atomType); + Text& operator<<(const QString& string); + Text& operator<<(const Atom& atom); + Text& operator<<(const Text& text); + void stripFirstAtom(); + void stripLastAtom(); + + bool isEmpty() const { return first == 0; } + QString toString() const; + const Atom *firstAtom() const { return first; } + const Atom *lastAtom() const { return last; } + Text subText(Atom::Type left, Atom::Type right, const Atom *from = 0, bool inclusive = false) const; + void dump() const; + void clear(); + + static Text subText(const Atom *begin, const Atom *end = 0); + static Text sectionHeading(const Atom *sectionBegin); + static const Atom *sectionHeadingAtom(const Atom *sectionLeft); + static int compare(const Text &text1, const Text &text2); + +private: + + Atom *first; + Atom *last; +}; + +inline bool operator==(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) == 0; } +inline bool operator!=(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) != 0; } +inline bool operator<(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) < 0; } +inline bool operator<=(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) <= 0; } +inline bool operator>(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) > 0; } +inline bool operator>=(const Text &text1, const Text &text2) +{ return Text::compare(text1, text2) >= 0; } + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/tokenizer.cpp b/src/tools/qdoc/tokenizer.cpp new file mode 100644 index 0000000000..c87764b934 --- /dev/null +++ b/src/tools/qdoc/tokenizer.cpp @@ -0,0 +1,771 @@ +/**************************************************************************** +** +** 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 "config.h" +#include "tokenizer.h" + +#include <qfile.h> +#include <qhash.h> +#include <qregexp.h> +#include <qstring.h> +#include <qtextcodec.h> + +#include <ctype.h> +#include <string.h> + +QT_BEGIN_NAMESPACE + +#define LANGUAGE_CPP "Cpp" + +/* qmake ignore Q_OBJECT */ + +/* + Keep in sync with tokenizer.h. +*/ +static const char *kwords[] = { + "char", "class", "const", "double", "enum", "explicit", + "friend", "inline", "int", "long", "namespace", "operator", + "private", "protected", "public", "short", "signals", "signed", + "slots", "static", "struct", "template", "typedef", "typename", + "union", "unsigned", "using", "virtual", "void", "volatile", + "__int64", + "Q_OBJECT", + "Q_OVERRIDE", + "Q_PROPERTY", + "Q_PRIVATE_PROPERTY", + "Q_DECLARE_SEQUENTIAL_ITERATOR", + "Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR", + "Q_DECLARE_ASSOCIATIVE_ITERATOR", + "Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR", + "Q_DECLARE_FLAGS", + "Q_SIGNALS", + "Q_SLOTS", + "QT_COMPAT", + "QT_COMPAT_CONSTRUCTOR", + "QT_DEPRECATED", + "QT_MOC_COMPAT", + "QT_MODULE", + "QT3_SUPPORT", + "QT3_SUPPORT_CONSTRUCTOR", + "QT3_MOC_SUPPORT", + "QDOC_PROPERTY" +}; + +static const int KwordHashTableSize = 4096; +static int kwordHashTable[KwordHashTableSize]; + +static QHash<QByteArray, bool> *ignoredTokensAndDirectives = 0; + +static QRegExp *comment = 0; +static QRegExp *versionX = 0; +static QRegExp *definedX = 0; + +static QRegExp *defines = 0; +static QRegExp *falsehoods = 0; + +static QTextCodec *sourceCodec = 0; + +/* + This function is a perfect hash function for the 37 keywords of C99 + (with a hash table size of 512). It should perform well on our + Qt-enhanced C++ subset. +*/ +static int hashKword(const char *s, int len) +{ + return (((uchar) s[0]) + (((uchar) s[2]) << 5) + + (((uchar) s[len - 1]) << 3)) % KwordHashTableSize; +} + +static void insertKwordIntoHash(const char *s, int number) +{ + int k = hashKword(s, strlen(s)); + while (kwordHashTable[k]) { + if (++k == KwordHashTableSize) + k = 0; + } + kwordHashTable[k] = number; +} + +Tokenizer::Tokenizer(const Location& loc, QFile &in) +{ + init(); + yyIn = in.readAll(); + yyPos = 0; + start(loc); +} + +Tokenizer::Tokenizer(const Location& loc, const QByteArray &in) + : yyIn(in) +{ + init(); + yyPos = 0; + start(loc); +} + +Tokenizer::~Tokenizer() +{ + delete[] yyLexBuf1; + delete[] yyLexBuf2; +} + +int Tokenizer::getToken() +{ + char *t = yyPrevLex; + yyPrevLex = yyLex; + yyLex = t; + + while (yyCh != EOF) { + yyTokLoc = yyCurLoc; + yyLexLen = 0; + + if (isspace(yyCh)) { + do { + yyCh = getChar(); + } while (isspace(yyCh)); + } + else if (isalpha(yyCh) || yyCh == '_') { + do { + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '_'); + + int k = hashKword(yyLex, yyLexLen); + for (;;) { + int i = kwordHashTable[k]; + if (i == 0) { + return Tok_Ident; + } + else if (i == -1) { + if (!parsingMacro && ignoredTokensAndDirectives->contains(yyLex)) { + if (ignoredTokensAndDirectives->value(yyLex)) { // it's a directive + int parenDepth = 0; + while (yyCh != EOF && (yyCh != ')' || parenDepth > 1)) { + if (yyCh == '(') + ++parenDepth; + else if (yyCh == ')') + --parenDepth; + yyCh = getChar(); + } + if (yyCh == ')') + yyCh = getChar(); + } + break; + } + } + else if (strcmp(yyLex, kwords[i - 1]) == 0) { + int ret = (int) Tok_FirstKeyword + i - 1; + if (ret != Tok_explicit && ret != Tok_inline && ret != Tok_typename) + return ret; + break; + } + + if (++k == KwordHashTableSize) + k = 0; + } + } + else if (isdigit(yyCh)) { + do { + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || + yyCh == '-'); + return Tok_Number; + } + else { + switch (yyCh) { + case '!': + case '%': + yyCh = getChar(); + if (yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + case '"': + yyCh = getChar(); + + while (yyCh != EOF && yyCh != '"') { + if (yyCh == '\\') + yyCh = getChar(); + yyCh = getChar(); + } + yyCh = getChar(); + + if (yyCh == EOF) + yyTokLoc.warning(tr("Unterminated C++ string literal"), + tr("Maybe you forgot '/*!' at the beginning of the file?")); + else + return Tok_String; + break; + case '#': + return getTokenAfterPreprocessor(); + case '&': + yyCh = getChar(); + if (yyCh == '&' || yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } + else { + return Tok_Ampersand; + } + case '\'': + yyCh = getChar(); + if (yyCh == '\\') + yyCh = getChar(); + do { + yyCh = getChar(); + } while (yyCh != EOF && yyCh != '\''); + + if (yyCh == EOF) { + yyTokLoc.warning(tr("Unterminated C++ character" + " literal")); + } + else { + yyCh = getChar(); + return Tok_Number; + } + break; + case '(': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyParenDepth++; + if (isspace(yyCh)) { + do { + yyCh = getChar(); + } while (isspace(yyCh)); + yyLexLen = 1; + yyLex[1] = '\0'; + } + if (yyCh == '*') { + yyCh = getChar(); + return Tok_LeftParenAster; + } + return Tok_LeftParen; + case ')': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyParenDepth--; + return Tok_RightParen; + case '*': + yyCh = getChar(); + if (yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } else { + return Tok_Aster; + } + case '^': + yyCh = getChar(); + if (yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } else { + return Tok_Caret; + } + case '+': + yyCh = getChar(); + if (yyCh == '+' || yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + case ',': + yyCh = getChar(); + return Tok_Comma; + case '-': + yyCh = getChar(); + if (yyCh == '-' || yyCh == '=') { + yyCh = getChar(); + } else if (yyCh == '>') { + yyCh = getChar(); + if (yyCh == '*') + yyCh = getChar(); + } + return Tok_SomeOperator; + case '.': + yyCh = getChar(); + if (yyCh == '*') { + yyCh = getChar(); + } else if (yyCh == '.') { + do { + yyCh = getChar(); + } while (yyCh == '.'); + return Tok_Ellipsis; + } else if (isdigit(yyCh)) { + do { + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '.' || yyCh == '+' || + yyCh == '-'); + return Tok_Number; + } + return Tok_SomeOperator; + case '/': + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + } while (yyCh != EOF && yyCh != '\n'); + } else if (yyCh == '*') { + bool metDoc = false; // empty doc is no doc + bool metSlashAsterBang = false; + bool metAster = false; + bool metAsterSlash = false; + + yyCh = getChar(); + if (yyCh == '!') + metSlashAsterBang = true; + + while (!metAsterSlash) { + if (yyCh == EOF) { + yyTokLoc.warning(tr("Unterminated C++ comment")); + break; + } else { + if (yyCh == '*') { + metAster = true; + } else if (metAster && yyCh == '/') { + metAsterSlash = true; + } else { + metAster = false; + if (isgraph(yyCh)) + metDoc = true; + } + } + yyCh = getChar(); + } + if (metSlashAsterBang && metDoc) + return Tok_Doc; + else if (yyParenDepth > 0) + return Tok_Comment; + } else { + if (yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + } + break; + case ':': + yyCh = getChar(); + if (yyCh == ':') { + yyCh = getChar(); + return Tok_Gulbrandsen; + } else { + return Tok_Colon; + } + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '<': + yyCh = getChar(); + if (yyCh == '<') { + yyCh = getChar(); + if (yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + } else if (yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } else { + return Tok_LeftAngle; + } + case '=': + yyCh = getChar(); + if (yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } else { + return Tok_Equal; + } + case '>': + yyCh = getChar(); + if (yyCh == '>') { + yyCh = getChar(); + if (yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + } else if (yyCh == '=') { + yyCh = getChar(); + return Tok_SomeOperator; + } else { + return Tok_RightAngle; + } + case '?': + yyCh = getChar(); + return Tok_SomeOperator; + case '[': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyBracketDepth++; + return Tok_LeftBracket; + case '\\': + yyCh = getChar(); + yyCh = getChar(); // skip one character + break; + case ']': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyBracketDepth--; + return Tok_RightBracket; + case '{': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyBraceDepth++; + return Tok_LeftBrace; + case '}': + yyCh = getChar(); + if (yyNumPreprocessorSkipping == 0) + yyBraceDepth--; + return Tok_RightBrace; + case '|': + yyCh = getChar(); + if (yyCh == '|' || yyCh == '=') + yyCh = getChar(); + return Tok_SomeOperator; + case '~': + yyCh = getChar(); + return Tok_Tilde; + case '@': + yyCh = getChar(); + return Tok_At; + default: + // ### We should really prevent qdoc from looking at snippet files rather than + // ### suppress warnings when reading them. + if (yyNumPreprocessorSkipping == 0 && !yyTokLoc.fileName().endsWith(".qdoc")) { + yyTokLoc.warning(tr("Hostile character 0x%1 in C++ source") + .arg((uchar)yyCh, 1, 16)); + } + yyCh = getChar(); + } + } + } + + if (yyPreprocessorSkipping.count() > 1) { + yyTokLoc.warning(tr("Expected #endif before end of file")); + // clear it out or we get an infinite loop! + while (!yyPreprocessorSkipping.isEmpty()) { + popSkipping(); + } + } + + strcpy(yyLex, "end-of-input"); + yyLexLen = strlen(yyLex); + return Tok_Eoi; +} + +void Tokenizer::initialize(const Config &config) +{ + QString versionSym = config.getString(CONFIG_VERSIONSYM); + + QString sourceEncoding = config.getString(CONFIG_SOURCEENCODING); + if (sourceEncoding.isEmpty()) + sourceEncoding = QLatin1String("ISO-8859-1"); + sourceCodec = QTextCodec::codecForName(sourceEncoding.toLocal8Bit()); + + comment = new QRegExp("/(?:\\*.*\\*/|/.*\n|/[^\n]*$)"); + comment->setMinimal(true); + versionX = new QRegExp("$cannot possibly match^"); + if (!versionSym.isEmpty()) + versionX->setPattern("[ \t]*(?:" + QRegExp::escape(versionSym) + + ")[ \t]+\"([^\"]*)\"[ \t]*"); + definedX = new QRegExp("defined ?\\(?([A-Z_0-9a-z]+) ?\\)"); + + QStringList d = config.getStringList(CONFIG_DEFINES); + d += "qdoc"; + defines = new QRegExp(d.join("|")); + falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join("|")); + + memset(kwordHashTable, 0, sizeof(kwordHashTable)); + for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++) + insertKwordIntoHash(kwords[i], i + 1); + + ignoredTokensAndDirectives = new QHash<QByteArray, bool>; + + QStringList tokens = config.getStringList(LANGUAGE_CPP + Config::dot + CONFIG_IGNORETOKENS); + foreach (const QString &t, tokens) { + const QByteArray tb = t.toAscii(); + ignoredTokensAndDirectives->insert(tb, false); + insertKwordIntoHash(tb.data(), -1); + } + + QStringList directives = config.getStringList(LANGUAGE_CPP + Config::dot + + CONFIG_IGNOREDIRECTIVES); + foreach (const QString &d, directives) { + const QByteArray db = d.toAscii(); + ignoredTokensAndDirectives->insert(db, true); + insertKwordIntoHash(db.data(), -1); + } +} + +void Tokenizer::terminate() +{ + delete comment; + comment = 0; + delete versionX; + versionX = 0; + delete definedX; + definedX = 0; + delete defines; + defines = 0; + delete falsehoods; + falsehoods = 0; + delete ignoredTokensAndDirectives; + ignoredTokensAndDirectives = 0; +} + +void Tokenizer::init() +{ + yyLexBuf1 = new char[(int) yyLexBufSize]; + yyLexBuf2 = new char[(int) yyLexBufSize]; + yyPrevLex = yyLexBuf1; + yyPrevLex[0] = '\0'; + yyLex = yyLexBuf2; + yyLex[0] = '\0'; + yyLexLen = 0; + yyPreprocessorSkipping.push(false); + yyNumPreprocessorSkipping = 0; + yyBraceDepth = 0; + yyParenDepth = 0; + yyBracketDepth = 0; + yyCh = '\0'; + parsingMacro = false; +} + +void Tokenizer::start(const Location& loc) +{ + yyTokLoc = loc; + yyCurLoc = loc; + yyCurLoc.start(); + strcpy(yyPrevLex, "beginning-of-input"); + strcpy(yyLex, "beginning-of-input"); + yyLexLen = strlen(yyLex); + yyBraceDepth = 0; + yyParenDepth = 0; + yyBracketDepth = 0; + yyCh = '\0'; + yyCh = getChar(); +} + +/* + Returns the next token, if # was met. This function interprets the + preprocessor directive, skips over any #ifdef'd out tokens, and returns the + token after all of that. +*/ +int Tokenizer::getTokenAfterPreprocessor() +{ + yyCh = getChar(); + while (isspace(yyCh) && yyCh != '\n') + yyCh = getChar(); + + /* + #directive condition + */ + QString directive; + QString condition; + + while (isalpha(yyCh)) { + directive += QChar(yyCh); + yyCh = getChar(); + } + if (!directive.isEmpty()) { + while (yyCh != EOF && yyCh != '\n') { + if (yyCh == '\\') + yyCh = getChar(); + condition += yyCh; + yyCh = getChar(); + } + condition.remove(*comment); + condition = condition.simplified(); + + /* + The #if, #ifdef, #ifndef, #elif, #else, and #endif + directives have an effect on the skipping stack. For + instance, if the code processed so far is + + #if 1 + #if 0 + #if 1 + // ... + #else + + the skipping stack contains, from bottom to top, false true + true (assuming 0 is false and 1 is true). If at least one + entry of the stack is true, the tokens are skipped. + + This mechanism is simple yet hard to understand. + */ + if (directive[0] == QChar('i')) { + if (directive == QString("if")) + pushSkipping(!isTrue(condition)); + else if (directive == QString("ifdef")) + pushSkipping(!defines->exactMatch(condition)); + else if (directive == QString("ifndef")) + pushSkipping(defines->exactMatch(condition)); + } else if (directive[0] == QChar('e')) { + if (directive == QString("elif")) { + bool old = popSkipping(); + if (old) + pushSkipping(!isTrue(condition)); + else + pushSkipping(true); + } else if (directive == QString("else")) { + pushSkipping(!popSkipping()); + } else if (directive == QString("endif")) { + popSkipping(); + } + } else if (directive == QString("define")) { + if (versionX->exactMatch(condition)) + yyVersion = versionX->cap(1); + } + } + + int tok; + do { + /* + We set yyLex now, and after getToken() this will be + yyPrevLex. This way, we skip over the preprocessor + directive. + */ + qstrcpy(yyLex, yyPrevLex); + + /* + If getToken() meets another #, it will call + getTokenAfterPreprocessor() once again, which could in turn + call getToken() again, etc. Unless there are 10,000 or so + preprocessor directives in a row, this shouldn't overflow + the stack. + */ + tok = getToken(); + } while (yyNumPreprocessorSkipping > 0 && tok != Tok_Eoi); + return tok; +} + +/* + Pushes a new skipping value onto the stack. This corresponds to entering a + new #if block. +*/ +void Tokenizer::pushSkipping(bool skip) +{ + yyPreprocessorSkipping.push(skip); + if (skip) + yyNumPreprocessorSkipping++; +} + +/* + Pops a skipping value from the stack. This corresponds to reaching a #endif. +*/ +bool Tokenizer::popSkipping() +{ + if (yyPreprocessorSkipping.isEmpty()) { + yyTokLoc.warning(tr("Unexpected #elif, #else or #endif")); + return true; + } + + bool skip = yyPreprocessorSkipping.pop(); + if (skip) + yyNumPreprocessorSkipping--; + return skip; +} + +/* + Returns true if the condition evaluates as true, otherwise false. The + condition is represented by a string. Unsophisticated parsing techniques are + used. The preprocessing method could be named StriNg-Oriented PreProcessing, + as SNOBOL stands for StriNg-Oriented symBOlic Language. +*/ +bool Tokenizer::isTrue(const QString &condition) +{ + int firstOr = -1; + int firstAnd = -1; + int parenDepth = 0; + + /* + Find the first logical operator at top level, but be careful + about precedence. Examples: + + X || Y // the or + X || Y || Z // the leftmost or + X || Y && Z // the or + X && Y || Z // the or + (X || Y) && Z // the and + */ + for (int i = 0; i < (int) condition.length() - 1; i++) { + QChar ch = condition[i]; + if (ch == QChar('(')) { + parenDepth++; + } else if (ch == QChar(')')) { + parenDepth--; + } else if (parenDepth == 0) { + if (condition[i + 1] == ch) { + if (ch == QChar('|')) { + firstOr = i; + break; + } else if (ch == QChar('&')) { + if (firstAnd == -1) + firstAnd = i; + } + } + } + } + if (firstOr != -1) + return isTrue(condition.left(firstOr)) || + isTrue(condition.mid(firstOr + 2)); + if (firstAnd != -1) + return isTrue(condition.left(firstAnd)) && + isTrue(condition.mid(firstAnd + 2)); + + QString t = condition.simplified(); + if (t.isEmpty()) + return true; + + if (t[0] == QChar('!')) + return !isTrue(t.mid(1)); + if (t[0] == QChar('(') && t.endsWith(QChar(')'))) + return isTrue(t.mid(1, t.length() - 2)); + + if (definedX->exactMatch(t)) + return defines->exactMatch(definedX->cap(1)); + else + return !falsehoods->exactMatch(t); +} + +QString Tokenizer::lexeme() const +{ + return sourceCodec->toUnicode(yyLex); +} + +QString Tokenizer::previousLexeme() const +{ + return sourceCodec->toUnicode(yyPrevLex); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/tokenizer.h b/src/tools/qdoc/tokenizer.h new file mode 100644 index 0000000000..b6d6e1d9ca --- /dev/null +++ b/src/tools/qdoc/tokenizer.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + tokenizer.h +*/ + +#ifndef TOKENIZER_H +#define TOKENIZER_H + +#include <qfile.h> +#include <qstack.h> +#include <qstring.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +/* + Here come the C++ tokens we support. The first part contains + all-purpose tokens; then come keywords. + + If you add a keyword, make sure to modify the keyword array in + tokenizer.cpp as well, and possibly adjust Tok_FirstKeyword and + Tok_LastKeyword. +*/ +enum { Tok_Eoi, Tok_Ampersand, Tok_Aster, Tok_Caret, Tok_LeftParen, + Tok_RightParen, Tok_LeftParenAster, Tok_Equal, Tok_LeftBrace, + Tok_RightBrace, Tok_Semicolon, Tok_Colon, Tok_LeftAngle, + Tok_RightAngle, Tok_Comma, Tok_Ellipsis, Tok_Gulbrandsen, + Tok_LeftBracket, Tok_RightBracket, Tok_Tilde, Tok_SomeOperator, + Tok_Number, Tok_String, Tok_Doc, Tok_Comment, Tok_Ident, Tok_At, + Tok_char, Tok_class, Tok_const, Tok_double, Tok_enum, + Tok_explicit, Tok_friend, Tok_inline, Tok_int, Tok_long, + Tok_namespace, Tok_operator, Tok_private, Tok_protected, + Tok_public, Tok_short, Tok_signals, Tok_signed, Tok_slots, + Tok_static, Tok_struct, Tok_template, Tok_typedef, + Tok_typename, Tok_union, Tok_unsigned, Tok_using, Tok_virtual, + Tok_void, Tok_volatile, Tok_int64, Tok_Q_OBJECT, Tok_Q_OVERRIDE, + Tok_Q_PROPERTY, Tok_Q_PRIVATE_PROPERTY, Tok_Q_DECLARE_SEQUENTIAL_ITERATOR, + Tok_Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR, + Tok_Q_DECLARE_ASSOCIATIVE_ITERATOR, + Tok_Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR, + Tok_Q_DECLARE_FLAGS, Tok_Q_SIGNALS, Tok_Q_SLOTS, Tok_QT_COMPAT, + Tok_QT_COMPAT_CONSTRUCTOR, Tok_QT_DEPRECATED, Tok_QT_MOC_COMPAT, + Tok_QT_MODULE, Tok_QT3_SUPPORT, Tok_QT3_SUPPORT_CONSTRUCTOR, + Tok_QT3_MOC_SUPPORT, Tok_QDOC_PROPERTY, + Tok_FirstKeyword = Tok_char, Tok_LastKeyword = Tok_QDOC_PROPERTY }; + +/* + The Tokenizer class implements lexical analysis of C++ source + files. + + Not every operator or keyword of C++ is recognized; only those + that are interesting to us. Some Qt keywords or macros are also + recognized. +*/ + +class Tokenizer +{ +public: + Tokenizer(const Location& loc, const QByteArray &in); + Tokenizer(const Location& loc, QFile &file); + + ~Tokenizer(); + + int getToken(); + void setParsingFnOrMacro(bool macro) { parsingMacro = macro; } + bool parsingFnOrMacro() const { return parsingMacro; } + + const Location &location() const { return yyTokLoc; } + QString previousLexeme() const; + QString lexeme() const; + QString version() const { return yyVersion; } + int braceDepth() const { return yyBraceDepth; } + int parenDepth() const { return yyParenDepth; } + int bracketDepth() const { return yyBracketDepth; } + + static void initialize(const Config &config); + static void terminate(); + static bool isTrue(const QString &condition); + +private: + void init(); + void start(const Location& loc); + /* + This limit on the length of a lexeme seems fairly high, but a + doc comment can be arbitrarily long. The previous 65,536 limit + was reached by Mark Summerfield. + */ + enum { yyLexBufSize = 524288 }; + + int getch() + { + return yyPos == yyIn.size() ? EOF : yyIn[yyPos++]; + } + + inline int getChar() + { + if (yyCh == EOF) + return EOF; + if (yyLexLen < yyLexBufSize - 1) { + yyLex[yyLexLen++] = (char) yyCh; + yyLex[yyLexLen] = '\0'; + } + yyCurLoc.advance(yyCh); + int ch = getch(); + if (ch == EOF) + return EOF; + // cast explicitly to make sure the value of ch + // is in range [0..255] to avoid assert messages + // when using debug CRT that checks its input. + return int(uint(uchar(ch))); + } + + int getTokenAfterPreprocessor(); + void pushSkipping(bool skip); + bool popSkipping(); + + Location yyTokLoc; + Location yyCurLoc; + char *yyLexBuf1; + char *yyLexBuf2; + char *yyPrevLex; + char *yyLex; + size_t yyLexLen; + QStack<bool> yyPreprocessorSkipping; + int yyNumPreprocessorSkipping; + int yyBraceDepth; + int yyParenDepth; + int yyBracketDepth; + int yyCh; + + QString yyVersion; + bool parsingMacro; + +protected: + QByteArray yyIn; + int yyPos; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/tr.h b/src/tools/qdoc/tr.h new file mode 100644 index 0000000000..f01e645ca6 --- /dev/null +++ b/src/tools/qdoc/tr.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + tr.h +*/ + +#ifndef TR_H +#define TR_H + +#ifndef QT_BOOTSTRAPPED +# include "qcoreapplication.h" +#endif + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +#if defined(QT_BOOTSTRAPPED) || defined(QT_NO_TRANSLATION) +inline QString tr(const char *sourceText, const char *comment = 0) +{ + Q_UNUSED(comment); + return QString( QLatin1String(sourceText) ); +} +#else +inline QString tr(const char *sourceText, const char *comment = 0) +{ + return QCoreApplication::instance()->translate("", sourceText, comment); +} +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp new file mode 100644 index 0000000000..c52e45739a --- /dev/null +++ b/src/tools/qdoc/tree.cpp @@ -0,0 +1,2358 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + tree.cpp +*/ + +#include <QDomDocument> +#include "atom.h" +#include "doc.h" +#include "htmlgenerator.h" +#include "location.h" +#include "node.h" +#include "text.h" +#include "tree.h" +#include <limits.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +struct InheritanceBound +{ + Node::Access access; + QStringList basePath; + QString dataTypeWithTemplateArgs; + InnerNode* parent; + + InheritanceBound() + : access(Node::Public) { } + InheritanceBound(Node::Access access0, + const QStringList& basePath0, + const QString& dataTypeWithTemplateArgs0, + InnerNode* parent) + : access(access0), basePath(basePath0), + dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0), + parent(parent) { } +}; + +struct Target +{ + Node* node; + Atom* atom; + int priority; +}; + +typedef QMap<PropertyNode::FunctionRole, QString> RoleMap; +typedef QMap<PropertyNode*, RoleMap> PropertyMap; +typedef QMultiHash<QString, FakeNode*> FakeNodeHash; +typedef QMultiHash<QString, Target> TargetHash; + +class TreePrivate +{ +public: + QMap<ClassNode* , QList<InheritanceBound> > unresolvedInheritanceMap; + PropertyMap unresolvedPropertyMap; + NodeMultiMap groupMap; + NodeMultiMap qmlModuleMap; + QMultiMap<QString, QString> publicGroupMap; + FakeNodeHash fakeNodesByTitle; + TargetHash targetHash; + QList<QPair<ClassNode*,QString> > basesList; + QList<QPair<FunctionNode*,QString> > relatedList; +}; + +/*! + \class Tree + + This class constructs and maintains a tree of instances of + Node and its many subclasses. + */ + +/*! + The default constructor is the only constructor. + */ +Tree::Tree() + : roo(0, "") +{ + priv = new TreePrivate; +} + +/*! + The destructor deletes the internal, private tree. + */ +Tree::~Tree() +{ + delete priv; +} + +/*! + This function simply calls the const version of itself and + returns the result. + */ +Node* Tree::findNode(const QStringList& path, Node* relative, int findFlags, const Node* self) +{ + return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path, + relative, + findFlags, + self)); +} + +/*! + Searches the tree for a node that matches the \a path. The + search begins at \a start but can move up the parent chain + recursively if no match is found. + */ +const Node* Tree::findNode(const QStringList& path, + const Node* start, + int findFlags, + const Node* self) const +{ + const Node* current = start; + if (!current) + current = root(); + + /* + First, search for a node assuming we don't want a QML node. + If that search fails, search again assuming we do want a + QML node. + */ + const Node* n = findNode(path,current,findFlags,self,false); + if (!n) { + n = findNode(path,current,findFlags,self,true); + } + return n; +} + +/*! + This code in this function was extracted from the other + version of findNode() that has the same signature without + the last bool parameter \a qml. This function is called + only by that other findNode(). It can be called a second + time if the first call returns null. If \a qml is false, + the search will only match a node that is not a QML node. + If \a qml is true, the search will only match a node that + is a QML node. + */ +const Node* Tree::findNode(const QStringList& path, + const Node* start, + int findFlags, + const Node* self, + bool qml) const +{ + const Node* current = start; + do { + const Node* node = current; + int i; + int start_idx = 0; + + /* + If the path contains one or two double colons ("::"), + check first to see if the first two path strings refer + to a QML element. If yes, that reference identifies a + QML class node. + */ + if (qml && path.size() >= 2) { + QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]); + if (qcn) { + node = qcn; + if (path.size() == 2) + return node; + start_idx = 2; + } + } + + for (i = start_idx; i < path.size(); ++i) { + if (node == 0 || !node->isInnerNode()) + break; + + const Node* next = static_cast<const InnerNode*>(node)->findNode(path.at(i), qml); + + if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) + next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); + + if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); + foreach (const Node* baseClass, baseClasses) { + next = static_cast<const InnerNode*>(baseClass)->findNode(path.at(i)); + if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) + next = static_cast<const InnerNode*>(baseClass) + ->findEnumNodeForValue(path.at(i)); + if (next) + break; + } + } + node = next; + } + if (node && i == path.size() + && (!(findFlags & NonFunction) || node->type() != Node::Function + || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) { + if ((node != self) && (node->subType() != Node::QmlPropertyGroup)) { + if (node->subType() == Node::Collision) { + node = node->applyModuleIdentifier(start); + } + return node; + } + } + current = current->parent(); + } while (current); + + return 0; +} + +/*! + Find the node with the specified \a path name of the + specified \a type. + */ +Node* Tree::findNode(const QStringList& path, + Node::Type type, + Node* relative, + int findFlags) +{ + return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path, + type, + relative, + findFlags)); +} + +/*! + This function just calls the const version of itself and returns + a pointer to the QML class node, or null. + */ +QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& element) +{ + return const_cast<QmlClassNode*>(const_cast<const Tree*>(this)->findQmlClassNode(module, element)); +} + +/*! + Find the node with the specified \a path name of the + specified \a type. + */ +const Node* Tree::findNode(const QStringList& path, + Node::Type type, + const Node* relative, + int findFlags) const +{ + const Node* node = findNode(path, relative, findFlags); + if (node != 0 && node->type() == type) + return node; + return 0; +} + +/*! + Find the QML class node for the specified \a module and \a name + identifiers. The \a module identifier may be empty. If the module + identifier is empty, then begin by finding the FakeNode that has + the specified \a name. If that FakeNode is a QML class, return it. + If it is a collision node, return its current child, if the current + child is a QML class. If the collision node does not have a child + that is a QML class node, return 0. + */ +const QmlClassNode* Tree::findQmlClassNode(const QString& module, const QString& name) const +{ + if (module.isEmpty()) { + const Node* n = findNode(QStringList(name), Node::Fake); + if (n) { + if (n->subType() == Node::QmlClass) + return static_cast<const QmlClassNode*>(n); + else if (n->subType() == Node::Collision) { + const NameCollisionNode* ncn; + ncn = static_cast<const NameCollisionNode*>(n); + return static_cast<const QmlClassNode*>(ncn->findAny(Node::Fake,Node::QmlClass)); + } + } + return 0; + } + return QmlClassNode::moduleMap.value(module + "::" + name); +} + +/*! + First, search for a node with the specified \a name. If a matching + node is found, if it is a collision node, another collision with + this name has been found, so return the collision node. If the + matching node is not a collision node, the first collision for this + name has been found, so create a NameCollisionNode with the matching + node as its first child, and return a pointer to the new + NameCollisionNode. Otherwise return 0. + */ +NameCollisionNode* Tree::checkForCollision(const QString& name) const +{ + Node* n = const_cast<Node*>(findNode(QStringList(name))); + if (n) { + if (n->subType() == Node::Collision) { + NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n); + return ncn; + } + if (n->isInnerNode()) + return new NameCollisionNode(static_cast<InnerNode*>(n)); + } + return 0; +} + +/*! + This function is like checkForCollision() in that it searches + for a collision node with the specified \a name. But it doesn't + create anything. If it finds a match, it returns the pointer. + Otherwise it returns 0. + */ +NameCollisionNode* Tree::findCollisionNode(const QString& name) const +{ + Node* n = const_cast<Node*>(findNode(QStringList(name))); + if (n) { + if (n->subType() == Node::Collision) { + NameCollisionNode* ncn = static_cast<NameCollisionNode*>(n); + return ncn; + } + } + return 0; +} + +/*! + This function just calls the const version of the same function + and returns the function node. + */ +FunctionNode* Tree::findFunctionNode(const QStringList& path, + Node* relative, + int findFlags) +{ + return const_cast<FunctionNode*> + (const_cast<const Tree*>(this)->findFunctionNode(path,relative,findFlags)); +} + +/*! + This function begins searching the tree at \a relative for + the \l {FunctionNode} {function node} identified by \a path. + The \a findFlags are used to restrict the search. If a node + that matches the \a path is found, it is returned. Otherwise, + 0 is returned. If \a relative is 0, the root of the tree is + used as the starting point. + */ +const FunctionNode* Tree::findFunctionNode(const QStringList& path, + const Node* relative, + int findFlags) const +{ + if (!relative) + relative = root(); + + /* + If the path contains two double colons ("::"), check + first to see if it is a reference to a QML method. If + it is a reference to a QML method, first look up the + QML class node in the QML module map. + */ + if (path.size() == 3) { + QmlClassNode* qcn = QmlClassNode::moduleMap.value(path[0]+ "::" +path[1]); + if (qcn) { + return static_cast<const FunctionNode*>(qcn->findFunctionNode(path[2])); + } + } + + do { + const Node* node = relative; + int i; + + for (i = 0; i < path.size(); ++i) { + if (node == 0 || !node->isInnerNode()) + break; + + const Node* next; + if (i == path.size() - 1) + next = ((InnerNode*) node)->findFunctionNode(path.at(i)); + else + next = ((InnerNode*) node)->findNode(path.at(i)); + + if (!next && node->type() == Node::Class && + (findFlags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast<const ClassNode*>(node)); + foreach (const Node* baseClass, baseClasses) { + if (i == path.size() - 1) + next = static_cast<const InnerNode*>(baseClass)->findFunctionNode(path.at(i)); + else + next = static_cast<const InnerNode*>(baseClass)->findNode(path.at(i)); + + if (next) + break; + } + } + + node = next; + } + if (node && i == path.size() && node->isFunction()) { + // CppCodeParser::processOtherMetaCommand ensures that reimplemented + // functions are private. + const FunctionNode* func = static_cast<const FunctionNode*>(node); + while (func->access() == Node::Private) { + const FunctionNode* from = func->reimplementedFrom(); + if (from != 0) { + if (from->access() != Node::Private) + return from; + else + func = from; + } + else + break; + } + return func; + } + relative = relative->parent(); + } while (relative); + + return 0; +} + +/*! + This function just calls the const version of itself and + returns the result. + */ +FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, + const FunctionNode* clone, + Node* relative, + int findFlags) +{ + return const_cast<FunctionNode*>( + const_cast<const Tree*>(this)->findFunctionNode(parentPath, + clone, + relative, + findFlags)); +} + +/*! + This function first ignores the \a clone node and searches + for the node having the \a parentPath by calling the main + findFunction(\a {parentPath}, \a {relative}, \a {findFlags}). + If that search is successful, then it searches for the \a clone + in the found parent node. + */ +const FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, + const FunctionNode* clone, + const Node* relative, + int findFlags) const +{ + const Node* parent = findNode(parentPath, relative, findFlags); + if (parent == 0 || !parent->isInnerNode()) { + return 0; + } + else { + return ((InnerNode*)parent)->findFunctionNode(clone); + } +} + +static const int NumSuffixes = 3; +static const char* const suffixes[NumSuffixes] = { "", "s", "es" }; + +/*! + This function searches for a node with the specified \a title. + If \a relative is provided, use it to disambiguate if it has a + QML module identifier. + */ +const FakeNode* Tree::findFakeNodeByTitle(const QString& title, const Node* relative ) const +{ + for (int pass = 0; pass < NumSuffixes; ++pass) { + FakeNodeHash::const_iterator i = priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass])); + if (i != priv->fakeNodesByTitle.constEnd()) { + if (relative && !relative->qmlModuleIdentifier().isEmpty()) { + const FakeNode* fn = i.value(); + InnerNode* parent = fn->parent(); + if (parent && parent->type() == Node::Fake && parent->subType() == Node::Collision) { + const NodeList& nl = parent->childNodes(); + NodeList::ConstIterator it = nl.begin(); + while (it != nl.end()) { + if ((*it)->qmlModuleIdentifier() == relative->qmlModuleIdentifier()) { + /* + By returning here, we avoid printing all the duplicate + header warnings, which are not really duplicates now, + because of the QML module identifier being used as a + namespace qualifier. + */ + fn = static_cast<const FakeNode*>(*it); + return fn; + } + ++it; + } + } + } + /* + Reporting all these duplicate section titles is probably + overkill. We should report the duplicate file and let + that suffice. + */ + FakeNodeHash::const_iterator j = i; + ++j; + if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) { + QList<Location> internalLocations; + while (j != priv->fakeNodesByTitle.constEnd()) { + if (j.key() == i.key() && j.value()->url().isEmpty()) + internalLocations.append(j.value()->doc().location()); + ++j; + } + if (internalLocations.size() > 0) { + i.value()->doc().location().warning( + tr("Page '%1' defined in more than one location:").arg(title)); + foreach (const Location &location, internalLocations) + location.warning(tr("(defined here)")); + } + } + return i.value(); + } + } + return 0; +} + +/*! + This function searches for a \a target anchor node. If it + finds one, it sets \a atom from the found node and returns + the found node. + */ +const Node* +Tree::findUnambiguousTarget(const QString& target, Atom *&atom, const Node* relative) const +{ + Target bestTarget = {0, 0, INT_MAX}; + int numBestTargets = 0; + QList<Target> bestTargetList; + + for (int pass = 0; pass < NumSuffixes; ++pass) { + TargetHash::const_iterator i = priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass])); + if (i != priv->targetHash.constEnd()) { + TargetHash::const_iterator j = i; + do { + const Target& candidate = j.value(); + if (candidate.priority < bestTarget.priority) { + bestTarget = candidate; + bestTargetList.clear(); + bestTargetList.append(candidate); + numBestTargets = 1; + } else if (candidate.priority == bestTarget.priority) { + bestTargetList.append(candidate); + ++numBestTargets; + } + ++j; + } while (j != priv->targetHash.constEnd() && j.key() == i.key()); + + if (numBestTargets == 1) { + atom = bestTarget.atom; + return bestTarget.node; + } + else if (bestTargetList.size() > 1) { + if (relative && !relative->qmlModuleIdentifier().isEmpty()) { + for (int i=0; i<bestTargetList.size(); ++i) { + const Node* n = bestTargetList.at(i).node; + if (relative->qmlModuleIdentifier() == n->qmlModuleIdentifier()) { + atom = bestTargetList.at(i).atom; + return n; + } + } + } + } + } + } + return 0; +} + +/*! + This function searches for a node with a canonical title + constructed from \a target and each of the possible suffixes. + If the node it finds is \a node, it returns the Atom from that + node. Otherwise it returns null. + */ +Atom* Tree::findTarget(const QString& target, const Node* node) const +{ + for (int pass = 0; pass < NumSuffixes; ++pass) { + QString key = Doc::canonicalTitle(target + suffixes[pass]); + TargetHash::const_iterator i = priv->targetHash.find(key); + + if (i != priv->targetHash.constEnd()) { + do { + if (i.value().node == node) + return i.value().atom; + ++i; + } while (i != priv->targetHash.constEnd() && i.key() == key); + } + } + return 0; +} + +/*! + */ +void Tree::addBaseClass(ClassNode* subclass, Node::Access access, + const QStringList& basePath, + const QString& dataTypeWithTemplateArgs, + InnerNode* parent) +{ + priv->unresolvedInheritanceMap[subclass].append( + InheritanceBound(access, + basePath, + dataTypeWithTemplateArgs, + parent) + ); +} + +/*! + */ +void Tree::addPropertyFunction(PropertyNode* property, + const QString& funcName, + PropertyNode::FunctionRole funcRole) +{ + priv->unresolvedPropertyMap[property].insert(funcRole, funcName); +} + +/*! + This function adds the \a node to the \a group. The group + can be listed anywhere using the \e{annotated list} command. + */ +void Tree::addToGroup(Node* node, const QString& group) +{ + priv->groupMap.insert(group, node); +} + +/*! + This function adds the \a node to the QML \a module. The QML + module can be listed anywhere using the \e{annotated list} + command. + */ +void Tree::addToQmlModule(Node* node, const QString& module) +{ + priv->qmlModuleMap.insert(module, node); +} + +/*! + Returns the group map. + */ +NodeMultiMap Tree::groups() const +{ + return priv->groupMap; +} + +/*! + Returns the QML module map. + */ +NodeMultiMap Tree::qmlModules() const +{ + return priv->qmlModuleMap; +} + +/*! + */ +void Tree::addToPublicGroup(Node* node, const QString& group) +{ + priv->publicGroupMap.insert(node->name(), group); + addToGroup(node, group); +} + +/*! + */ +QMultiMap<QString, QString> Tree::publicGroups() const +{ + return priv->publicGroupMap; +} + +/*! + */ +void Tree::resolveInheritance(NamespaceNode* rootNode) +{ + if (!rootNode) + rootNode = root(); + + for (int pass = 0; pass < 2; pass++) { + NodeList::ConstIterator c = rootNode->childNodes().begin(); + while (c != rootNode->childNodes().end()) { + if ((*c)->type() == Node::Class) { + resolveInheritance(pass, (ClassNode*)* c); + } + else if ((*c)->type() == Node::Namespace) { + NamespaceNode* ns = static_cast<NamespaceNode*>(*c); + resolveInheritance(ns); + } + ++c; + } + if (rootNode == root()) + priv->unresolvedInheritanceMap.clear(); + } +} + +/*! + */ +void Tree::resolveProperties() +{ + PropertyMap::ConstIterator propEntry; + + propEntry = priv->unresolvedPropertyMap.begin(); + while (propEntry != priv->unresolvedPropertyMap.end()) { + PropertyNode* property = propEntry.key(); + InnerNode* parent = property->parent(); + QString getterName = (*propEntry)[PropertyNode::Getter]; + QString setterName = (*propEntry)[PropertyNode::Setter]; + QString resetterName = (*propEntry)[PropertyNode::Resetter]; + QString notifierName = (*propEntry)[PropertyNode::Notifier]; + + NodeList::ConstIterator c = parent->childNodes().begin(); + while (c != parent->childNodes().end()) { + if ((*c)->type() == Node::Function) { + FunctionNode* function = static_cast<FunctionNode*>(*c); + if (function->access() == property->access() && + (function->status() == property->status() || + function->doc().isEmpty())) { + if (function->name() == getterName) { + property->addFunction(function, PropertyNode::Getter); + } + else if (function->name() == setterName) { + property->addFunction(function, PropertyNode::Setter); + } + else if (function->name() == resetterName) { + property->addFunction(function, PropertyNode::Resetter); + } + else if (function->name() == notifierName) { + property->addSignal(function, PropertyNode::Notifier); + } + } + } + ++c; + } + ++propEntry; + } + + propEntry = priv->unresolvedPropertyMap.begin(); + while (propEntry != priv->unresolvedPropertyMap.end()) { + PropertyNode* property = propEntry.key(); + // redo it to set the property functions + if (property->overriddenFrom()) + property->setOverriddenFrom(property->overriddenFrom()); + ++propEntry; + } + + priv->unresolvedPropertyMap.clear(); +} + +/*! + */ +void Tree::resolveInheritance(int pass, ClassNode* classe) +{ + if (pass == 0) { + QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe]; + QList<InheritanceBound>::ConstIterator b = bounds.begin(); + while (b != bounds.end()) { + ClassNode* baseClass = (ClassNode*)findNode((*b).basePath, + Node::Class); + if (!baseClass && (*b).parent) { + baseClass = (ClassNode*)findNode((*b).basePath, + Node::Class, + (*b).parent); + } + if (baseClass) { + classe->addBaseClass((*b).access, + baseClass, + (*b).dataTypeWithTemplateArgs); + } + ++b; + } + } + else { + NodeList::ConstIterator c = classe->childNodes().begin(); + while (c != classe->childNodes().end()) { + if ((*c)->type() == Node::Function) { + FunctionNode* func = (FunctionNode*)* c; + FunctionNode* from = findVirtualFunctionInBaseClasses(classe, func); + if (from != 0) { + if (func->virtualness() == FunctionNode::NonVirtual) + func->setVirtualness(FunctionNode::ImpureVirtual); + func->setReimplementedFrom(from); + } + } + else if ((*c)->type() == Node::Property) { + fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode*>(*c)); + } + ++c; + } + } +} + +/*! + For each node in the group map, add the node to the appropriate + group node. + */ +void Tree::resolveGroups() +{ + NodeMultiMap::const_iterator i; + for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) { + if (i.value()->access() == Node::Private) + continue; + + FakeNode* fake = + static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake)); + if (fake && fake->subType() == Node::Group) { + fake->addGroupMember(i.value()); + } + } +} + +/*! + For each node in the QML module map, add the node to the + appropriate QML module node. + */ +void Tree::resolveQmlModules() +{ + NodeMultiMap::const_iterator i; + for (i = priv->qmlModuleMap.constBegin(); i != priv->qmlModuleMap.constEnd(); ++i) { + FakeNode* fake = + static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake)); + if (fake && fake->subType() == Node::QmlModule) { + fake->addQmlModuleMember(i.value()); + } + } +} + +/*! + */ +void Tree::resolveTargets(InnerNode* root) +{ + // need recursion + + foreach (Node* child, root->childNodes()) { + if (child->type() == Node::Fake) { + FakeNode* node = static_cast<FakeNode*>(child); + if (!node->title().isEmpty()) + priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node); + if (node->subType() == Node::Collision) { + resolveTargets(node); + } + } + + if (child->doc().hasTableOfContents()) { + const QList<Atom*>& toc = child->doc().tableOfContents(); + Target target; + target.node = child; + target.priority = 3; + + for (int i = 0; i < toc.size(); ++i) { + target.atom = toc.at(i); + QString title = Text::sectionHeading(target.atom).toString(); + if (!title.isEmpty()) + priv->targetHash.insert(Doc::canonicalTitle(title), target); + } + } + if (child->doc().hasKeywords()) { + const QList<Atom*>& keywords = child->doc().keywords(); + Target target; + target.node = child; + target.priority = 1; + + for (int i = 0; i < keywords.size(); ++i) { + target.atom = keywords.at(i); + priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + if (child->doc().hasTargets()) { + const QList<Atom*>& toc = child->doc().targets(); + Target target; + target.node = child; + target.priority = 2; + + for (int i = 0; i < toc.size(); ++i) { + target.atom = toc.at(i); + priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + } +} + +/*! + For each QML class node that points to a C++ class node, + follow its C++ class node pointer and set the C++ class + node's QML class node pointer back to the QML class node. + */ +void Tree::resolveCppToQmlLinks() +{ + + foreach (Node* child, roo.childNodes()) { + if (child->type() == Node::Fake && child->subType() == Node::QmlClass) { + QmlClassNode* qcn = static_cast<QmlClassNode*>(child); + ClassNode* cn = const_cast<ClassNode*>(qcn->classNode()); + if (cn) + cn->setQmlElement(qcn); + } + } +} + +/*! + For each QML class node in the tree, determine whether + it inherits a QML base class and, if so, which one, and + store that pointer in the QML class node's state. + */ +void Tree::resolveQmlInheritance() +{ + + foreach (Node* child, roo.childNodes()) { + if (child->type() == Node::Fake) { + if (child->subType() == Node::QmlClass) { + QmlClassNode* qcn = static_cast<QmlClassNode*>(child); + qcn->resolveInheritance(this); + } + else if (child->subType() == Node::Collision) { + NameCollisionNode* ncn = static_cast<NameCollisionNode*>(child); + foreach (Node* child, ncn->childNodes()) { + if (child->type() == Node::Fake) { + if (child->subType() == Node::QmlClass) { + QmlClassNode* qcn = static_cast<QmlClassNode*>(child); + qcn->resolveInheritance(this); + } + } + } + } + } + } +} + +/*! + */ +void Tree::fixInheritance(NamespaceNode* rootNode) +{ + if (!rootNode) + rootNode = root(); + + NodeList::ConstIterator c = rootNode->childNodes().begin(); + while (c != rootNode->childNodes().end()) { + if ((*c)->type() == Node::Class) + static_cast<ClassNode*>(*c)->fixBaseClasses(); + else if ((*c)->type() == Node::Namespace) { + NamespaceNode* ns = static_cast<NamespaceNode*>(*c); + fixInheritance(ns); + } + ++c; + } +} + +/*! + */ +FunctionNode* Tree::findVirtualFunctionInBaseClasses(ClassNode* classe, + FunctionNode* clone) +{ + QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin(); + while (r != classe->baseClasses().end()) { + FunctionNode* func; + if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 || + (func = (*r).node->findFunctionNode(clone)) != 0)) { + if (func->virtualness() != FunctionNode::NonVirtual) + return func; + } + ++r; + } + return 0; +} + +/*! + */ +void Tree::fixPropertyUsingBaseClasses(ClassNode* classe, + PropertyNode* property) +{ + QList<RelatedClass>::const_iterator r = classe->baseClasses().begin(); + while (r != classe->baseClasses().end()) { + PropertyNode* baseProperty = + static_cast<PropertyNode*>(r->node->findNode(property->name(), + Node::Property)); + if (baseProperty) { + fixPropertyUsingBaseClasses(r->node, baseProperty); + property->setOverriddenFrom(baseProperty); + } + else { + fixPropertyUsingBaseClasses(r->node, property); + } + ++r; + } +} + +/*! + */ +NodeList Tree::allBaseClasses(const ClassNode* classe) const +{ + NodeList result; + foreach (const RelatedClass& r, classe->baseClasses()) { + result += r.node; + result += allBaseClasses(r.node); + } + return result; +} + +/*! + */ +void Tree::readIndexes(const QStringList& indexFiles) +{ + foreach (const QString& indexFile, indexFiles) + readIndexFile(indexFile); +} + +/*! + Read the QDomDocument at \a path and get the index from it. + */ +void Tree::readIndexFile(const QString& path) +{ + QFile file(path); + if (file.open(QFile::ReadOnly)) { + QDomDocument document; + document.setContent(&file); + file.close(); + + QDomElement indexElement = document.documentElement(); + QString indexUrl = indexElement.attribute("url", ""); + priv->basesList.clear(); + priv->relatedList.clear(); + + // Scan all elements in the XML file, constructing a map that contains + // base classes for each class found. + + QDomElement child = indexElement.firstChildElement(); + while (!child.isNull()) { + readIndexSection(child, root(), indexUrl); + child = child.nextSiblingElement(); + } + + // Now that all the base classes have been found for this index, + // arrange them into an inheritance hierarchy. + + resolveIndex(); + } +} + +/*! + */ +void Tree::readIndexSection(const QDomElement& element, + InnerNode* parent, + const QString& indexUrl) +{ + QString name = element.attribute("name"); + QString href = element.attribute("href"); + + Node* section; + Location location; + + if (element.nodeName() == "namespace") { + section = new NamespaceNode(parent, name); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(name.toLower() + ".html"); + + } + else if (element.nodeName() == "class") { + section = new ClassNode(parent, name); + priv->basesList.append(QPair<ClassNode*,QString>( + static_cast<ClassNode*>(section), element.attribute("bases"))); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name.toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(name.toLower() + ".html"); + + } + else if ((element.nodeName() == "qmlclass") || + ((element.nodeName() == "page") && (element.attribute("subtype") == "qmlclass"))) { + QmlClassNode* qcn = new QmlClassNode(parent, name, 0); + qcn->setTitle(element.attribute("title")); + if (element.hasAttribute("location")) + name = element.attribute("location", ""); + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name); + else if (!indexUrl.isNull()) + location = Location(name); + section = qcn; + } + else if (element.nodeName() == "qmlbasictype") { + QmlBasicTypeNode* qbtn = new QmlBasicTypeNode(parent, name); + qbtn->setTitle(element.attribute("title")); + if (element.hasAttribute("location")) + name = element.attribute("location", ""); + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name); + else if (!indexUrl.isNull()) + location = Location(name); + section = qbtn; + } + else if (element.nodeName() == "page") { + Node::SubType subtype; + Node::PageType ptype = Node::NoPageType; + if (element.attribute("subtype") == "example") { + subtype = Node::Example; + ptype = Node::ExamplePage; + } + else if (element.attribute("subtype") == "header") { + subtype = Node::HeaderFile; + ptype = Node::ApiPage; + } + else if (element.attribute("subtype") == "file") { + subtype = Node::File; + ptype = Node::NoPageType; + } + else if (element.attribute("subtype") == "group") { + subtype = Node::Group; + ptype = Node::OverviewPage; + } + else if (element.attribute("subtype") == "module") { + subtype = Node::Module; + ptype = Node::OverviewPage; + } + else if (element.attribute("subtype") == "page") { + subtype = Node::Page; + ptype = Node::ArticlePage; + } + else if (element.attribute("subtype") == "externalpage") { + subtype = Node::ExternalPage; + ptype = Node::ArticlePage; + } + else if (element.attribute("subtype") == "qmlclass") { + subtype = Node::QmlClass; + ptype = Node::ApiPage; + } + else if (element.attribute("subtype") == "qmlpropertygroup") { + subtype = Node::QmlPropertyGroup; + ptype = Node::ApiPage; + } + else if (element.attribute("subtype") == "qmlbasictype") { + subtype = Node::QmlBasicType; + ptype = Node::ApiPage; + } + else + return; + + FakeNode* fakeNode = new FakeNode(parent, name, subtype, ptype); + fakeNode->setTitle(element.attribute("title")); + + if (element.hasAttribute("location")) + name = element.attribute("location", ""); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + name); + else if (!indexUrl.isNull()) + location = Location(name); + + section = fakeNode; + + } + else if (element.nodeName() == "enum") { + EnumNode* enumNode = new EnumNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + QDomElement child = element.firstChildElement("value"); + while (!child.isNull()) { + EnumItem item(child.attribute("name"), child.attribute("value")); + enumNode->addItem(item); + child = child.nextSiblingElement("value"); + } + + section = enumNode; + + } else if (element.nodeName() == "typedef") { + section = new TypedefNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "property") { + section = new PropertyNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } else if (element.nodeName() == "function") { + FunctionNode::Virtualness virt; + if (element.attribute("virtual") == "non") + virt = FunctionNode::NonVirtual; + else if (element.attribute("virtual") == "impure") + virt = FunctionNode::ImpureVirtual; + else if (element.attribute("virtual") == "pure") + virt = FunctionNode::PureVirtual; + else + return; + + FunctionNode::Metaness meta; + if (element.attribute("meta") == "plain") + meta = FunctionNode::Plain; + else if (element.attribute("meta") == "signal") + meta = FunctionNode::Signal; + else if (element.attribute("meta") == "slot") + meta = FunctionNode::Slot; + else if (element.attribute("meta") == "constructor") + meta = FunctionNode::Ctor; + else if (element.attribute("meta") == "destructor") + meta = FunctionNode::Dtor; + else if (element.attribute("meta") == "macro") + meta = FunctionNode::MacroWithParams; + else if (element.attribute("meta") == "macrowithparams") + meta = FunctionNode::MacroWithParams; + else if (element.attribute("meta") == "macrowithoutparams") + meta = FunctionNode::MacroWithoutParams; + else + return; + + FunctionNode* functionNode = new FunctionNode(parent, name); + functionNode->setReturnType(element.attribute("return")); + functionNode->setVirtualness(virt); + functionNode->setMetaness(meta); + functionNode->setConst(element.attribute("const") == "true"); + functionNode->setStatic(element.attribute("static") == "true"); + functionNode->setOverload(element.attribute("overload") == "true"); + + if (element.hasAttribute("relates") + && element.attribute("relates") != parent->name()) { + priv->relatedList.append( + QPair<FunctionNode*,QString>(functionNode, + element.attribute("relates"))); + } + + QDomElement child = element.firstChildElement("parameter"); + while (!child.isNull()) { + // Do not use the default value for the parameter; it is not + // required, and has been known to cause problems. + Parameter parameter(child.attribute("left"), + child.attribute("right"), + child.attribute("name"), + ""); // child.attribute("default") + functionNode->addParameter(parameter); + child = child.nextSiblingElement("parameter"); + } + + section = functionNode; + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "variable") { + section = new VariableNode(parent, name); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + QLatin1Char('/') + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "keyword") { + Target target; + target.node = parent; + target.priority = 1; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else if (element.nodeName() == "target") { + Target target; + target.node = parent; + target.priority = 2; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else if (element.nodeName() == "contents") { + Target target; + target.node = parent; + target.priority = 3; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else + return; + + QString access = element.attribute("access"); + if (access == "public") + section->setAccess(Node::Public); + else if (access == "protected") + section->setAccess(Node::Protected); + else if (access == "private") + section->setAccess(Node::Private); + else + section->setAccess(Node::Public); + + if ((element.nodeName() != "page") && + (element.nodeName() != "qmlclass") && + (element.nodeName() != "qmlbasictype")) { + QString threadSafety = element.attribute("threadsafety"); + if (threadSafety == "non-reentrant") + section->setThreadSafeness(Node::NonReentrant); + else if (threadSafety == "reentrant") + section->setThreadSafeness(Node::Reentrant); + else if (threadSafety == "thread safe") + section->setThreadSafeness(Node::ThreadSafe); + else + section->setThreadSafeness(Node::UnspecifiedSafeness); + } + else + section->setThreadSafeness(Node::UnspecifiedSafeness); + + QString status = element.attribute("status"); + if (status == "compat") + section->setStatus(Node::Compat); + else if (status == "obsolete") + section->setStatus(Node::Obsolete); + else if (status == "deprecated") + section->setStatus(Node::Deprecated); + else if (status == "preliminary") + section->setStatus(Node::Preliminary); + else if (status == "commendable") + section->setStatus(Node::Commendable); + else if (status == "internal") + section->setStatus(Node::Internal); + else if (status == "main") + section->setStatus(Node::Main); + else + section->setStatus(Node::Commendable); + + section->setModuleName(element.attribute("module")); + if (!indexUrl.isEmpty()) { + if (indexUrl.startsWith(QLatin1Char('.'))) + section->setUrl(href); + else + section->setUrl(indexUrl + QLatin1Char('/') + href); + } + + // Create some content for the node. + QSet<QString> emptySet; + + Doc doc(location, location, " ", emptySet); // placeholder + section->setDoc(doc); + + if (section->isInnerNode()) { + InnerNode* inner = static_cast<InnerNode*>(section); + if (inner) { + QDomElement child = element.firstChildElement(); + + while (!child.isNull()) { + if (element.nodeName() == "class") + readIndexSection(child, inner, indexUrl); + else if (element.nodeName() == "qmlclass") + readIndexSection(child, inner, indexUrl); + else if (element.nodeName() == "page") + readIndexSection(child, inner, indexUrl); + else if (element.nodeName() == "namespace" && !name.isEmpty()) + // The root node in the index is a namespace with an empty name. + readIndexSection(child, inner, indexUrl); + else + readIndexSection(child, parent, indexUrl); + + child = child.nextSiblingElement(); + } + } + } +} + +/*! + */ +QString Tree::readIndexText(const QDomElement& element) +{ + QString text; + QDomNode child = element.firstChild(); + while (!child.isNull()) { + if (child.isText()) + text += child.toText().nodeValue(); + child = child.nextSibling(); + } + return text; +} + +/*! + */ +void Tree::resolveIndex() +{ + QPair<ClassNode*,QString> pair; + + foreach (pair, priv->basesList) { + foreach (const QString& base, pair.second.split(QLatin1Char(','))) { + Node* baseClass = root()->findNode(base, Node::Class); + if (baseClass) { + pair.first->addBaseClass(Node::Public, + static_cast<ClassNode*>(baseClass)); + } + } + } + + QPair<FunctionNode*,QString> relatedPair; + + foreach (relatedPair, priv->relatedList) { + Node* classNode = root()->findNode(relatedPair.second, Node::Class); + if (classNode) + relatedPair.first->setRelates(static_cast<ClassNode*>(classNode)); + } +} + +/*! + Generate the index section with the given \a writer for the \a node + specified, returning true if an element was written; otherwise returns + false. + */ +bool Tree::generateIndexSection(QXmlStreamWriter& writer, + const Node* node, + bool generateInternalNodes) const +{ + if (!node->url().isEmpty()) + return false; + + QString nodeName; + switch (node->type()) { + case Node::Namespace: + nodeName = "namespace"; + break; + case Node::Class: + nodeName = "class"; + break; + case Node::Fake: + nodeName = "page"; + if (node->subType() == Node::QmlClass) + nodeName = "qmlclass"; + else if (node->subType() == Node::QmlBasicType) + nodeName = "qmlbasictype"; + break; + case Node::Enum: + nodeName = "enum"; + break; + case Node::Typedef: + nodeName = "typedef"; + break; + case Node::Property: + nodeName = "property"; + break; + case Node::Function: + nodeName = "function"; + break; + case Node::Variable: + nodeName = "variable"; + break; + case Node::Target: + nodeName = "target"; + break; + case Node::QmlProperty: + nodeName = "qmlproperty"; + break; + case Node::QmlSignal: + nodeName = "qmlsignal"; + break; + case Node::QmlSignalHandler: + nodeName = "qmlsignalhandler"; + break; + case Node::QmlMethod: + nodeName = "qmlmethod"; + break; + default: + return false; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + // Do not include private non-internal nodes in the index. + // (Internal public and protected nodes are marked as private + // by qdoc. We can check their internal status to determine + // whether they were really private to begin with.) + if (node->status() == Node::Internal && generateInternalNodes) + access = "internal"; + else + return false; + break; + default: + return false; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + return false; + + writer.writeStartElement(nodeName); + + QXmlStreamAttributes attributes; + writer.writeAttribute("access", access); + + if (node->type() != Node::Fake) { + QString threadSafety; + switch (node->threadSafeness()) { + case Node::NonReentrant: + threadSafety = "non-reentrant"; + break; + case Node::Reentrant: + threadSafety = "reentrant"; + break; + case Node::ThreadSafe: + threadSafety = "thread safe"; + break; + case Node::UnspecifiedSafeness: + default: + threadSafety = "unspecified"; + break; + } + writer.writeAttribute("threadsafety", threadSafety); + } + + QString status; + switch (node->status()) { + case Node::Compat: + status = "compat"; + break; + case Node::Obsolete: + status = "obsolete"; + break; + case Node::Deprecated: + status = "deprecated"; + break; + case Node::Preliminary: + status = "preliminary"; + break; + case Node::Commendable: + status = "commendable"; + break; + case Node::Internal: + status = "internal"; + break; + case Node::Main: + default: + status = "main"; + break; + } + writer.writeAttribute("status", status); + + writer.writeAttribute("name", objName); + QString fullName = node->fullDocumentName(); + if (fullName != objName) + writer.writeAttribute("fullname", fullName); + QString href = node->outputSubdirectory(); + if (!href.isEmpty()) + href.append(QLatin1Char('/')); + href.append(HtmlGenerator::fullDocumentLocation(node)); + writer.writeAttribute("href", href); + if ((node->type() != Node::Fake) && (!node->isQmlNode())) + writer.writeAttribute("location", node->location().fileName()); + + switch (node->type()) { + + case Node::Class: + { + // Classes contain information about their base classes. + + const ClassNode* classNode = static_cast<const ClassNode*>(node); + QList<RelatedClass> bases = classNode->baseClasses(); + QSet<QString> baseStrings; + foreach (const RelatedClass& related, bases) { + ClassNode* baseClassNode = related.node; + baseStrings.insert(baseClassNode->name()); + } + writer.writeAttribute("bases", QStringList(baseStrings.toList()).join(",")); + writer.writeAttribute("module", node->moduleName()); + } + break; + + case Node::Namespace: + writer.writeAttribute("module", node->moduleName()); + break; + + case Node::Fake: + { + /* + Fake nodes (such as manual pages) contain subtypes, + titles and other attributes. + */ + + const FakeNode* fakeNode = static_cast<const FakeNode*>(node); + switch (fakeNode->subType()) { + case Node::Example: + writer.writeAttribute("subtype", "example"); + break; + case Node::HeaderFile: + writer.writeAttribute("subtype", "header"); + break; + case Node::File: + writer.writeAttribute("subtype", "file"); + break; + case Node::Group: + writer.writeAttribute("subtype", "group"); + break; + case Node::Module: + writer.writeAttribute("subtype", "module"); + break; + case Node::Page: + writer.writeAttribute("subtype", "page"); + break; + case Node::ExternalPage: + writer.writeAttribute("subtype", "externalpage"); + break; + case Node::QmlClass: + //writer.writeAttribute("subtype", "qmlclass"); + break; + case Node::QmlBasicType: + //writer.writeAttribute("subtype", "qmlbasictype"); + break; + default: + break; + } + writer.writeAttribute("title", fakeNode->title()); + writer.writeAttribute("fulltitle", fakeNode->fullTitle()); + writer.writeAttribute("subtitle", fakeNode->subTitle()); + writer.writeAttribute("location", fakeNode->doc().location().fileName()); + } + break; + + case Node::Function: + { + /* + Function nodes contain information about the type of + function being described. + */ + + const FunctionNode* functionNode = + static_cast<const FunctionNode*>(node); + + switch (functionNode->virtualness()) { + case FunctionNode::NonVirtual: + writer.writeAttribute("virtual", "non"); + break; + case FunctionNode::ImpureVirtual: + writer.writeAttribute("virtual", "impure"); + break; + case FunctionNode::PureVirtual: + writer.writeAttribute("virtual", "pure"); + break; + default: + break; + } + switch (functionNode->metaness()) { + case FunctionNode::Plain: + writer.writeAttribute("meta", "plain"); + break; + case FunctionNode::Signal: + writer.writeAttribute("meta", "signal"); + break; + case FunctionNode::Slot: + writer.writeAttribute("meta", "slot"); + break; + case FunctionNode::Ctor: + writer.writeAttribute("meta", "constructor"); + break; + case FunctionNode::Dtor: + writer.writeAttribute("meta", "destructor"); + break; + case FunctionNode::MacroWithParams: + writer.writeAttribute("meta", "macrowithparams"); + break; + case FunctionNode::MacroWithoutParams: + writer.writeAttribute("meta", "macrowithoutparams"); + break; + default: + break; + } + writer.writeAttribute("const", functionNode->isConst()?"true":"false"); + writer.writeAttribute("static", functionNode->isStatic()?"true":"false"); + writer.writeAttribute("overload", functionNode->isOverload()?"true":"false"); + if (functionNode->isOverload()) + writer.writeAttribute("overload-number", QString::number(functionNode->overloadNumber())); + if (functionNode->relates()) + writer.writeAttribute("relates", functionNode->relates()->name()); + const PropertyNode* propertyNode = functionNode->associatedProperty(); + if (propertyNode) + writer.writeAttribute("associated-property", propertyNode->name()); + writer.writeAttribute("type", functionNode->returnType()); + } + break; + + case Node::QmlProperty: + { + const QmlPropertyNode* qpn = static_cast<const QmlPropertyNode*>(node); + writer.writeAttribute("type", qpn->dataType()); + writer.writeAttribute("attached", qpn->isAttached() ? "true" : "false"); + writer.writeAttribute("writable", qpn->isWritable(this) ? "true" : "false"); + } + break; + case Node::Property: + { + const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node); + writer.writeAttribute("type", propertyNode->dataType()); + foreach (const Node* fnNode, propertyNode->getters()) { + if (fnNode) { + const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode); + writer.writeStartElement("getter"); + writer.writeAttribute("name", functionNode->name()); + writer.writeEndElement(); // getter + } + } + foreach (const Node* fnNode, propertyNode->setters()) { + if (fnNode) { + const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode); + writer.writeStartElement("setter"); + writer.writeAttribute("name", functionNode->name()); + writer.writeEndElement(); // getter + } + } + foreach (const Node* fnNode, propertyNode->resetters()) { + if (fnNode) { + const FunctionNode* functionNode = static_cast<const FunctionNode*>(fnNode); + writer.writeStartElement("resetter"); + writer.writeAttribute("name", functionNode->name()); + writer.writeEndElement(); // getter + } + } + } + break; + + case Node::Variable: + { + const VariableNode* variableNode = + static_cast<const VariableNode*>(node); + writer.writeAttribute("type", variableNode->dataType()); + writer.writeAttribute("static", + variableNode->isStatic() ? "true" : "false"); + } + break; + default: + break; + } + + // Inner nodes and function nodes contain child nodes of some sort, either + // actual child nodes or function parameters. For these, we close the + // opening tag, create child elements, then add a closing tag for the + // element. Elements for all other nodes are closed in the opening tag. + + if (node->isInnerNode()) { + + const InnerNode* inner = static_cast<const InnerNode*>(node); + + // For internal pages, we canonicalize the target, keyword and content + // item names so that they can be used by qdoc for other sets of + // documentation. + // The reason we do this here is that we don't want to ruin + // externally composed indexes, containing non-qdoc-style target names + // when reading in indexes. + + if (inner->doc().hasTargets()) { + bool external = false; + if (inner->type() == Node::Fake) { + const FakeNode* fakeNode = static_cast<const FakeNode*>(inner); + if (fakeNode->subType() == Node::ExternalPage) + external = true; + } + + foreach (const Atom* target, inner->doc().targets()) { + QString targetName = target->string(); + if (!external) + targetName = Doc::canonicalTitle(targetName); + + writer.writeStartElement("target"); + writer.writeAttribute("name", targetName); + writer.writeEndElement(); // target + } + } + if (inner->doc().hasKeywords()) { + foreach (const Atom* keyword, inner->doc().keywords()) { + writer.writeStartElement("keyword"); + writer.writeAttribute("name", + Doc::canonicalTitle(keyword->string())); + writer.writeEndElement(); // keyword + } + } + if (inner->doc().hasTableOfContents()) { + for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) { + Atom* item = inner->doc().tableOfContents()[i]; + int level = inner->doc().tableOfContentsLevels()[i]; + + QString title = Text::sectionHeading(item).toString(); + writer.writeStartElement("contents"); + writer.writeAttribute("name", Doc::canonicalTitle(title)); + writer.writeAttribute("title", title); + writer.writeAttribute("level", QString::number(level)); + writer.writeEndElement(); // contents + } + } + + } + else if (node->type() == Node::Function) { + + const FunctionNode* functionNode = static_cast<const FunctionNode*>(node); + // Write a signature attribute for convenience. + QStringList signatureList; + QStringList resolvedParameters; + + foreach (const Parameter& parameter, functionNode->parameters()) { + QString leftType = parameter.leftType(); + const Node* leftNode = + const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"), + Node::Typedef, 0, SearchBaseClasses|NonFunction); + if (!leftNode) { + leftNode = const_cast<Tree*>(this)->findNode( + parameter.leftType().split("::"), Node::Typedef, + node->parent(), SearchBaseClasses|NonFunction); + } + if (leftNode) { + if (leftNode->type() == Node::Typedef) { + const TypedefNode* typedefNode = + static_cast<const TypedefNode*>(leftNode); + if (typedefNode->associatedEnum()) { + leftType = "QFlags<" + + typedefNode->associatedEnum()->fullDocumentName() + + QLatin1Char('>'); + } + } + else + leftType = leftNode->fullDocumentName(); + } + resolvedParameters.append(leftType); + signatureList.append(leftType + QLatin1Char(' ') + parameter.name()); + } + + QString signature = functionNode->name()+QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')'); + if (functionNode->isConst()) + signature += " const"; + writer.writeAttribute("signature", signature); + + for (int i = 0; i < functionNode->parameters().size(); ++i) { + Parameter parameter = functionNode->parameters()[i]; + writer.writeStartElement("parameter"); + writer.writeAttribute("left", resolvedParameters[i]); + writer.writeAttribute("right", parameter.rightType()); + writer.writeAttribute("name", parameter.name()); + writer.writeAttribute("default", parameter.defaultValue()); + writer.writeEndElement(); // parameter + } + + } + else if (node->type() == Node::Enum) { + + const EnumNode* enumNode = static_cast<const EnumNode*>(node); + if (enumNode->flagsType()) { + writer.writeAttribute("typedef",enumNode->flagsType()->fullDocumentName()); + } + foreach (const EnumItem& item, enumNode->items()) { + writer.writeStartElement("value"); + writer.writeAttribute("name", item.name()); + writer.writeAttribute("value", item.value()); + writer.writeEndElement(); // value + } + + } + else if (node->type() == Node::Typedef) { + + const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node); + if (typedefNode->associatedEnum()) { + writer.writeAttribute("enum",typedefNode->associatedEnum()->fullDocumentName()); + } + } + + return true; +} + + +/*! + Returns true if the node \a n1 is less than node \a n2. + The comparison is performed by comparing properties of the nodes in order + of increasing complexity. +*/ +bool compareNodes(const Node* n1, const Node* n2) +{ + // Private nodes can occur in any order since they won't normally be + // written to the index. + if (n1->access() == Node::Private && n2->access() == Node::Private) + return true; + + if (n1->location().filePath() < n2->location().filePath()) + return true; + else if (n1->location().filePath() > n2->location().filePath()) + return false; + + if (n1->type() < n2->type()) + return true; + else if (n1->type() > n2->type()) + return false; + + if (n1->name() < n2->name()) + return true; + else if (n1->name() > n2->name()) + return false; + + if (n1->access() < n2->access()) + return true; + else if (n1->access() > n2->access()) + return false; + + if (n1->type() == Node::Function && n2->type() == Node::Function) { + const FunctionNode* f1 = static_cast<const FunctionNode*>(n1); + const FunctionNode* f2 = static_cast<const FunctionNode*>(n2); + + if (f1->isConst() < f2->isConst()) + return true; + else if (f1->isConst() > f2->isConst()) + return false; + + if (f1->signature() < f2->signature()) + return true; + else if (f1->signature() > f2->signature()) + return false; + } + + if (n1->type() == Node::Fake && n2->type() == Node::Fake) { + const FakeNode* f1 = static_cast<const FakeNode*>(n1); + const FakeNode* f2 = static_cast<const FakeNode*>(n2); + if (f1->fullTitle() < f2->fullTitle()) + return true; + else if (f1->fullTitle() > f2->fullTitle()) + return false; + } + + return false; +} + +/*! + Generate index sections for the child nodes of the given \a node + using the \a writer specified. If \a generateInternalNodes is true, + nodes marked as internal will be included in the index; otherwise, + they will be omitted. +*/ +void Tree::generateIndexSections(QXmlStreamWriter& writer, + const Node* node, + bool generateInternalNodes) const +{ + if (generateIndexSection(writer, node, generateInternalNodes)) { + + if (node->isInnerNode()) { + const InnerNode* inner = static_cast<const InnerNode*>(node); + + NodeList cnodes = inner->childNodes(); + qSort(cnodes.begin(), cnodes.end(), compareNodes); + + foreach (const Node* child, cnodes) { + /* + Don't generate anything for a QML property group node. + It is just a place holder for a collection of QML property + nodes. Recurse to its children, which are the QML property + nodes. + */ + if (child->subType() == Node::QmlPropertyGroup) { + const InnerNode* pgn = static_cast<const InnerNode*>(child); + foreach (const Node* c, pgn->childNodes()) { + generateIndexSections(writer, c, generateInternalNodes); + } + } + else + generateIndexSections(writer, child, generateInternalNodes); + } + + /* + foreach (const Node* child, inner->relatedNodes()) { + QDomElement childElement = generateIndexSections(document, child); + element.appendChild(childElement); + } +*/ + } + writer.writeEndElement(); + } +} + +/*! + Outputs an index file. + */ +void Tree::generateIndex(const QString& fileName, + const QString& url, + const QString& title, + bool generateInternalNodes) const +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeDTD("<!DOCTYPE QDOCINDEX>"); + + writer.writeStartElement("INDEX"); + writer.writeAttribute("url", url); + writer.writeAttribute("title", title); + writer.writeAttribute("version", version()); + + generateIndexSections(writer, root(), generateInternalNodes); + + writer.writeEndElement(); // INDEX + writer.writeEndElement(); // QDOCINDEX + writer.writeEndDocument(); + file.close(); +} + +/*! + Generate the tag file section with the given \a writer for the \a node + specified, returning true if an element was written; otherwise returns + false. + */ +void Tree::generateTagFileCompounds(QXmlStreamWriter& writer, + const InnerNode* inner) const +{ + foreach (const Node* node, inner->childNodes()) { + + if (!node->url().isEmpty()) + continue; + + QString kind; + switch (node->type()) { + case Node::Namespace: + kind = "namespace"; + break; + case Node::Class: + kind = "class"; + break; + case Node::Enum: + case Node::Typedef: + case Node::Property: + case Node::Function: + case Node::Variable: + case Node::Target: + default: + continue; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + default: + continue; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + continue; + + // *** Write the starting tag for the element here. *** + writer.writeStartElement("compound"); + writer.writeAttribute("kind", kind); + + if (node->type() == Node::Class) { + writer.writeTextElement("name", node->fullDocumentName()); + writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true)); + + // Classes contain information about their base classes. + const ClassNode* classNode = static_cast<const ClassNode*>(node); + QList<RelatedClass> bases = classNode->baseClasses(); + foreach (const RelatedClass& related, bases) { + ClassNode* baseClassNode = related.node; + writer.writeTextElement("base", baseClassNode->name()); + } + + // Recurse to write all members. + generateTagFileMembers(writer, static_cast<const InnerNode*>(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast<const InnerNode*>(node)); + } else { + writer.writeTextElement("name", node->fullDocumentName()); + writer.writeTextElement("filename", HtmlGenerator::fullDocumentLocation(node,true)); + + // Recurse to write all members. + generateTagFileMembers(writer, static_cast<const InnerNode*>(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast<const InnerNode*>(node)); + } + } +} + +/*! + */ +void Tree::generateTagFileMembers(QXmlStreamWriter& writer, + const InnerNode* inner) const +{ + foreach (const Node* node, inner->childNodes()) { + + if (!node->url().isEmpty()) + continue; + + QString nodeName; + QString kind; + switch (node->type()) { + case Node::Enum: + nodeName = "member"; + kind = "enum"; + break; + case Node::Typedef: + nodeName = "member"; + kind = "typedef"; + break; + case Node::Property: + nodeName = "member"; + kind = "property"; + break; + case Node::Function: + nodeName = "member"; + kind = "function"; + break; + case Node::Namespace: + nodeName = "namespace"; + break; + case Node::Class: + nodeName = "class"; + break; + case Node::Variable: + case Node::Target: + default: + continue; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + default: + continue; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + continue; + + // *** Write the starting tag for the element here. *** + writer.writeStartElement(nodeName); + if (!kind.isEmpty()) + writer.writeAttribute("kind", kind); + + switch (node->type()) { + + case Node::Class: + writer.writeCharacters(node->fullDocumentName()); + writer.writeEndElement(); + break; + case Node::Namespace: + writer.writeCharacters(node->fullDocumentName()); + writer.writeEndElement(); + break; + case Node::Function: + { + /* + Function nodes contain information about + the type of function being described. + */ + + const FunctionNode* functionNode = + static_cast<const FunctionNode*>(node); + writer.writeAttribute("protection", access); + + switch (functionNode->virtualness()) { + case FunctionNode::NonVirtual: + writer.writeAttribute("virtualness", "non"); + break; + case FunctionNode::ImpureVirtual: + writer.writeAttribute("virtualness", "virtual"); + break; + case FunctionNode::PureVirtual: + writer.writeAttribute("virtual", "pure"); + break; + default: + break; + } + writer.writeAttribute("static", + functionNode->isStatic() ? "yes" : "no"); + + if (functionNode->virtualness() == FunctionNode::NonVirtual) + writer.writeTextElement("type", functionNode->returnType()); + else + writer.writeTextElement("type", + "virtual " + functionNode->returnType()); + + writer.writeTextElement("name", objName); + QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#')); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + + // Write a signature attribute for convenience. + QStringList signatureList; + + foreach (const Parameter& parameter, functionNode->parameters()) { + QString leftType = parameter.leftType(); + const Node* leftNode = const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"), + Node::Typedef, 0, SearchBaseClasses|NonFunction); + if (!leftNode) { + leftNode = const_cast<Tree*>(this)->findNode( + parameter.leftType().split("::"), Node::Typedef, + node->parent(), SearchBaseClasses|NonFunction); + } + if (leftNode) { + const TypedefNode* typedefNode = static_cast<const TypedefNode*>(leftNode); + if (typedefNode->associatedEnum()) { + leftType = "QFlags<" + + typedefNode->associatedEnum()->fullDocumentName() + + QLatin1Char('>'); + } + } + signatureList.append(leftType + QLatin1Char(' ') + parameter.name()); + } + + QString signature = QLatin1Char('(')+signatureList.join(", ")+QLatin1Char(')'); + if (functionNode->isConst()) + signature += " const"; + if (functionNode->virtualness() == FunctionNode::PureVirtual) + signature += " = 0"; + writer.writeTextElement("arglist", signature); + } + writer.writeEndElement(); // member + break; + + case Node::Property: + { + const PropertyNode* propertyNode = static_cast<const PropertyNode*>(node); + writer.writeAttribute("type", propertyNode->dataType()); + writer.writeTextElement("name", objName); + QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#')); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + } + writer.writeEndElement(); // member + break; + + case Node::Enum: + { + const EnumNode* enumNode = static_cast<const EnumNode*>(node); + writer.writeTextElement("name", objName); + QStringList pieces = HtmlGenerator::fullDocumentLocation(node).split(QLatin1Char('#')); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + writer.writeEndElement(); // member + + for (int i = 0; i < enumNode->items().size(); ++i) { + EnumItem item = enumNode->items().value(i); + writer.writeStartElement("member"); + writer.writeAttribute("name", item.name()); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + writer.writeEndElement(); // member + } + } + break; + + case Node::Typedef: + { + const TypedefNode* typedefNode = static_cast<const TypedefNode*>(node); + if (typedefNode->associatedEnum()) + writer.writeAttribute("type", typedefNode->associatedEnum()->fullDocumentName()); + else + writer.writeAttribute("type", ""); + writer.writeTextElement("name", objName); + QStringList pieces = HtmlGenerator::fullDocumentLocation(node,true).split(QLatin1Char('#')); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + } + writer.writeEndElement(); // member + break; + + case Node::Variable: + case Node::Target: + default: + break; + } + } +} + +/*! + */ +void Tree::generateTagFile(const QString& fileName) const +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + + writer.writeStartElement("tagfile"); + + generateTagFileCompounds(writer, root()); + + writer.writeEndElement(); // tagfile + writer.writeEndDocument(); + file.close(); +} + +/*! + */ +void Tree::addExternalLink(const QString& url, const Node* relative) +{ + FakeNode* fakeNode = new FakeNode(root(), url, Node::ExternalPage, Node::ArticlePage); + fakeNode->setAccess(Node::Public); + + // Create some content for the node. + QSet<QString> emptySet; + Location location(relative->doc().location()); + Doc doc(location, location, " ", emptySet); // placeholder + fakeNode->setDoc(doc); +} + +QT_END_NAMESPACE diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h new file mode 100644 index 0000000000..d39babfab0 --- /dev/null +++ b/src/tools/qdoc/tree.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + tree.h +*/ + +#ifndef TREE_H +#define TREE_H + +#include "node.h" +#include <QDomElement> +#include <QXmlStreamWriter> + +QT_BEGIN_NAMESPACE + +class QStringList; +class TreePrivate; + +class Tree +{ +public: + enum FindFlag { SearchBaseClasses = 0x1, + SearchEnumValues = 0x2, + NonFunction = 0x4 }; + + Tree(); + ~Tree(); + + Node* findNode(const QStringList &path, + Node* relative=0, + int findFlags=0, + const Node* self=0); + Node* findNode(const QStringList &path, + Node::Type type, + Node* relative = 0, + int findFlags = 0); + const Node* findNode(const QStringList& path, + const Node* start, + int findFlags, + const Node* self, + bool qml) const; + QmlClassNode* findQmlClassNode(const QString& module, const QString& name); + NameCollisionNode* checkForCollision(const QString& name) const; + NameCollisionNode* findCollisionNode(const QString& name) const; + FunctionNode *findFunctionNode(const QStringList &path, + Node *relative = 0, + int findFlags = 0); + FunctionNode *findFunctionNode(const QStringList &parentPath, + const FunctionNode *clone, + Node *relative = 0, + int findFlags = 0); + void addBaseClass(ClassNode *subclass, + Node::Access access, + const QStringList &basePath, + const QString &dataTypeWithTemplateArgs, + InnerNode *parent = 0); + void addPropertyFunction(PropertyNode *property, + const QString &funcName, + PropertyNode::FunctionRole funcRole); + void addToGroup(Node *node, const QString &group); + void addToPublicGroup(Node *node, const QString &group); + void addToQmlModule(Node* node, const QString& module); + NodeMultiMap groups() const; + NodeMultiMap qmlModules() const; + QMultiMap<QString,QString> publicGroups() const; + void resolveInheritance(NamespaceNode *rootNode = 0); + void resolveProperties(); + void resolveGroups(); + void resolveQmlModules(); + void resolveTargets(InnerNode* root); + void resolveCppToQmlLinks(); + void fixInheritance(NamespaceNode *rootNode = 0); + void setVersion(const QString &version) { vers = version; } + NamespaceNode *root() { return &roo; } + + QString version() const { return vers; } + const Node* findNode(const QStringList &path, + const Node* relative = 0, + int findFlags = 0, + const Node* self=0) const; + const Node* findNode(const QStringList &path, + Node::Type type, const + Node* relative = 0, + int findFlags = 0) const; + const QmlClassNode* findQmlClassNode(const QString& module, const QString& element) const; + const FunctionNode *findFunctionNode(const QStringList &path, + const Node *relative = 0, + int findFlags = 0) const; + const FunctionNode *findFunctionNode(const QStringList &parentPath, + const FunctionNode *clone, + const Node *relative = 0, + int findFlags = 0) const; + const FakeNode *findFakeNodeByTitle(const QString &title, const Node* relative = 0) const; + const Node *findUnambiguousTarget(const QString &target, Atom *&atom, const Node* relative) const; + Atom *findTarget(const QString &target, const Node *node) const; + const NamespaceNode *root() const { return &roo; } + void readIndexes(const QStringList &indexFiles); + bool generateIndexSection(QXmlStreamWriter &writer, const Node *node, + bool generateInternalNodes = false) const; + void generateIndexSections(QXmlStreamWriter &writer, const Node *node, + bool generateInternalNodes = false) const; + void generateIndex(const QString &fileName, + const QString &url, + const QString &title, + bool generateInternalNodes = false) const; + void generateTagFileCompounds(QXmlStreamWriter &writer, + const InnerNode *inner) const; + void generateTagFileMembers(QXmlStreamWriter &writer, + const InnerNode *inner) const; + void generateTagFile(const QString &fileName) const; + void addExternalLink(const QString &url, const Node *relative); + QString fullDocumentLocation(const Node *node) const; + void resolveQmlInheritance(); + +private: + void resolveInheritance(int pass, ClassNode *classe); + FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, + FunctionNode *clone); + void fixPropertyUsingBaseClasses(ClassNode *classe, PropertyNode *property); + NodeList allBaseClasses(const ClassNode *classe) const; + void readIndexFile(const QString &path); + void readIndexSection(const QDomElement &element, InnerNode *parent, + const QString &indexUrl); + QString readIndexText(const QDomElement &element); + void resolveIndex(); + +private: + NamespaceNode roo; + QString vers; + TreePrivate *priv; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/tools/qdoc/yyindent.cpp b/src/tools/qdoc/yyindent.cpp new file mode 100644 index 0000000000..3f21ca4177 --- /dev/null +++ b/src/tools/qdoc/yyindent.cpp @@ -0,0 +1,1190 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* + This file is a self-contained interactive indenter for C++ and Qt + Script. + + The general problem of indenting a C++ program is ill posed. On + the one hand, an indenter has to analyze programs written in a + free-form formal language that is best described in terms of + tokens, not characters, not lines. On the other hand, indentation + applies to lines and white space characters matter, and otherwise + the programs to indent are formally invalid in general, as they + are begin edited. + + The approach taken here works line by line. We receive a program + consisting of N lines or more, and we want to compute the + indentation appropriate for the Nth line. Lines beyond the Nth + lines are of no concern to us, so for simplicity we pretend the + program has exactly N lines and we call the Nth line the "bottom + line". Typically, we have to indent the bottom line when it's + still empty, so we concentrate our analysis on the N - 1 lines + that precede. + + By inspecting the (N - 1)-th line, the (N - 2)-th line, ... + backwards, we determine the kind of the bottom line and indent it + accordingly. + + * The bottom line is a comment line. See + bottomLineStartsInCComment() and + indentWhenBottomLineStartsInCComment(). + * The bottom line is a continuation line. See isContinuationLine() + and indentForContinuationLine(). + * The bottom line is a standalone line. See + indentForStandaloneLine(). + + Certain tokens that influence the indentation, notably braces, + are looked for in the lines. This is done by simple string + comparison, without a real tokenizer. Confusing constructs such + as comments and string literals are removed beforehand. +*/ + +#include <qregexp.h> +#include <qstringlist.h> + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +/* + The indenter avoids getting stuck in almost infinite loops by + imposing arbitrary limits on the number of lines it analyzes when + looking for a construct. + + For example, the indenter never considers more than BigRoof lines + backwards when looking for the start of a C-style comment. +*/ +static const int SmallRoof = 40; +static const int BigRoof = 400; + +/* + The indenter supports a few parameters: + + * ppHardwareTabSize is the size of a '\t' in your favorite editor. + * ppIndentSize is the size of an indentation, or software tab + size. + * ppContinuationIndentSize is the extra indent for a continuation + line, when there is nothing to align against on the previous + line. + * ppCommentOffset is the indentation within a C-style comment, + when it cannot be picked up. +*/ + +static int ppHardwareTabSize = 8; +static int ppIndentSize = 4; +static int ppContinuationIndentSize = 8; + +static const int ppCommentOffset = 2; + +void setTabSize( int size ) +{ + ppHardwareTabSize = size; +} + +void setIndentSize( int size ) +{ + ppIndentSize = size; + ppContinuationIndentSize = 2 * size; +} + +static QRegExp *literal = 0; +static QRegExp *label = 0; +static QRegExp *inlineCComment = 0; +static QRegExp *braceX = 0; +static QRegExp *iflikeKeyword = 0; + +/* + Returns the first non-space character in the string t, or + QChar::Null if the string is made only of white space. +*/ +static QChar firstNonWhiteSpace( const QString& t ) +{ + int i = 0; + while ( i < (int) t.length() ) { + if ( !t[i].isSpace() ) + return t[i]; + i++; + } + return QChar::Null; +} + +/* + Returns true if string t is made only of white space; otherwise + returns false. +*/ +static bool isOnlyWhiteSpace( const QString& t ) +{ + return firstNonWhiteSpace( t ).isNull(); +} + +/* + Assuming string t is a line, returns the column number of a given + index. Column numbers and index are identical for strings that don't + contain '\t's. +*/ +int columnForIndex( const QString& t, int index ) +{ + int col = 0; + if ( index > (int) t.length() ) + index = t.length(); + + for ( int i = 0; i < index; i++ ) { + if ( t[i] == QChar('\t') ) { + col = ( (col / ppHardwareTabSize) + 1 ) * ppHardwareTabSize; + } else { + col++; + } + } + return col; +} + +/* + Returns the indentation size of string t. +*/ +int indentOfLine( const QString& t ) +{ + return columnForIndex( t, t.indexOf(firstNonWhiteSpace(t)) ); +} + +/* + Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better + left alone since they break the "index equals column" rule. No + provisions are taken against '\n' or '\r', which shouldn't occur in + t anyway. +*/ +static inline void eraseChar( QString& t, int k, QChar ch ) +{ + if ( t[k] != '\t' ) + t[k] = ch; +} + +/* + Removes some nefast constructs from a code line and returns the + resulting line. +*/ +static QString trimmedCodeLine( const QString& t ) +{ + QString trimmed = t; + int k; + + /* + Replace character and string literals by X's, since they may + contain confusing characters (such as '{' and ';'). "Hello!" is + replaced by XXXXXXXX. The literals are rigourously of the same + length before and after; otherwise, we would break alignment of + continuation lines. + */ + k = 0; + while ( (k = trimmed.indexOf(*literal, k)) != -1 ) { + for ( int i = 0; i < literal->matchedLength(); i++ ) + eraseChar( trimmed, k + i, 'X' ); + k += literal->matchedLength(); + } + + /* + Replace inline C-style comments by spaces. Other comments are + handled elsewhere. + */ + k = 0; + while ( (k = trimmed.indexOf(*inlineCComment, k)) != -1 ) { + for ( int i = 0; i < inlineCComment->matchedLength(); i++ ) + eraseChar( trimmed, k + i, ' ' ); + k += inlineCComment->matchedLength(); + } + + /* + Replace goto and switch labels by whitespace, but be careful + with this case: + + foo1: bar1; + bar2; + */ + while ( trimmed.lastIndexOf(':') != -1 && trimmed.indexOf(*label) != -1 ) { + QString cap1 = label->cap( 1 ); + int pos1 = label->pos( 1 ); + int stop = cap1.length(); + + if ( pos1 + stop < (int) trimmed.length() && ppIndentSize < stop ) + stop = ppIndentSize; + + int i = 0; + while ( i < stop ) { + eraseChar( trimmed, pos1 + i, ' ' ); + i++; + } + while ( i < (int) cap1.length() ) { + eraseChar( trimmed, pos1 + i, ';' ); + i++; + } + } + + /* + Remove C++-style comments. + */ + k = trimmed.indexOf( "//" ); + if ( k != -1 ) + trimmed.truncate( k ); + + return trimmed; +} + +/* + Returns '(' if the last parenthesis is opening, ')' if it is + closing, and QChar::Null if there are no parentheses in t. +*/ +static inline QChar lastParen( const QString& t ) +{ + int i = t.length(); + while ( i > 0 ) { + i--; + if ( t[i] == QChar('(') || t[i] == QChar(')') ) + return t[i]; + } + return QChar::Null; +} + +/* + Returns true if typedIn the same as okayCh or is null; otherwise + returns false. +*/ +static inline bool okay( QChar typedIn, QChar okayCh ) +{ + return typedIn == QChar::Null || typedIn == okayCh; +} + +/* + The "linizer" is a group of functions and variables to iterate + through the source code of the program to indent. The program is + given as a list of strings, with the bottom line being the line + to indent. The actual program might contain extra lines, but + those are uninteresting and not passed over to us. +*/ + +struct LinizerState +{ + QString line; + int braceDepth; + bool leftBraceFollows; + + QStringList::ConstIterator iter; + bool inCComment; + bool pendingRightBrace; +}; + +static QStringList *yyProgram = 0; +static LinizerState *yyLinizerState = 0; + +// shorthands +static const QString *yyLine = 0; +static const int *yyBraceDepth = 0; +static const bool *yyLeftBraceFollows = 0; + +/* + Saves and restores the state of the global linizer. This enables + backtracking. +*/ +#define YY_SAVE() \ + LinizerState savedState = *yyLinizerState +#define YY_RESTORE() \ + *yyLinizerState = savedState + +/* + Advances to the previous line in yyProgram and update yyLine + accordingly. yyLine is cleaned from comments and other damageable + constructs. Empty lines are skipped. +*/ +static bool readLine() +{ + int k; + + yyLinizerState->leftBraceFollows = + ( firstNonWhiteSpace(yyLinizerState->line) == QChar('{') ); + + do { + if ( yyLinizerState->iter == yyProgram->begin() ) { + yyLinizerState->line.clear(); + return false; + } + + --yyLinizerState->iter; + yyLinizerState->line = *yyLinizerState->iter; + + yyLinizerState->line = trimmedCodeLine( yyLinizerState->line ); + + /* + Remove C-style comments that span multiple lines. If the + bottom line starts in a C-style comment, we are not aware + of that and eventually yyLine will contain a slash-aster. + + Notice that both if's can be executed, since + yyLinizerState->inCComment is potentially set to false in + the first if. The order of the if's is also important. + */ + + if ( yyLinizerState->inCComment ) { + QString slashAster( "/*" ); + + k = yyLinizerState->line.indexOf( slashAster ); + if ( k == -1 ) { + yyLinizerState->line.clear(); + } else { + yyLinizerState->line.truncate( k ); + yyLinizerState->inCComment = false; + } + } + + if ( !yyLinizerState->inCComment ) { + QString asterSlash( "*/" ); + + k = yyLinizerState->line.indexOf( asterSlash ); + if ( k != -1 ) { + for ( int i = 0; i < k + 2; i++ ) + eraseChar( yyLinizerState->line, i, ' ' ); + yyLinizerState->inCComment = true; + } + } + + /* + Remove preprocessor directives. + */ + k = 0; + while ( k < (int) yyLinizerState->line.length() ) { + QChar ch = yyLinizerState->line[k]; + if ( ch == QChar('#') ) { + yyLinizerState->line.clear(); + } else if ( !ch.isSpace() ) { + break; + } + k++; + } + + /* + Remove trailing spaces. + */ + k = yyLinizerState->line.length(); + while ( k > 0 && yyLinizerState->line[k - 1].isSpace() ) + k--; + yyLinizerState->line.truncate( k ); + + /* + '}' increment the brace depth and '{' decrements it and not + the other way around, as we are parsing backwards. + */ + yyLinizerState->braceDepth += + yyLinizerState->line.count( '}' ) - + yyLinizerState->line.count( '{' ); + + /* + We use a dirty trick for + + } else ... + + We don't count the '}' yet, so that it's more or less + equivalent to the friendly construct + + } + else ... + */ + if ( yyLinizerState->pendingRightBrace ) + yyLinizerState->braceDepth++; + yyLinizerState->pendingRightBrace = + ( yyLinizerState->line.indexOf(*braceX) == 0 ); + if ( yyLinizerState->pendingRightBrace ) + yyLinizerState->braceDepth--; + } while ( yyLinizerState->line.isEmpty() ); + + return true; +} + +/* + Resets the linizer to its initial state, with yyLine containing the + line above the bottom line of the program. +*/ +static void startLinizer() +{ + yyLinizerState->braceDepth = 0; + yyLinizerState->inCComment = false; + yyLinizerState->pendingRightBrace = false; + + yyLine = &yyLinizerState->line; + yyBraceDepth = &yyLinizerState->braceDepth; + yyLeftBraceFollows = &yyLinizerState->leftBraceFollows; + + yyLinizerState->iter = yyProgram->end(); + --yyLinizerState->iter; + yyLinizerState->line = *yyLinizerState->iter; + readLine(); +} + +/* + Returns true if the start of the bottom line of yyProgram (and + potentially the whole line) is part of a C-style comment; + otherwise returns false. +*/ +static bool bottomLineStartsInCComment() +{ + QString slashAster( "/*" ); + QString asterSlash( "*/" ); + + /* + We could use the linizer here, but that would slow us down + terribly. We are better to trim only the code lines we need. + */ + QStringList::ConstIterator p = yyProgram->end(); + --p; // skip bottom line + + for ( int i = 0; i < BigRoof; i++ ) { + if ( p == yyProgram->begin() ) + return false; + --p; + + if ( (*p).indexOf(slashAster) != -1 || (*p).indexOf(asterSlash) != -1 ) { + QString trimmed = trimmedCodeLine( *p ); + + if ( trimmed.indexOf(slashAster) != -1 ) { + return true; + } else if ( trimmed.indexOf(asterSlash) != -1 ) { + return false; + } + } + } + return false; +} + +/* + Returns the recommended indent for the bottom line of yyProgram + assuming that it starts in a C-style comment, a condition that is + tested elsewhere. + + Essentially, we're trying to align against some text on the + previous line. +*/ +static int indentWhenBottomLineStartsInCComment() +{ + int k = yyLine->lastIndexOf( "/*" ); + if ( k == -1 ) { + /* + We found a normal text line in a comment. Align the + bottom line with the text on this line. + */ + return indentOfLine( *yyLine ); + } else { + /* + The C-style comment starts on this line. If there is + text on the same line, align with it. Otherwise, align + with the slash-aster plus a given offset. + */ + int indent = columnForIndex( *yyLine, k ); + k += 2; + while ( k < (int) yyLine->length() ) { + if ( !(*yyLine)[k].isSpace() ) + return columnForIndex( *yyLine, k ); + k++; + } + return indent + ppCommentOffset; + } +} + +/* + A function called match...() modifies the linizer state. If it + returns true, yyLine is the top line of the matched construct; + otherwise, the linizer is left in an unknown state. + + A function called is...() keeps the linizer state intact. +*/ + +/* + Returns true if the current line (and upwards) forms a braceless + control statement; otherwise returns false. + + The first line of the following example is a "braceless control + statement": + + if ( x ) + y; +*/ +static bool matchBracelessControlStatement() +{ + int delimDepth = 0; + + if ( yyLine->endsWith("else") ) + return true; + + if ( !yyLine->endsWith(QLatin1Char(')')) ) + return false; + + for ( int i = 0; i < SmallRoof; i++ ) { + int j = yyLine->length(); + while ( j > 0 ) { + j--; + QChar ch = (*yyLine)[j]; + + switch ( ch.unicode() ) { + case ')': + delimDepth++; + break; + case '(': + delimDepth--; + if ( delimDepth == 0 ) { + if ( yyLine->indexOf(*iflikeKeyword) != -1 ) { + /* + We have + + if ( x ) + y + + "if ( x )" is not part of the statement + "y". + */ + return true; + } + } + if ( delimDepth == -1 ) { + /* + We have + + if ( (1 + + 2) + + and not + + if ( 1 + + 2 ) + */ + return false; + } + break; + case '{': + case '}': + case ';': + /* + We met a statement separator, but not where we + expected it. What follows is probably a weird + continuation line. Be careful with ';' in for, + though. + */ + if ( ch != QChar(';') || delimDepth == 0 ) + return false; + } + } + + if ( !readLine() ) + break; + } + return false; +} + +/* + Returns true if yyLine is an unfinished line; otherwise returns + false. + + In many places we'll use the terms "standalone line", "unfinished + line" and "continuation line". The meaning of these should be + evident from this code example: + + a = b; // standalone line + c = d + // unfinished line + e + // unfinished continuation line + f + // unfinished continuation line + g; // continuation line +*/ +static bool isUnfinishedLine() +{ + bool unf = false; + + YY_SAVE(); + + if ( yyLine->isEmpty() ) + return false; + + QChar lastCh = (*yyLine)[(int) yyLine->length() - 1]; + if ( QString("{};").indexOf(lastCh) == -1 && !yyLine->endsWith("...") ) { + /* + It doesn't end with ';' or similar. If it's neither + "Q_OBJECT" nor "if ( x )", it must be an unfinished line. + */ + unf = ( yyLine->indexOf("Q_OBJECT") == -1 && + !matchBracelessControlStatement() ); + } else if ( lastCh == QChar(';') ) { + if ( lastParen(*yyLine) == QChar('(') ) { + /* + Exception: + + for ( int i = 1; i < 10; + */ + unf = true; + } else if ( readLine() && yyLine->endsWith(QLatin1Char(';')) && + lastParen(*yyLine) == QChar('(') ) { + /* + Exception: + + for ( int i = 1; + i < 10; + */ + unf = true; + } + } + + YY_RESTORE(); + return unf; +} + +/* + Returns true if yyLine is a continuation line; otherwise returns + false. +*/ +static bool isContinuationLine() +{ + bool cont = false; + + YY_SAVE(); + if ( readLine() ) + cont = isUnfinishedLine(); + YY_RESTORE(); + return cont; +} + +/* + Returns the recommended indent for the bottom line of yyProgram, + assuming it's a continuation line. + + We're trying to align the continuation line against some parenthesis + or other bracked left opened on a previous line, or some interesting + operator such as '='. +*/ +static int indentForContinuationLine() +{ + int braceDepth = 0; + int delimDepth = 0; + + bool leftBraceFollowed = *yyLeftBraceFollows; + + for ( int i = 0; i < SmallRoof; i++ ) { + int hook = -1; + + int j = yyLine->length(); + while ( j > 0 && hook < 0 ) { + j--; + QChar ch = (*yyLine)[j]; + + switch ( ch.unicode() ) { + case ')': + case ']': + delimDepth++; + break; + case '}': + braceDepth++; + break; + case '(': + case '[': + delimDepth--; + /* + An unclosed delimiter is a good place to align at, + at least for some styles (including Qt's). + */ + if ( delimDepth == -1 ) + hook = j; + break; + case '{': + braceDepth--; + /* + A left brace followed by other stuff on the same + line is typically for an enum or an initializer. + Such a brace must be treated just like the other + delimiters. + */ + if ( braceDepth == -1 ) { + if ( j < (int) yyLine->length() - 1 ) { + hook = j; + } else { + return 0; // shouldn't happen + } + } + break; + case '=': + /* + An equal sign is a very natural alignment hook + because it's usually the operator with the lowest + precedence in statements it appears in. Case in + point: + + int x = 1 + + 2; + + However, we have to beware of constructs such as + default arguments and explicit enum constant + values: + + void foo( int x = 0, + int y = 0 ); + + And not + + void foo( int x = 0, + int y = 0 ); + + These constructs are caracterized by a ',' at the + end of the unfinished lines or by unbalanced + parentheses. + */ + if ( QString("!=<>").indexOf((*yyLine)[j - 1]) == -1 && + (*yyLine)[j + 1] != '=' ) { + if ( braceDepth == 0 && delimDepth == 0 && + j < (int) yyLine->length() - 1 && + !yyLine->endsWith(QLatin1Char(',')) && + (yyLine->contains('(') == yyLine->contains(')')) ) + hook = j; + } + } + } + + if ( hook >= 0 ) { + /* + Yes, we have a delimiter or an operator to align + against! We don't really align against it, but rather + against the following token, if any. In this example, + the following token is "11": + + int x = ( 11 + + 2 ); + + If there is no such token, we use a continuation indent: + + static QRegExp foo( QString( + "foo foo foo foo foo foo foo foo foo") ); + */ + hook++; + while ( hook < (int) yyLine->length() ) { + if ( !(*yyLine)[hook].isSpace() ) + return columnForIndex( *yyLine, hook ); + hook++; + } + return indentOfLine( *yyLine ) + ppContinuationIndentSize; + } + + if ( braceDepth != 0 ) + break; + + /* + The line's delimiters are balanced. It looks like a + continuation line or something. + */ + if ( delimDepth == 0 ) { + if ( leftBraceFollowed ) { + /* + We have + + int main() + { + + or + + Bar::Bar() + : Foo( x ) + { + + The "{" should be flush left. + */ + if ( !isContinuationLine() ) + return indentOfLine( *yyLine ); + } else if ( isContinuationLine() || yyLine->endsWith(QLatin1Char(',')) ) { + /* + We have + + x = a + + b + + c; + + or + + int t[] = { + 1, 2, 3, + 4, 5, 6 + + The "c;" should fall right under the "b +", and the + "4, 5, 6" right under the "1, 2, 3,". + */ + return indentOfLine( *yyLine ); + } else { + /* + We have + + stream << 1 + + 2; + + We could, but we don't, try to analyze which + operator has precedence over which and so on, to + obtain the excellent result + + stream << 1 + + 2; + + We do have a special trick above for the assignment + operator above, though. + */ + return indentOfLine( *yyLine ) + ppContinuationIndentSize; + } + } + + if ( !readLine() ) + break; + } + return 0; +} + +/* + Returns the recommended indent for the bottom line of yyProgram if + that line is standalone (or should be indented likewise). + + Indenting a standalone line is tricky, mostly because of braceless + control statements. Grossly, we are looking backwards for a special + line, a "hook line", that we can use as a starting point to indent, + and then modify the indentation level according to the braces met + along the way to that hook. + + Let's consider a few examples. In all cases, we want to indent the + bottom line. + + Example 1: + + x = 1; + y = 2; + + The hook line is "x = 1;". We met 0 opening braces and 0 closing + braces. Therefore, "y = 2;" inherits the indent of "x = 1;". + + Example 2: + + if ( x ) { + y; + + The hook line is "if ( x ) {". No matter what precedes it, "y;" has + to be indented one level deeper than the hook line, since we met one + opening brace along the way. + + Example 3: + + if ( a ) + while ( b ) { + c; + } + d; + + To indent "d;" correctly, we have to go as far as the "if ( a )". + Compare with + + if ( a ) { + while ( b ) { + c; + } + d; + + Still, we're striving to go back as little as possible to + accommodate people with irregular indentation schemes. A hook line + near at hand is much more reliable than a remote one. +*/ +static int indentForStandaloneLine() +{ + for ( int i = 0; i < SmallRoof; i++ ) { + if ( !*yyLeftBraceFollows ) { + YY_SAVE(); + + if ( matchBracelessControlStatement() ) { + /* + The situation is this, and we want to indent "z;": + + if ( x && + y ) + z; + + yyLine is "if ( x &&". + */ + return indentOfLine( *yyLine ) + ppIndentSize; + } + YY_RESTORE(); + } + + if ( yyLine->endsWith(QLatin1Char(';')) || yyLine->contains('{') ) { + /* + The situation is possibly this, and we want to indent + "z;": + + while ( x ) + y; + z; + + We return the indent of "while ( x )". In place of "y;", + any arbitrarily complex compound statement can appear. + */ + + if ( *yyBraceDepth > 0 ) { + do { + if ( !readLine() ) + break; + } while ( *yyBraceDepth > 0 ); + } + + LinizerState hookState; + + while ( isContinuationLine() ) + readLine(); + hookState = *yyLinizerState; + + readLine(); + if ( *yyBraceDepth <= 0 ) { + do { + if ( !matchBracelessControlStatement() ) + break; + hookState = *yyLinizerState; + } while ( readLine() ); + } + + *yyLinizerState = hookState; + + while ( isContinuationLine() ) + readLine(); + + /* + Never trust lines containing only '{' or '}', as some + people (Richard M. Stallman) format them weirdly. + */ + if ( yyLine->trimmed().length() > 1 ) + return indentOfLine( *yyLine ) - *yyBraceDepth * ppIndentSize; + } + + if ( !readLine() ) + return -*yyBraceDepth * ppIndentSize; + } + return 0; +} + +/* + Constructs global variables used by the indenter. +*/ +static void initializeIndenter() +{ + literal = new QRegExp( "([\"'])(?:\\\\.|[^\\\\])*\\1" ); + literal->setMinimal( true ); + label = new QRegExp( + "^\\s*((?:case\\b([^:]|::)+|[a-zA-Z_0-9]+)(?:\\s+slots)?:)(?!:)" ); + inlineCComment = new QRegExp( "/\\*.*\\*/" ); + inlineCComment->setMinimal( true ); + braceX = new QRegExp( "^\\s*\\}\\s*(?:else|catch)\\b" ); + iflikeKeyword = new QRegExp( "\\b(?:catch|do|for|if|while)\\b" ); + + yyLinizerState = new LinizerState; +} + +/* + Destroys global variables used by the indenter. +*/ +static void terminateIndenter() +{ + delete literal; + delete label; + delete inlineCComment; + delete braceX; + delete iflikeKeyword; + delete yyLinizerState; +} + +/* + Returns the recommended indent for the bottom line of program. + Unless null, typedIn stores the character of yyProgram that + triggered reindentation. + + This function works better if typedIn is set properly; it is + slightly more conservative if typedIn is completely wild, and + slighly more liberal if typedIn is always null. The user might be + annoyed by the liberal behavior. +*/ +int indentForBottomLine( const QStringList& program, QChar typedIn ) +{ + if ( program.isEmpty() ) + return 0; + + initializeIndenter(); + + yyProgram = new QStringList( program ); + startLinizer(); + + const QString& bottomLine = program.last(); + QChar firstCh = firstNonWhiteSpace( bottomLine ); + int indent; + + if ( bottomLineStartsInCComment() ) { + /* + The bottom line starts in a C-style comment. Indent it + smartly, unless the user has already played around with it, + in which case it's better to leave her stuff alone. + */ + if ( isOnlyWhiteSpace(bottomLine) ) { + indent = indentWhenBottomLineStartsInCComment(); + } else { + indent = indentOfLine( bottomLine ); + } + } else if ( okay(typedIn, '#') && firstCh == QChar('#') ) { + /* + Preprocessor directives go flush left. + */ + indent = 0; + } else { + if ( isUnfinishedLine() ) { + indent = indentForContinuationLine(); + } else { + indent = indentForStandaloneLine(); + } + + if ( okay(typedIn, '}') && firstCh == QChar('}') ) { + /* + A closing brace is one level more to the left than the + code it follows. + */ + indent -= ppIndentSize; + } else if ( okay(typedIn, ':') ) { + QRegExp caseLabel( + "\\s*(?:case\\b(?:[^:]|::)+" + "|(?:public|protected|private|signals|default)(?:\\s+slots)?\\s*" + ")?:.*" ); + + if ( caseLabel.exactMatch(bottomLine) ) { + /* + Move a case label (or the ':' in front of a + constructor initialization list) one level to the + left, but only if the user did not play around with + it yet. Some users have exotic tastes in the + matter, and most users probably are not patient + enough to wait for the final ':' to format their + code properly. + + We don't attempt the same for goto labels, as the + user is probably the middle of "foo::bar". (Who + uses goto, anyway?) + */ + if ( indentOfLine(bottomLine) <= indent ) + indent -= ppIndentSize; + else + indent = indentOfLine( bottomLine ); + } + } + } + delete yyProgram; + terminateIndenter(); + return qMax( 0, indent ); +} + +QT_END_NAMESPACE + +#ifdef Q_TEST_YYINDENT +/* + Test driver. +*/ + +#include <qfile.h> +#include <qtextstream.h> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +static QString fileContents( const QString& fileName ) +{ + QFile f( fileName ); + if ( !f.open(QFile::ReadOnly) ) { + qWarning( "yyindent error: Cannot open file '%s' for reading: %s", + fileName.toLatin1().data(), strerror(errno) ); + return QString(); + } + + QTextStream t( &f ); + QString contents = t.read(); + f.close(); + if ( contents.isEmpty() ) + qWarning( "yyindent error: File '%s' is empty", fileName.toLatin1().data() ); + return contents; +} + +QT_END_NAMESPACE + +int main( int argc, char **argv ) +{ + QT_USE_NAMESPACE + + if ( argc != 2 ) { + qWarning( "usage: yyindent file.cpp" ); + return 1; + } + + QString code = fileContents( argv[1] ); + QStringList program = QStringList::split( '\n', code, true ); + QStringList p; + QString out; + + while ( !program.isEmpty() && program.last().trimmed().isEmpty() ) + program.remove( program.fromLast() ); + + QStringList::ConstIterator line = program.begin(); + while ( line != program.end() ) { + p.push_back( *line ); + QChar typedIn = firstNonWhiteSpace( *line ); + if ( p.last().endsWith(QLatin1Char(':')) ) + typedIn = ':'; + + int indent = indentForBottomLine( p, typedIn ); + + if ( !(*line).trimmed().isEmpty() ) { + for ( int j = 0; j < indent; j++ ) + out += QLatin1Char(' '); + out += (*line).trimmed(); + } + out += QLatin1Char('\n'); + ++line; + } + + while ( out.endsWith(QLatin1Char('\n')) ) + out.truncate( out.length() - 1 ); + + printf( "%s\n", out.toLatin1().data() ); + return 0; +} + +#endif // Q_TEST_YYINDENT diff --git a/src/tools/tools.pro b/src/tools/tools.pro index 082339cac9..1d12423744 100644 --- a/src/tools/tools.pro +++ b/src/tools/tools.pro @@ -1,6 +1,6 @@ TEMPLATE = subdirs -TOOLS_SUBDIRS = src_tools_bootstrap src_tools_moc src_tools_rcc +TOOLS_SUBDIRS = src_tools_bootstrap src_tools_moc src_tools_rcc src_tools_qdoc !contains(QT_CONFIG, no-gui): TOOLS_SUBDIRS += src_tools_uic # Set subdir and respective target name src_tools_bootstrap.subdir = $$QT_SOURCE_TREE/src/tools/bootstrap @@ -11,12 +11,15 @@ src_tools_rcc.subdir = $$QT_SOURCE_TREE/src/tools/rcc src_tools_rcc.target = sub-rcc src_tools_uic.subdir = $$QT_SOURCE_TREE/src/tools/uic src_tools_uic.target = sub-uic +src_tools_qdoc.subdir = $$QT_SOURCE_TREE/src/tools/qdoc +src_tools_qdoc.target = sub-qdoc !wince*:!ordered { # Set dependencies for each subdir src_tools_moc.depends = src_tools_bootstrap src_tools_rcc.depends = src_tools_bootstrap src_tools_uic.depends = src_tools_bootstrap + src_tools_qdoc.depends = src_tools_bootstrap } # Special handling, depending on type of project, if it used debug/release or only has one configuration |