diff options
Diffstat (limited to 'tools/qdoc3')
120 files changed, 34541 insertions, 0 deletions
diff --git a/tools/qdoc3/JAVATODO.txt b/tools/qdoc3/JAVATODO.txt new file mode 100644 index 0000000..96df4c1 --- /dev/null +++ b/tools/qdoc3/JAVATODO.txt @@ -0,0 +1,28 @@ + * index page on "O" and downwards? + * flag types, e.g. QTextStream::NumberFlags + * example references doc.trolltech.com etc. + + * fix enum value table (e.g. QCoreApplication.Encoding.html) + * check reimplementation of interface functions (e.g. QWidget::widthMM()) + * handle '::' in doc, e.g. in QAbstractEventDispatcher's detailed desc + * make sure default constructor logic doesn't interfere with default params (e.g. QAbstractEventDispatcher ctors) + * document default constructor provided for Java (e.g.?) + * document "protected QAbstractEventDispatcher(QtObject.QPrivateConstructor p)" and the like + * memory-managed, type + + * replace QString with String, etc. + * nested classes + * enum_1 + + * fix stylesheet + * no link to class itself + * support \ifjava + * add // C++ + * support Java-only /*! ... */ comments + + * links to property names have to be fixed + * example: version 1 in QStyleOptionFrame + * example: foo -> isFoo() + + * lowercase, and remove final stop, in \brief texts for variables and properties + * omit "see alsos" that don't work instead of showing them as broken links diff --git a/tools/qdoc3/README.TXT b/tools/qdoc3/README.TXT new file mode 100644 index 0000000..4f9f924 --- /dev/null +++ b/tools/qdoc3/README.TXT @@ -0,0 +1,6 @@ +qdoc3 is the tool used to generate the Qt reference documentation. +The source code is included as part of this package primarily to +fulfill our GPL obligations. We highly recommend using +Doxygen (http://www.stack.nl/~dimitri/doxygen/) for generating +documentation for your Qt-based application. + diff --git a/tools/qdoc3/TODO.txt b/tools/qdoc3/TODO.txt new file mode 100644 index 0000000..6deeb06 --- /dev/null +++ b/tools/qdoc3/TODO.txt @@ -0,0 +1,96 @@ + * 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. + * Use bullet list for the list of all functions, now that + Assistant handles these gracefully. + * 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 *> + * make \macro work (?) + * warning for unnamed parameters in property access functions + * \center...\endcenter + + * warning for undocumented enum values + +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 qbook + + * 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 + + * use qtstyle.css instead of inline style for each page diff --git a/tools/qdoc3/apigenerator.cpp b/tools/qdoc3/apigenerator.cpp new file mode 100644 index 0000000..d998839 --- /dev/null +++ b/tools/qdoc3/apigenerator.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QFile> + +#include "apigenerator.h" +#include "codemarker.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +static QString indentStr(int indent) +{ + QString str; + str.fill(QLatin1Char(' '), indent * 4); + return str; +} + +static bool lessThanName(Node *node1, Node *node2) +{ + return node1->name() < node2->name(); +} + +QString ApiGenerator::format() +{ + return QLatin1String("API"); +} + +void ApiGenerator::generateTree(const Tree *tree, CodeMarker *marker) +{ + QFile outFile(QLatin1String("api")); + outFile.open(QIODevice::WriteOnly); + + out.setDevice(&outFile); + generateNode(tree->root(), marker); + out.flush(); +} + +void ApiGenerator::generateNode(const Node *node, CodeMarker *marker, int indent) +{ + if (node->access() == Node::Private) + return; + + switch (node->type()) { + case Node::Namespace: + if (!node->name().isEmpty()) { + out << indentStr(indent) << "Namespace: " << node->name() << "\n"; + ++indent; + } + break; + case Node::Class: + { + const ClassNode *classe = static_cast<const ClassNode *>(node); + out << indentStr(indent) << "Class: " << node->name(); + foreach (const RelatedClass &baseClass, classe->baseClasses()) { + if (baseClass.access == Node::Public) + out << " inherits " << baseClass.dataTypeWithTemplateArgs; + } + out << "\n"; + ++indent; + } + break; + case Node::Enum: + { + const EnumNode *enume = static_cast<const EnumNode *>(node); + out << indentStr(indent) << "Enum: " << node->name() << "\n"; + ++indent; + + QStringList enumNames; + foreach (const EnumItem &item, enume->items()) + enumNames << item.name(); + qSort(enumNames); + + foreach (const QString &name, enumNames) + out << indentStr(indent) << "Enum value: " << name << "\n"; + } + break; + case Node::Typedef: + out << indentStr(indent) << "Typedef: " << node->name() << "\n"; + ++indent; + break; + case Node::Function: + { + out << indentStr(indent) << "Function: " + << plainCode(marker->markedUpSynopsis(node, 0, CodeMarker::Detailed)) << "\n"; + ++indent; + } + break; + case Node::Property: + { + const PropertyNode *property = static_cast<const PropertyNode *>(node); + out << indentStr(indent) << "Property: " << property->name() + << " type " << property->dataType() << "\n"; + ++indent; + } + break; + default: + ; + } + + if (node->isInnerNode()) { + const InnerNode *inner = static_cast<const InnerNode *>(node); + NodeList nodes = inner->childNodes(); + qSort(nodes.begin(), nodes.end(), lessThanName); + foreach (const Node *child, nodes) + generateNode(child, marker, indent); + } + + out.flush(); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/apigenerator.h b/tools/qdoc3/apigenerator.h new file mode 100644 index 0000000..9545fae --- /dev/null +++ b/tools/qdoc3/apigenerator.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APIGENERATOR_H +#define APIGENERATOR_H + +#include <QTextStream> + +#include "generator.h" + +QT_BEGIN_NAMESPACE + +class ApiGenerator : public Generator +{ +public: + QString format(); + void generateTree(const Tree *tree, CodeMarker *marker); + +private: + void generateNode(const Node *node, CodeMarker *marker, int indent = 0); + + QTextStream out; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/archiveextractor.cpp b/tools/qdoc3/archiveextractor.cpp new file mode 100644 index 0000000..b72d27a --- /dev/null +++ b/tools/qdoc3/archiveextractor.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + archiveextractor.cpp +*/ + +#include "archiveextractor.h" + +QT_BEGIN_NAMESPACE + +QList<ArchiveExtractor *> ArchiveExtractor::extractors; + +/*! + \class ArchiveExtractor + + \brief The ArchiveExtractor class is a base class for classes that + know how to unpack a certain kind of archive file. + + The archive extractor contains a list of the filename extensions + of the files that the archive extractor knows how to unpack. + + It maintains a static list of all the instances of ArchiveExtractor + that have been created. It also has a static function for searching + that list to find the archive extracter for a file with a certain + extension. + */ + +/*! + The constructor takes a list of filename extensions, which it + copies and saves internally. This archive extractor is prepended + to the static list. + */ +ArchiveExtractor::ArchiveExtractor( const QStringList& extensions ) + : fileExts( extensions ) +{ + extractors.prepend( this ); +} + +/*! + The destructor deletes all the filename extensions. + */ +ArchiveExtractor::~ArchiveExtractor() +{ + extractors.removeAll( this ); +} + +/*! + This function searches the static list of archive extractors + to find the first one that can handle \a fileName. If it finds + an acceptable extractor, it returns a pointer to it. Otherwise + it returns null. + */ +ArchiveExtractor* +ArchiveExtractor::extractorForFileName( const QString& fileName ) +{ + int dot = -1; + while ( (dot = fileName.indexOf(QLatin1Char('.'), dot + 1)) != -1 ) { + QString ext = fileName.mid( dot + 1 ); + QList<ArchiveExtractor *>::ConstIterator e = extractors.begin(); + while ( e != extractors.end() ) { + if ( (*e)->fileExtensions().contains(ext) ) + return *e; + ++e; + } + } + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/archiveextractor.h b/tools/qdoc3/archiveextractor.h new file mode 100644 index 0000000..cfa45bd --- /dev/null +++ b/tools/qdoc3/archiveextractor.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + archiveextractor.h +*/ + +#ifndef ARCHIVEEXTRACTOR_H +#define ARCHIVEEXTRACTOR_H + +#include <qstringlist.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class ArchiveExtractor +{ +public: + ArchiveExtractor( const QStringList& extensions ); + virtual ~ArchiveExtractor(); + + virtual void extractArchive( const Location& location, + const QString& filePath, + const QString& outputDir ) = 0; + + static ArchiveExtractor *extractorForFileName( const QString& fileName ); + +protected: + const QStringList& fileExtensions() { return fileExts; } + +private: + QStringList fileExts; + + static QList<ArchiveExtractor *> extractors; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/atom.cpp b/tools/qdoc3/atom.cpp new file mode 100644 index 0000000..a764653 --- /dev/null +++ b/tools/qdoc3/atom.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qregexp.h> +#include "atom.h" +#include "location.h" +#include <stdio.h> + +QT_BEGIN_NAMESPACE + +QString Atom::BOLD_ ("bold"); +QString Atom::INDEX_ ("index"); +QString Atom::ITALIC_ ("italic"); +QString Atom::LINK_ ("link"); +QString Atom::PARAMETER_ ("parameter"); +QString Atom::SUBSCRIPT_ ("subscript"); +QString Atom::SUPERSCRIPT_ ("superscript"); +QString Atom::TELETYPE_ ("teletype"); +QString Atom::UNDERLINE_ ("underline"); + +QString Atom::BULLET_ ("bullet"); +QString Atom::TAG_ ("tag"); +QString Atom::VALUE_ ("value"); +QString Atom::LOWERALPHA_ ("loweralpha"); +QString Atom::LOWERROMAN_ ("lowerroman"); +QString Atom::NUMERIC_ ("numeric"); +QString Atom::UPPERALPHA_ ("upperalpha"); +QString 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 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 FormatElse + \value FormatEndif + \value FormatIf + \value FootnoteLeft + \value FootnoteRight + \value FormattingLeft + \value FormattingRight + \value GeneratedList + \value Image + \value ImageText + \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 ParaLeft + \value ParaRight + \value Qml + \value QuotationLeft + \value QuotationRight + \value RawString + \value SectionLeft + \value SectionRight + \value SectionHeadingLeft + \value SectionHeadingRight + \value SidebarLeft + \value SidebarRight + \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 }, + { "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 }, + { "FootnoteLeft", Atom::FootnoteLeft }, + { "FootnoteRight", Atom::FootnoteRight }, + { "FormatElse", Atom::FormatElse }, + { "FormatEndif", Atom::FormatEndif }, + { "FormatIf", Atom::FormatIf }, + { "FormattingLeft", Atom::FormattingLeft }, + { "FormattingRight", Atom::FormattingRight }, + { "GeneratedList", Atom::GeneratedList }, + { "Image", Atom::Image }, + { "ImageText", Atom::ImageText }, + { "InlineImage", Atom::InlineImage }, + { "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 }, + { "ParaLeft", Atom::ParaLeft }, + { "ParaRight", Atom::ParaRight }, +#ifdef QDOC_QML + { "Qml", Atom::Qml}, +#endif + { "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 }, + { "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 (\a type, \a string) outside of any atom list. +*/ + +/*! \fn Atom( Atom *prev, Type type, const QString& string ) + + Constructs an atom (\a type, \a string) that follows \a prev in \a + prev's atom list. +*/ + +/*! \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 (nxt && (nxt->type() == t)) ? nxt : 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 (nxt && (nxt->type() == t) && (nxt->string() == s)) ? nxt : 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( "\\", "\\\\" ); + str.replace( "\"", "\\\"" ); + str.replace( "\n", "\\n" ); + str.replace( QRegExp("[^\x20-\x7e]"), "?" ); + if (!str.isEmpty()) + str = " \"" + str + "\""; + fprintf(stderr, + " %-15s%s\n", + typeString().toLatin1().data(), + str.toLatin1().data()); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/atom.h b/tools/qdoc3/atom.h new file mode 100644 index 0000000..0d2a924 --- /dev/null +++ b/tools/qdoc3/atom.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + atom.h +*/ + +#ifndef ATOM_H +#define ATOM_H + +#include <qstring.h> + +#define QDOC_QML + +QT_BEGIN_NAMESPACE + +class Atom +{ + public: + enum Type { + AbstractLeft, + AbstractRight, + AutoLink, + BaseName, + BriefLeft, + BriefRight, + C, + CaptionLeft, + CaptionRight, + Code, + CodeBad, + CodeNew, + CodeOld, + CodeQuoteArgument, + CodeQuoteCommand, + FootnoteLeft, + FootnoteRight, + FormatElse, + FormatEndif, + FormatIf, + FormattingLeft, + FormattingRight, + GeneratedList, + Image, + ImageText, + InlineImage, + LegaleseLeft, + LegaleseRight, + LineBreak, + Link, + LinkNode, + ListLeft, + ListItemNumber, + ListTagLeft, + ListTagRight, + ListItemLeft, + ListItemRight, + ListRight, + Nop, + ParaLeft, + ParaRight, +#ifdef QDOC_QML + Qml, +#endif + QuotationLeft, + QuotationRight, + RawString, + SectionLeft, + SectionRight, + SectionHeadingLeft, + SectionHeadingRight, + SidebarLeft, SidebarRight, + SnippetCommand, + SnippetIdentifier, + SnippetLocation, + String, + TableLeft, + TableRight, + TableHeaderLeft, + TableHeaderRight, + TableRowLeft, + TableRowRight, + TableItemLeft, + TableItemRight, + TableOfContents, + Target, + UnhandledFormat, + UnknownCommand, + Last = UnknownCommand + }; + + Atom(Type type, const QString &string = "") + : nxt(0), typ(type), str(string) { } + Atom(Atom *prev, Type type, const QString &string = "") + : nxt(prev->nxt), typ(type), str(string) { prev->nxt = this; } + + void appendChar(QChar ch) { str += ch; } + void appendString(const QString& string) { str += string; } + void chopString() { str.chop(1); } + void setString(const QString &string) { str = string; } + Atom *next() { return nxt; } + void setNext(Atom *newNext) { nxt = newNext; } + + const Atom *next() const { return nxt; } + const Atom *next(Type t) const; + const Atom *next(Type t, const QString& s) const; + Type type() const { return typ; } + QString typeString() const; + const QString& string() const { return str; } + void dump() const; + + static QString BOLD_; + static QString INDEX_; + static QString ITALIC_; + static QString LINK_; + static QString PARAMETER_; + static QString SUBSCRIPT_; + static QString SUPERSCRIPT_; + static QString TELETYPE_; + static QString UNDERLINE_; + + static QString BULLET_; + static QString TAG_; + static QString VALUE_; + static QString LOWERALPHA_; + static QString LOWERROMAN_; + static QString NUMERIC_; + static QString UPPERALPHA_; + static QString UPPERROMAN_; + + private: + Atom *nxt; + Type typ; + QString str; +}; + +#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_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/tools/qdoc3/bookgenerator.cpp b/tools/qdoc3/bookgenerator.cpp new file mode 100644 index 0000000..4e49491 --- /dev/null +++ b/tools/qdoc3/bookgenerator.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + bookgenerator.cpp +*/ + +#include "bookgenerator.h" + +QT_BEGIN_NAMESPACE + +BookGenerator::BookGenerator() +{ +} + +BookGenerator::~BookGenerator() +{ +} + +void BookGenerator::generateTree( const Tree *tree, CodeMarker *marker ) +{ + Q_UNUSED( tree ) + Q_UNUSED( marker ) +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/bookgenerator.h b/tools/qdoc3/bookgenerator.h new file mode 100644 index 0000000..15b830e --- /dev/null +++ b/tools/qdoc3/bookgenerator.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + bookgenerator.h +*/ + +#ifndef BOOKGENERATOR_H +#define BOOKGENERATOR_H + +#include "generator.h" + +QT_BEGIN_NAMESPACE + +class BookGenerator : public Generator +{ +public: + BookGenerator(); + ~BookGenerator(); + + virtual void generateTree( const Tree *tree, CodeMarker *marker ); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/ccodeparser.cpp b/tools/qdoc3/ccodeparser.cpp new file mode 100644 index 0000000..d6318f5 --- /dev/null +++ b/tools/qdoc3/ccodeparser.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + ccodeparser.cpp +*/ + +#include "ccodeparser.h" + +QT_BEGIN_NAMESPACE + +CCodeParser::CCodeParser() +{ +} + +CCodeParser::~CCodeParser() +{ +} + +QString CCodeParser::language() +{ + return QLatin1String("C"); +} + +QString CCodeParser::headerFileNameFilter() +{ + return QLatin1String("*.ch *.h"); +} + +QString CCodeParser::sourceFileNameFilter() +{ + return QLatin1String("*.c"); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/ccodeparser.h b/tools/qdoc3/ccodeparser.h new file mode 100644 index 0000000..4e1877a --- /dev/null +++ b/tools/qdoc3/ccodeparser.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + ccodeparser.h +*/ + +#ifndef CCODEPARSER_H +#define CCODEPARSER_H + +#include "cppcodeparser.h" + +QT_BEGIN_NAMESPACE + +class CCodeParser : public CppCodeParser +{ +public: + CCodeParser(); + ~CCodeParser(); + + virtual QString language(); + virtual QString headerFileNameFilter(); + virtual QString sourceFileNameFilter(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/codechunk.cpp b/tools/qdoc3/codechunk.cpp new file mode 100644 index 0000000..77c2a3e --- /dev/null +++ b/tools/qdoc3/codechunk.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/tools/qdoc3/codechunk.h b/tools/qdoc3/codechunk.h new file mode 100644 index 0000000..f7ac863 --- /dev/null +++ b/tools/qdoc3/codechunk.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 convertion 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/tools/qdoc3/codemarker.cpp b/tools/qdoc3/codemarker.cpp new file mode 100644 index 0000000..a803281 --- /dev/null +++ b/tools/qdoc3/codemarker.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QMetaObject> +#include <QDebug> +#include "codemarker.h" +#include "config.h" +#include "node.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() + : slow(false) +{ + markers.prepend(this); +} + +/*! + When a code marker destroys itself, it removes itself from + the static list of code markers. + */ +CodeMarker::~CodeMarker() +{ + markers.removeAll(this); +} + +/*! + The only thing a code market initializes is its \e{slow} + flag. The \e{slow} flag indicates whether the operations + that slow down qdoc are to be performed or not. It is + turned off by default. + */ +void CodeMarker::initializeMarker(const Config &config) +{ + slow = config.getBool(QLatin1String(CONFIG_SLOW)); +} + +/*! + 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<ulong>(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; + + 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; + default: + tag = QLatin1String("@unknown"); + } + 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) +{ + QString 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, QLatin1String("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 inheritedMember = (!node->relates() && + (node->parent() != (const InnerNode *)fastSection.innerNode)); + bool irrelevant = false; + + 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 != SeparateList); + 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 == SeparateList) { + 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<ClassNode *, int> p((ClassNode *)node->parent(), 0); + fastSection.inherited.append(p); + } + fastSection.inherited.last().second++; + } + } + } +} + +void CodeMarker::append(QList<Section>& sectionList, + const FastSection& fastSection) +{ + if (!fastSection.memberMap.isEmpty() || + !fastSection.inherited.isEmpty()) { + Section section(fastSection.name, + fastSection.singularMember, + fastSection.pluralMember); + section.members = fastSection.memberMap.values(); + section.inherited = fastSection.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(const 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; + 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/"); + isMacro = true; +#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 += "/" + 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 += ","; + result += type; + } + result += ")"; + } +#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 (const 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('/') + myName; + } else { + return plainFullName(node) + QLatin1Char('/') + myName; + } +} + + +QT_END_NAMESPACE diff --git a/tools/qdoc3/codemarker.h b/tools/qdoc3/codemarker.h new file mode 100644 index 0000000..2bb1f2b --- /dev/null +++ b/tools/qdoc3/codemarker.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + codemarker.h +*/ + +#ifndef CODEMARKER_H +#define CODEMARKER_H + +#include <qpair.h> + +#include "node.h" + +QT_BEGIN_NAMESPACE + +class Config; +class Tree; + +struct Section +{ + QString name; + QString singularMember; + QString pluralMember; + NodeList members; + QList<QPair<ClassNode *, int> > inherited; + + Section() { } + Section(const QString& name0, + const QString& singularMember0, + const QString& pluralMember0) + : name(name0), + singularMember(singularMember0), + pluralMember(pluralMember0) { } +}; + +struct FastSection +{ + const InnerNode *innerNode; + QString name; + QString singularMember; + QString pluralMember; + QMap<QString, Node *> memberMap; + QList<QPair<ClassNode *, int> > inherited; + + FastSection(const InnerNode *innerNode0, + const QString& name0 = "", + const QString& singularMember0 = "member", + const QString& pluralMember0 = "members") + : innerNode(innerNode0), + name(name0), + singularMember(singularMember0), + pluralMember(pluralMember0) { } +}; + +class CodeMarker +{ + public: + enum SynopsisStyle { Summary, Detailed, SeparateList, 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 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 QString& dirPath) = 0; + virtual QString markedUpSynopsis(const Node *node, + const Node *relative, + SynopsisStyle style) = 0; + 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 const Node *resolveTarget(const QString& target, + const Tree *tree, + const Node *relative) = 0; + virtual QStringList macRefsForNode(const 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); + + protected: + bool hurryUp() const { return !slow; } + + virtual QString sortName(const Node *node); + QString protect(const QString &string); + QString typified(const QString &string); + QString taggedNode(const Node *node); + QString linkTag(const Node *node, const QString& body); + void insert(FastSection &fastSection, + Node *node, + SynopsisStyle style, + Status status); + void append(QList<Section>& sectionList, const FastSection& fastSection); + + private: + QString macName(const Node *parent, const QString &name = QString()); + + bool slow; + + static QString defaultLang; + static QList<CodeMarker *> markers; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/codeparser.cpp b/tools/qdoc3/codeparser.cpp new file mode 100644 index 0000000..63b6865 --- /dev/null +++ b/tools/qdoc3/codeparser.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + codeparser.cpp +*/ + +#include <QtCore> +#include "codeparser.h" +#include "node.h" +#include "tree.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_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_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")) + +QList<CodeParser *> CodeParser::parsers; + +/*! + 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); +} + +/*! + Initializing a code parser is trivial. + */ +void CodeParser::initializeParser(const Config & /* config */) +{ + // nothing. +} + +/*! + Teerminating a code parser is trivial. + */ +void CodeParser::terminateParser() +{ + // nothing. +} + +QString 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; +} + +/*! + 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_INTERNAL + << COMMAND_MAINCLASS + << COMMAND_NONREENTRANT + << COMMAND_OBSOLETE + << COMMAND_PRELIMINARY + << COMMAND_INPUBLICGROUP + << COMMAND_REENTRANT + << COMMAND_SINCE + << COMMAND_SUBTITLE + << COMMAND_THREADSAFE + << COMMAND_TITLE; +} + +void CodeParser::processCommonMetaCommand(const Location &location, + const QString &command, + const QString &arg, + Node *node, + Tree *tree) +{ + if (command == COMMAND_COMPAT) { + 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_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) { + 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_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); +#ifdef QDOC2DOX + /* qdoc -> doxygen. + I think this must be done here, because there can be multiple + "\externalpage" and "\title" metacommands in a single qdoc + comment, which means, among other things, that the "\title" + commands are not inserted into the metacommand map used by + the Doc class. I'm sure there4 is a better way to do this in + the DoxWriter class using the information in the FakeNode, + but I don't have time to figure it out right now. + */ + if (DoxWriter::isDoxPass(1)) + DoxWriter::insertTitle(fake,arg); +#endif + } + else + location.warning(tr("Ignored '\\%1'").arg(COMMAND_TITLE)); + } +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/codeparser.h b/tools/qdoc3/codeparser.h new file mode 100644 index 0000000..019e806 --- /dev/null +++ b/tools/qdoc3/codeparser.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + codeparser.h +*/ + +#ifndef CODEPARSER_H +#define CODEPARSER_H + +#include <QSet> + +#include "location.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 QString headerFileNameFilter(); + virtual QString 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; + + static void initialize(const Config& config); + static void terminate(); + static CodeParser *parserForLanguage(const QString& language); + + protected: + QSet<QString> commonMetaCommands(); + void processCommonMetaCommand(const Location& location, + const QString& command, const QString& arg, + Node *node, Tree *tree); + + private: + static QList<CodeParser *> parsers; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/command.cpp b/tools/qdoc3/command.cpp new file mode 100644 index 0000000..a82494d --- /dev/null +++ b/tools/qdoc3/command.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + command.cpp +*/ + +#include <QProcess> + +#include "command.h" + +QT_BEGIN_NAMESPACE + +void executeCommand( const Location& location, const QString& format, + const QStringList& args ) +{ + QString actualCommand; + for ( int i = 0; i < (int) format.length(); i++ ) { + int ch = format[i].unicode(); + if ( ch > 0 && ch < 8 ) { + actualCommand += args[ch - 1]; + } else { + actualCommand += format[i]; + } + } + + QString toolName = actualCommand; + int space = toolName.indexOf( QLatin1Char(' ') ); + if ( space != -1 ) + toolName.truncate( space ); + + QProcess process; + process.start(QLatin1String("sh"), + QStringList() << QLatin1String("-c") << actualCommand ); + process.waitForFinished(); + + if (process.exitCode() == 127) + location.fatal( tr("Couldn't launch the '%1' tool") + .arg(toolName), + tr("Make sure the tool is installed and in the" + " path.") ); + + QString errors = QString::fromLocal8Bit(process.readAllStandardError()); + while ( errors.endsWith(QLatin1Char('\n')) ) + errors.truncate( errors.length() - 1 ); + if ( !errors.isEmpty() ) + location.fatal( tr("The '%1' tool encountered some problems") + .arg(toolName), + tr("The tool was invoked like this:\n%1\n" + "It emitted these errors:\n%2") + .arg(actualCommand).arg(errors) ); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/command.h b/tools/qdoc3/command.h new file mode 100644 index 0000000..4b86c38 --- /dev/null +++ b/tools/qdoc3/command.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + command.h +*/ + +#ifndef COMMAND_H +#define COMMAND_H + +#include <qstringlist.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +void executeCommand( const Location& location, const QString& commandFormat, + const QStringList& args ); + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/config.cpp b/tools/qdoc3/config.cpp new file mode 100644 index 0000000..c8488f3 --- /dev/null +++ b/tools/qdoc3/config.cpp @@ -0,0 +1,892 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + config.cpp +*/ + +#include <QtCore> + +#include "archiveextractor.h" +#include "config.h" +#include "uncompressor.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("."); +QMap<QString, QString> Config::uncompressedFiles; +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 deletes all the temporary files and + directories it built. + */ +Config::~Config() +{ + if (--numInstances == 0) { + QMap<QString, QString>::ConstIterator f = uncompressedFiles.begin(); + while (f != uncompressedFiles.end()) { + QDir().remove(*f); + ++f; + } + uncompressedFiles.clear(); + + QMap<QString, QString>::ConstIterator d = extractedDirs.begin(); + while (d != extractedDirs.end()) { + removeDirContents(*d); + QDir dir(*d); + QString name = dir.dirName(); + dir.cdUp(); + dir.rmdir(name); + ++d; + } + extractedDirs.clear(); + } +} + +/*! + 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; +} + +/*! + 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; +} + +/*! + 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]; +} + +/*! + 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. + */ +QSet<QString> Config::subVars(const QString& var) const +{ + QSet<QString> result; + QString varDot = var + QLatin1Char('.'); + QMap<QString, QString>::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; +} + +/*! + 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. + */ +QStringList Config::getAllFiles(const QString &filesVar, + const QString &dirsVar, + const QString &defaultNameFilter, + const QSet<QString> &excludedDirs) +{ + QStringList result = getStringList(filesVar); + QStringList dirs = getStringList(dirsVar); + + QString nameFilter = getString(filesVar + dot + + QLatin1String(CONFIG_FILEEXTENSIONS)); + if (nameFilter.isEmpty()) + nameFilter = defaultNameFilter; + + QStringList::ConstIterator d = dirs.begin(); + while (d != dirs.end()) { + result += getFilesHere(*d, nameFilter, excludedDirs); + ++d; + } + return result; +} + +/*! + */ +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); + ArchiveExtractor *extractor = 0; + QString userFriendly = *c; + + if (isArchive) { + extractor = ArchiveExtractor::extractorForFileName(userFriendly); + } + + if (extractor == 0) { + Uncompressor *uncompressor = + Uncompressor::uncompressorForFileName(userFriendly); + if (uncompressor != 0) { + QString fileNameWithCorrectExtension = + uncompressor->uncompressedFilePath( + fileInfo.filePath()); + QString uncompressed = uncompressedFiles[fileInfo.filePath()]; + if (uncompressed.isEmpty()) { + uncompressed = + QTemporaryFile(fileInfo.filePath()).fileName(); + uncompressor->uncompressFile(location, + fileInfo.filePath(), + uncompressed); + uncompressedFiles[fileInfo.filePath()] = uncompressed; + } + fileInfo.setFile(uncompressed); + + if (isArchive) { + extractor = ArchiveExtractor::extractorForFileName( + fileNameWithCorrectExtension); + } + else { + userFriendly = fileNameWithCorrectExtension; + } + } + } + userFriendlyFilePath += userFriendly; + + if (isArchive) { + if (extractor == 0) + location.fatal(tr("Unknown archive type '%1'") + .arg(userFriendlyFilePath)); + QString extracted = extractedDirs[fileInfo.filePath()]; + if (extracted.isEmpty()) { + extracted = QTemporaryFile(fileInfo.filePath()).fileName(); + if (!QDir().mkdir(extracted)) + location.fatal(tr("Cannot create temporary directory '%1'") + .arg(extracted)); + extractor->extractArchive(location, fileInfo.filePath(), + extracted); + extractedDirs[fileInfo.filePath()] = extracted; + } + ++c; + fileInfo.setFile(QDir(extracted), *c); + } + else { + break; + } + userFriendlyFilePath += "?"; + } + 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 + "." + *e, + userFriendlyFilePath); + if (!filePath.isEmpty()) + return filePath; + ++e; + } + return findFile(location, files, dirs, fileBase, userFriendlyFilePath); +} + +/*! + */ +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(inFile.fileName()).arg(inFile.errorString())); + return ""; + } + + QString outFileName = userFriendlySourceFilePath; + int slash = outFileName.lastIndexOf("/"); + if (slash != -1) + outFileName = outFileName.mid(slash); + + QFile outFile(targetDirPath + "/" + outFileName); + if (!outFile.open(QFile::WriteOnly)) { + location.fatal(tr("Cannot open output file '%1': %2") + .arg(outFile.fileName()).arg(outFile.errorString())); + return ""; + } + + 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() != "." && (*it).fileName() != "..") { + 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 qdog configuration file. + */ +void Config::load(Location location, const QString& fileName) +{ + QRegExp keySyntax("\\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 + ".qdoc"); + if (!fin.open(QFile::ReadOnly | QFile::Text)) + location.fatal(tr("Cannot open file '%1': %2").arg(fileName).arg(fin.errorString())); + } + + QString text = fin.readAll(); + text += QLatin1String("\n\n"); + text += QChar('\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() == "include") { + QString includeFile; + + if (cc != '(') + location.fatal(tr("Bad include syntax")); + SKIP_CHAR(); + SKIP_SPACES(); + while (!c.isSpace() && cc != '#' && cc != ')') { + 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(QLatin1String("abfnrtv")).indexOf(c)) != -1) { + word += "\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(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) +{ + QStringList result; + if (excludedDirs.contains(dir)) + return result; + + QDir dirInfo(dir); + QStringList fileNames; + QStringList::const_iterator fn; + + dirInfo.setNameFilters(nameFilter.split(' ')); + dirInfo.setSorting(QDir::Name); + dirInfo.setFilter(QDir::Files); + fileNames = dirInfo.entryList(); + fn = fileNames.constBegin(); + while (fn != fileNames.constEnd()) { + if (!fn->startsWith(QLatin1Char('~'))) + result.append(dirInfo.filePath(*fn)); + ++fn; + } + + dirInfo.setNameFilters(QStringList("*")); + dirInfo.setFilter(QDir::Dirs|QDir::NoDotAndDotDot); + fileNames = dirInfo.entryList(); + fn = fileNames.constBegin(); + while (fn != fileNames.constEnd()) { + result += getFilesHere(dirInfo.filePath(*fn), nameFilter, excludedDirs); + ++fn; + } + return result; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/config.h b/tools/qdoc3/config.h new file mode 100644 index 0000000..9443f0d --- /dev/null +++ b/tools/qdoc3/config.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + config.h +*/ + +#ifndef CONFIG_H +#define CONFIG_H + +#include <QMap> +#include <QSet> +#include <QStringList> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Config +{ + public: + Config(const QString& programName); + ~Config(); + + void load(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 getString(const QString& var) const; + QSet<QString> getStringSet(const QString& var) const; + QStringList getStringList(const QString& var) const; + QRegExp getRegExp(const QString& var) const; + QList<QRegExp> getRegExpList(const QString& var) const; + QSet<QString> subVars(const QString& var) const; + QStringList getAllFiles(const QString& filesVar, + const QString& dirsVar, + const QString& defaultNameFilter, + const QSet<QString> &excludedDirs = QSet<QString>()); + + static QStringList getFilesHere(const QString& dir, + const QString& nameFilter, + const QSet<QString> &excludedDirs = 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; + + 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_CODEINDENT "codeindent" +#define CONFIG_DEFINES "defines" +#define CONFIG_DESCRIPTION "description" +#define CONFIG_EDITION "edition" +#define CONFIG_EXAMPLEDIRS "exampledirs" +#define CONFIG_EXAMPLES "examples" +#define CONFIG_EXCLUDEDIRS "excludedirs" +#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_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_OUTPUTDIR "outputdir" +#define CONFIG_OUTPUTLANGUAGE "outputlanguage" +#define CONFIG_OUTPUTFORMATS "outputformats" +#define CONFIG_PROJECT "project" +#define CONFIG_QHP "qhp" +#define CONFIG_QUOTINGINFORMATION "quotinginformation" +#define CONFIG_SLOW "slow" +#define CONFIG_SOURCEDIRS "sourcedirs" +#define CONFIG_SOURCES "sources" +#define CONFIG_SPURIOUS "spurious" +#define CONFIG_STYLESHEETS "stylesheets" +#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" + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/cppcodemarker.cpp b/tools/qdoc3/cppcodemarker.cpp new file mode 100644 index 0000000..59b967b --- /dev/null +++ b/tools/qdoc3/cppcodemarker.cpp @@ -0,0 +1,1009 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodemarker.cpp +*/ + +#include "atom.h" +#include "cppcodemarker.h" +#include "node.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +static int insertTagAround(QString &result, int pos, int len, const QString &tagName, + const QString &attributes = QString()) +{ + QString s; + //s.reserve(result.size() + tagName.size() * 2 + attributes.size() + 20); + s += result.midRef(0, pos); + s += QLatin1Char('<'); + s += tagName; + if (!attributes.isEmpty()) { + s += QLatin1Char(' '); + s += attributes; + } + s += QLatin1Char('>'); + s += result.midRef(pos, len); + s += QLatin1String("</"); + s += tagName; + s += QLatin1Char('>'); + s += result.midRef(pos + len); + int diff = s.length() - result.length(); + result = s; + return diff; +} + +/*! + 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& ext) +{ + 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 == "C" || lang == "Cpp"; +} + +/*! + 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 += "()"; + return name; +} + +QString CppCodeMarker::plainFullName(const Node *node, const Node *relative) +{ + if (node->name().isEmpty()) { + return "global"; + } + else { + QString fullName; + for (;;) { + fullName.prepend(plainName(node)); + if (node->parent() == relative || node->parent()->name().isEmpty()) + break; + fullName.prepend("::"); + node = node->parent(); + } + return fullName; + } +} + +QString CppCodeMarker::markedUpCode(const QString &code, + const Node *relative, + const QString &dirPath) +{ + return addMarkUp(protect(code), relative, dirPath); +} + +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: + func = (const FunctionNode *) node; + if (style != SeparateList && !func->returnType().isEmpty()) + synopsis = typified(func->returnType()) + " "; + synopsis += name; + if (func->metaness() != FunctionNode::MacroWithoutParams) { + synopsis += " ("; + if (!func->parameters().isEmpty()) { + synopsis += " "; + QList<Parameter>::ConstIterator p = func->parameters().begin(); + while (p != func->parameters().end()) { + if (p != func->parameters().begin()) + synopsis += ", "; + synopsis += typified((*p).leftType()); + if (style != SeparateList && !(*p).name().isEmpty()) + synopsis += + " <@param>" + protect((*p).name()) + "</@param>"; + synopsis += protect((*p).rightType()); + if (style != SeparateList && !(*p).defaultValue().isEmpty()) + synopsis += " = " + protect((*p).defaultValue()); + ++p; + } + synopsis += " "; + } + synopsis += ")"; + } + 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 == SeparateList) { + 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(" ") + "]"; + } + 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 += " "; + synopsis += "}"; + } + 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 == SeparateList) { + synopsis = name + " : " + typified(variable->dataType()); + } + else { + synopsis = typified(variable->leftType()) + " " + + 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::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 += "#include <<@headerfile>" + *inc + "</@headerfile>>\n"; + ++inc; + } + return addMarkUp(code, 0, ""); +} + +QString CppCodeMarker::functionBeginRegExp(const QString& funcName) +{ + return "^" + QRegExp::escape(funcName) + "$"; + +} + +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", "signals"); + FastSection publicSlots(classe, "Public Slots", "public slot", "public slots"); + FastSection publicTypes(classe, "Public Types", "public type", "public types"); + FastSection publicVariables(classe, "Public Variables", "public type", "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) { + 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) { + 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) { + 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"); + FastSection memberTypes(classe,"Member Type Documentation"); + FastSection memberVariables(classe,"Member Variable Documentation"); + FastSection properties(classe,"Property Documentation"); + FastSection relatedNonMembers(classe,"Related Non-Members"); + FastSection macros(classe,"Macro Documentation"); + + 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); + + 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", + "namespace", + "namespaces"); + FastSection classes(inner, "Classes", "class", "classes"); + FastSection types(inner, + style == Summary ? "Types" : "Type Documentation", + "type", + "types"); + FastSection functions(inner, + style == Summary ? "Functions" : "Function Documentation", + "function", + "functions"); + FastSection macros(inner, + style == Summary ? "Macros" : "Macro Documentation", + "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) +{ + 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("#")) { + // ### this doesn't belong here; get rid of TargetNode hack + int hashAt = target.indexOf("#"); + 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; + if ((node = tree->findNode(path, + relative, + Tree::SearchBaseClasses | + Tree::SearchEnumValues | + Tree::NonFunction))) + return node; + } + return 0; +} + +QString CppCodeMarker::addMarkUp(const QString& protectedCode, + const Node * /* relative */, + const QString& /* dirPath */) +{ + static QRegExp globalInclude("#include +<([^<>&]+)>"); + static QRegExp yHasTypeX("(?:^|\n *)([a-zA-Z_][a-zA-Z_0-9]*)" + "(?:<[^;{}]+>)?(?: *(?:\\*|&) *| +)" + "([a-zA-Z_][a-zA-Z_0-9]*)? *[,;()=]"); + static QRegExp xNewY("([a-zA-Z_][a-zA-Z_0-9]*) *= *new +([a-zA-Z_0-9]+)"); + static QRegExp xDotY("\\b([a-zA-Z_][a-zA-Z_0-9]*) *(?:\\.|->|,[ \n]*S(?:IGNAL|LOT)\\() *" + "([a-zA-Z_][a-zA-Z_0-9]*)(?= *\\()"); + static QRegExp xIsStaticZOfY("[\n:;{(=] *(([a-zA-Z_0-9]+)::([a-zA-Z_0-9]+))(?= *\\()"); + static QRegExp classX("[:,][ \n]*(?:p(?:ublic|r(?:otected|ivate))[ \n]+)?" + "([a-zA-Z_][a-zA-Z_0-9]*)"); + static QRegExp globalX("[\n{()=] *([a-zA-Z_][a-zA-Z_0-9]*)[ \n]*\\("); + static QRegExp multiLineComment("/(?:( )?\\*(?:[^*]+|\\*(?! /))*\\*\\1/)"); + multiLineComment.setMinimal(true); + static QRegExp singleLineComment("//(?!!)[^!\n]*"); + static QRegExp preprocessor("(?:^|\n)(#[ \t]*(?:include|if|elif|endif|error|pragma|define" + "|warning)(?:(?:\\\\\n|\\n#)[^\n]*)*)"); + static QRegExp literals(""(?:[^\\\\&]|\\\\[^\n]|&(?!quot;))*"" + "|'(?:[^\\\\]|\\\\(?:[^x0-9']|x[0-9a-f]{1,4}|[0-9]{1,3}))'"); + + QString result = protectedCode; + int pos; + + if (!hurryUp()) { + /* + Mark global includes. For example: + + #include <<@headerfile>QString</@headerfile> + */ + pos = 0; + while ((pos = result.indexOf(globalInclude, pos)) != -1) + pos += globalInclude.matchedLength() + + insertTagAround(result, + globalInclude.pos(1), + globalInclude.cap(1).length(), + "@headerfile"); + + /* + Look for variable definitions and similar constructs, mark + the data type, and remember the type of the variable. + */ + QMap<QString, QSet<QString> > typesForVariable; + pos = 0; + while ((pos = yHasTypeX.indexIn(result, pos)) != -1) { + QString x = yHasTypeX.cap(1); + QString y = yHasTypeX.cap(2); + + if (!y.isEmpty()) + typesForVariable[y].insert(x); + + /* + Without the minus one at the end, 'void member(Class + var)' would give 'member' as a variable of type 'void', + but would ignore 'Class var'. (### Is that true?) + */ + pos += yHasTypeX.matchedLength() + + insertTagAround(result, + yHasTypeX.pos(1), + x.length(), + "@type") - 1; + } + + /* + Do syntax highlighting of preprocessor directives. + */ + pos = 0; + while ((pos = preprocessor.indexIn(result, pos)) != -1) + pos += preprocessor.matchedLength() + + insertTagAround(result, + preprocessor.pos(1), + preprocessor.cap(1).length(), + "@preprocessor"); + + /* + Deal with string and character literals. + */ + pos = 0; + while ((pos = literals.indexIn(result, pos)) != -1) + pos += literals.matchedLength() + + insertTagAround(result, + pos, + literals.matchedLength(), + result.at(pos) == + QLatin1Char(' ') ? "@string" : "@char"); + + /* + Look for 'var = new Class'. + */ + pos = 0; + while ((pos = xNewY.indexIn(result, pos)) != -1) { + QString x = xNewY.cap(1); + QString y = xNewY.cap(2); + typesForVariable[x].insert(y); + + pos += xNewY.matchedLength() + insertTagAround(result, + xNewY.pos(2), + y.length(), + "@type"); + } + + /* + Insert some stuff that cannot harm. + */ + typesForVariable["qApp"].insert("QApplication"); + + /* + Add link to ': Class'. + */ + pos = 0; + while ((pos = classX.indexIn(result, pos)) != -1) + pos += classX.matchedLength() + + insertTagAround(result, + classX.pos(1), + classX.cap(1).length(), + "@type") - 1; + + /* + Find use of any of + + var.method() + var->method() + var, SIGNAL(method()) + var, SLOT(method()). + */ + pos = 0; + while ((pos = xDotY.indexIn(result, pos)) != -1) { + QString x = xDotY.cap(1); + QString y = xDotY.cap(2); + + QSet<QString> types = typesForVariable.value(x); + pos += xDotY.matchedLength() + + insertTagAround(result, + xDotY.pos(2), + xDotY.cap(2).length(), + "@func", + (types.count() == 1) ? "target=\"" + + protect(*types.begin() + "::" + y) + + "()\"" : QString()); + } + + /* + Add link to 'Class::method()'. + */ + pos = 0; + while ((pos = xIsStaticZOfY.indexIn(result, pos)) != -1) { + QString x = xIsStaticZOfY.cap(1); + QString z = xIsStaticZOfY.cap(3); + + pos += insertTagAround(result, + xIsStaticZOfY.pos(3), + z.length(), + "@func", + "target=\"" + protect(x) + "()\""); + pos += insertTagAround(result, + xIsStaticZOfY.pos(2), + xIsStaticZOfY.cap(2).length(), + "@type"); + pos += xIsStaticZOfY.matchedLength() - 1; + } + + /* + Add link to 'globalFunction()'. + */ + pos = 0; + while ((pos = globalX.indexIn(result, pos)) != -1) { + QString x = globalX.cap(1); + if (x != "QT_FORWARD_DECLARE_CLASS") { + pos += globalX.matchedLength() + + insertTagAround(result, + globalX.pos(1), + x.length(), + "@func", + "target=\"" + protect(x) + "()\"") - 1; + } + else + pos += globalX.matchedLength(); + } + } + + /* + Do syntax highlighting of comments. Also alter the code in a + minor way, so that we can include comments in documentation + comments. + */ + pos = 0; + while (pos != -1) { + int mlpos; + int slpos; + int len; + slpos = singleLineComment.indexIn(result, pos); + mlpos = multiLineComment.indexIn(result, pos); + + if (slpos == -1 && mlpos == -1) + break; + + if (slpos == -1) { + pos = mlpos; + len = multiLineComment.matchedLength(); + } + else if (mlpos == -1) { + pos = slpos; + len = singleLineComment.matchedLength(); + } + else { + if (slpos < mlpos) { + pos = slpos; + len = singleLineComment.matchedLength(); + } + else { + pos = mlpos; + len = multiLineComment.matchedLength(); + } + } + + if (result.at(pos + 1) == QLatin1Char(' ')) { + result.remove(pos + len - 2, 1); + result.remove(pos + 1, 1); + len -= 2; + + forever { + int endcodePos = result.indexOf("\\ endcode", pos); + if (endcodePos == -1 || endcodePos >= pos + len) + break; + result.remove(endcodePos + 1, 1); + len -= 1; + } + } + pos += len + insertTagAround(result, pos, len, "@comment"); + } + + return result; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodemarker.h b/tools/qdoc3/cppcodemarker.h new file mode 100644 index 0000000..8b68bc0 --- /dev/null +++ b/tools/qdoc3/cppcodemarker.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodemarker.h +*/ + +#ifndef CPPCODEMARKER_H +#define CPPCODEMARKER_H + +#include "codemarker.h" + +QT_BEGIN_NAMESPACE + +class CppCodeMarker : public CodeMarker +{ + public: + CppCodeMarker(); + ~CppCodeMarker(); + + bool recognizeCode(const QString& code); + bool recognizeExtension(const QString& ext); + bool recognizeLanguage(const QString& lang); + QString plainName(const Node *node); + QString plainFullName(const Node *node, const Node *relative); + QString markedUpCode(const QString& code, + const Node *relative, + const QString& dirPath); + 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); + const Node *resolveTarget(const QString& target, + const Tree *tree, + const Node *relative); + +private: + QString addMarkUp(const QString& protectedCode, + const Node *relative, + const QString& dirPath); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/cppcodeparser.cpp b/tools/qdoc3/cppcodeparser.cpp new file mode 100644 index 0000000..fcf77f5 --- /dev/null +++ b/tools/qdoc3/cppcodeparser.cpp @@ -0,0 +1,2012 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodeparser.cpp +*/ + +#include <QtCore> +#include <qfile.h> + +#include <stdio.h> + +#include "codechunk.h" +#include "config.h" +#include "cppcodeparser.h" +#include "tokenizer.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +#define COMMAND_CLASS Doc::alias("class") +#define COMMAND_CONTENTSPAGE Doc::alias("contentspage") +#define COMMAND_ENUM Doc::alias("enum") +#define COMMAND_EXAMPLE Doc::alias("example") +#define COMMAND_EXTERNALPAGE Doc::alias("externalpage") +#define COMMAND_FILE Doc::alias("file") // ### don't document +#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") // ### don't document +#define COMMAND_MACRO Doc::alias("macro") +#define COMMAND_MODULE Doc::alias("module") // ### don't document +#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") + +#ifdef QDOC_QML +#define COMMAND_QMLCLASS Doc::alias("qmlclass") +#define COMMAND_QMLPROPERTY Doc::alias("qmlproperty") +#endif + +QStringList CppCodeParser::exampleFiles; +QStringList CppCodeParser::exampleDirs; + +static void extractPageLinkAndDesc(const QString &arg, + QString *link, + QString *desc) +{ + QRegExp bracedRegExp("\\{([^{}]*)\\}(?:\\{([^{}]*)\\})?"); + + if (bracedRegExp.exactMatch(arg)) { + *link = bracedRegExp.cap(1); + *desc = bracedRegExp.cap(2); + if (desc->isEmpty()) + *desc = *link; + } + else { + int spaceAt = arg.indexOf(" "); + if (arg.contains(".html") && spaceAt != -1) { + *link = arg.left(spaceAt).trimmed(); + *desc = arg.mid(spaceAt).trimmed(); + } else { + *link = arg; + *desc = arg; + } + } +} + +static void setLink(Node *node, Node::LinkType linkType, const QString &arg) +{ + QString link; + QString desc; + extractPageLinkAndDesc(arg, &link, &desc); + node->setLink(linkType, link, desc); +} + +/* + 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.replace(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() +{ +} + +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); + +#ifdef QDOC_QML + // nodeTypeMap.insert(COMMAND_QMLCLASS, Node::Class); + nodeTypeMap.insert(COMMAND_QMLPROPERTY, Node::Property); +#endif + + exampleFiles = config.getStringList(CONFIG_EXAMPLES); + exampleDirs = config.getStringList(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"; +} + +void CppCodeParser::terminateParser() +{ + nodeTypeMap.clear(); + CodeParser::terminateParser(); +} + +QString CppCodeParser::language() +{ + return "Cpp"; +} + +QString CppCodeParser::headerFileNameFilter() +{ + return "*.ch *.h *.h++ *.hh *.hpp *.hxx"; +} + +QString CppCodeParser::sourceFileNameFilter() +{ + return "*.c++ *.cc *.cpp *.cxx"; +} + +/*! + 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) +{ + FILE *in = fopen(QFile::encodeName(filePath), "r"); + if (!in) { + location.error(tr("Cannot open C++ header file '%1'").arg(filePath)); + return; + } + + reset(tree); + Location fileLocation(filePath); + Tokenizer fileTokenizer(fileLocation, in); + tokenizer = &fileTokenizer; + readToken(); + matchDeclList(tree->root()); + if (!fileTokenizer.version().isEmpty()) + tree->setVersion(fileTokenizer.version()); + fclose(in); + + 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) +{ + FILE *in = fopen(QFile::encodeName(filePath), "r"); + if (!in) { + location.error(tr("Cannot open C++ source file '%1'").arg(filePath)); + return; + } + + reset(tree); + Location fileLocation(filePath); + Tokenizer fileTokenizer(fileLocation, in); + tokenizer = &fileTokenizer; + readToken(); + usedNamespaces.clear(); + matchDocsAndStuff(); + fclose(in); +} + +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(); +} + +void CppCodeParser::doneParsingSourceFiles(Tree *tree) +{ + tree->root()->makeUndocumentedChildrenInternal(); + tree->root()->normalizeOverloads(); + tree->fixInheritance(); + tree->resolveProperties(); +} + +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_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 +#ifdef QDOC_QML + << COMMAND_VARIABLE + << COMMAND_QMLCLASS + << COMMAND_QMLPROPERTY; +#else + << COMMAND_VARIABLE; +#endif +} + +/*! + 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); + 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(" ") hack is there to support header file syntax + QStringList paths = arg.split(" "); + QStringList path = paths[0].split("::"); + +#if QDOC2DOX + // qdoc -> doxygen. + if (Doc::isDoxPass(1)) { + if (command == COMMAND_PROPERTY) { + Doc::insertProperty(path); + } + else if (command == COMMAND_VARIABLE) { + Doc::insertVariable(path); + } + else if (command == COMMAND_ENUM) { + // zzz + } + } +#endif + + 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(" "); + 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("::")); + } + } + + return node; + } + else if (command == COMMAND_EXAMPLE) { + FakeNode *fake = new FakeNode(tre->root(), arg, FakeNode::Example); + createExampleFileNodes(fake); + return fake; + } + else if (command == COMMAND_EXTERNALPAGE) { + return new FakeNode(tre->root(), arg, FakeNode::ExternalPage); + } + else if (command == COMMAND_FILE) { + return new FakeNode(tre->root(), arg, FakeNode::File); + } + else if (command == COMMAND_GROUP) { + return new FakeNode(tre->root(), arg, FakeNode::Group); + } + else if (command == COMMAND_HEADERFILE) { + return new FakeNode(tre->root(), arg, FakeNode::HeaderFile); + } + else if (command == COMMAND_MODULE) { + return new FakeNode(tre->root(), arg, FakeNode::Module); + } + else if (command == COMMAND_PAGE) { + return new FakeNode(tre->root(), arg, FakeNode::Page); + } +#ifdef QDOC_QML + else if (command == COMMAND_QMLCLASS) { + const ClassNode* classNode = 0; + QStringList names = arg.split(" "); + qDebug() << "QMLCLASS" << names; + Node* n = tre->findNode(names[1].split("::"),Node::Class); + if (n) { + classNode = static_cast<const ClassNode*>(n); + qDebug() << "FOUND IT!" << classNode->name(); + } + return new QmlNode(tre->root(), names[0], classNode); + } +#endif + return 0; +} + +/*! + 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; +} + +/*! + 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.")); + } +#if 0 // 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("Base function for '\\%1' in %2() is private or internal") + .arg(COMMAND_REIMP).arg(node->name())); + } +#endif + // Note: Setting the access to Private hides the documentation, + // but setting the status to Internal makes the node available + // in the XML output when the WebXMLGenerator is used. + func->setAccess(Node::Private); + func->setStatus(Node::Internal); + } + 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("<") || 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 { + 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; + } +} + +/*! + 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::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) +{ + 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))) + 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(); + 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(parent, name); + func->setAccess(access); + func->setLocation(location()); + func->setReturnType(returnType.toString()); + 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("~")) { + 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("0") && last.size() > 1) { + if (last.startsWith("0x") || last.startsWith("0X")) + strVal = last.left(2) + QString::number(n + 1, 16); + else + strVal = "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) +{ + if (!match(Tok_Q_PROPERTY) && + !match(Tok_Q_OVERRIDE) && + !match(Tok_QDOC_PROPERTY)) + return false; + if (!match(Tok_LeftParen)) + 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)) { + 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); + else if (key == "STORED") + property->setStored(value.toLower() == "true"); + else if (key == "DESIGNABLE") + property->setDesignable(value.toLower() == "true"); + else if (key == "RESET") + tre->addPropertyFunction(property, value, PropertyNode::Resetter); + } + 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_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); + /* + qdoc --> doxygen + We must also remember the location of the end + of the comment, so we can construct a diff for + it. + */ + 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. + */ + 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; +} + +bool CppCodeParser::makeFunctionNode(const QString& synopsis, + QStringList *parentPathPtr, + FunctionNode **funcPtr, + InnerNode *root) +{ + Tokenizer *outerTokenizer = tokenizer; + int outerTok = tok; + + Location loc; + QByteArray latin1 = synopsis.toLatin1(); + Tokenizer stringTokenizer(loc, latin1); + stringTokenizer.setParsingFnOrMacro(true); + tokenizer = &stringTokenizer; + readToken(); + + bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr); + // potential memory leak with funcPtr + + tokenizer = outerTokenizer; + tok = outerTok; + + return ok; +} + +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.replace("\\\n", ""); + QStringList lines = text.split("\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.replace(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(); + + // we can assume that this file always exists + QString proFileName = examplePath + "/" + + examplePath.split("/").last() + ".pro"; + + QString userFriendlyFilePath; + QString fullPath = Config::findFile(fake->doc().location(), + exampleFiles, + exampleDirs, + proFileName, + userFriendlyFilePath); + if (fullPath.isEmpty()) { + QString tmp = proFileName; + proFileName = examplePath + "/" + "qbuild.pro"; + 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)); + return; + } + } + + int sizeOfBoringPartOfName = fullPath.size() - proFileName.size(); + fullPath.truncate(fullPath.lastIndexOf('/')); + + QStringList exampleFiles = Config::getFilesHere(fullPath, + exampleNameFilter); + 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"); + } + + foreach (const QString &exampleFile, exampleFiles) + (void) new FakeNode(fake, + exampleFile.mid(sizeOfBoringPartOfName), + FakeNode::File); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/cppcodeparser.h b/tools/qdoc3/cppcodeparser.h new file mode 100644 index 0000000..e672654 --- /dev/null +++ b/tools/qdoc3/cppcodeparser.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cppcodeparser.h +*/ + +#ifndef CPPCODEPARSER_H +#define CPPCODEPARSER_H + +#include <qregexp.h> + +#include "codeparser.h" +#include "node.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 QString headerFileNameFilter(); + virtual QString 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); + 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 matchCompat(); + 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()); + 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); + 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; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/cpptoqsconverter.cpp b/tools/qdoc3/cpptoqsconverter.cpp new file mode 100644 index 0000000..dfc81bc --- /dev/null +++ b/tools/qdoc3/cpptoqsconverter.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cpptoqsconverter.cpp +*/ + +#include "config.h" +#include "cpptoqsconverter.h" + +QT_BEGIN_NAMESPACE + +#define CONFIG_QUICK "quick" +#define CONFIG_INDENTSIZE "indentsize" + +void setTabSize( int size ); +void setIndentSize( int size ); +int columnForIndex( const QString& t, int index ); +int indentForBottomLine( const QStringList& program, QChar typedIn ); + +static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*"; + +QRegExp CppToQsConverter::qClassRegExp; +QRegExp CppToQsConverter::addressOperatorRegExp; +QRegExp CppToQsConverter::gulbrandsenRegExp; +int CppToQsConverter::tabSize; + +ClassNode *CppToQsConverter::findClassNode( Tree *qsTree, + const QString& qtName ) +{ + ClassNode *classe = (ClassNode *) qsTree->findNode( QStringList(qtName), Node::Class ); + if ( classe == 0 ) + classe = (ClassNode *) qsTree->findNode( QStringList(qtName.mid(1)), Node::Class ); + return classe; +} + +QString CppToQsConverter::convertedDataType( Tree *qsTree, + const QString& leftType, + const QString& /* rightType */ ) +{ + QString s = leftType; + + if ( s.startsWith("const ") ) + s = s.mid( 6 ); + while ( s.endsWith("*") || s.endsWith("&") || s.endsWith(" ") ) + s.truncate( s.length() - 1 ); + + switch ( s[0].unicode() ) { + case 'Q': + if ( s == "QCString" ) { + return "String"; + } else { + Node *node = findClassNode( qsTree, s ); + if ( node == 0 ) { + return ""; + } else { + return node->name(); + } + } + break; + case 'b': + if ( s == "bool" ) + return "Boolean"; + break; + case 'c': + if ( s == "char" ) { + if ( leftType == "const char *" ) { + return "String"; + } else { + return "Number"; + } + } + break; + case 'd': + if ( s == "double" ) + return "Number"; + break; + case 'f': + if ( s == "float" ) + return "Number"; + case 'i': + if ( s == "int" ) + return "Number"; + break; + case 'l': + if ( s == "long" || s == "long int" || s == "long long" || + s == "long long int" || s == "long double" ) + return "Number"; + break; + case 's': + if ( s == "short" || s == "short int" || s == "signed char" || + s == "signed short" || s == "signed short int" || s == "signed" || + s == "signed int" || s == "signed long" || s == "signed long int" ) + return "Number"; + break; + case 'u': + if ( s == "uchar" || s == "unsigned" || s == "unsigned char" || + s == "ushort" || s == "unsigned short" || + s == "unsigned short int" || s == "uint" || s == "unsigned int" || + s == "ulong" || s == "unsigned long" || s == "unsigned long int" ) + return "Number"; + break; + case 'v': + if ( s == "void" ) + return ""; + } + return s; +} + +QString CppToQsConverter::convertedCode( Tree *qsTree, const QString& code, + const QSet<QString>& classesWithNoQ ) +{ + QString result; + QStringList program; + QStringList comments; + int programWidth = 0; + + QStringList originalLines = code.split("\n"); + QStringList::ConstIterator ol = originalLines.begin(); + while ( ol != originalLines.end() ) { + QString code = (*ol).trimmed(); + QString comment; + + int slashSlash = code.indexOf( "//" ); + if ( slashSlash != -1 ) { + comment = code.mid( slashSlash ); + code.truncate( slashSlash ); + code = code.trimmed(); + } + + code = convertCodeLine( qsTree, program, code, classesWithNoQ ); + program.append( code ); + + comment = convertComment( qsTree, comment, classesWithNoQ ); + comments.append( comment ); + + int n = indentForBottomLine( program, QChar::Null ); + for ( int i = 0; i < n; i++ ) + program.last().prepend( " " ); + + int width = columnForIndex( program.last(), program.last().length() ); + if ( !comment.isEmpty() && width > programWidth ) + programWidth = width; + ++ol; + } + + programWidth = ( (programWidth + (tabSize - 1) + 2) / tabSize ) * tabSize; + + QStringList::ConstIterator p = program.begin(); + QStringList::ConstIterator c = comments.begin(); + while ( c != comments.end() ) { + if ( c != comments.begin() ) + result += "\n"; + + if ( (*p).trimmed().isEmpty() ) { + if ( !(*c).isEmpty() ) + result += *p; + } else { + result += *p; + if ( !(*c).isEmpty() ) { + int i = columnForIndex( *p, (*p).length() ); + while ( i++ < programWidth ) + result += " "; + } + } + result += *c; + ++p; + ++c; + } + return result; +} + +void CppToQsConverter::initialize( const Config& config ) +{ + qClassRegExp.setPattern( "\\bQ([A-Z][A-Za-z]+)\\b" ); + addressOperatorRegExp.setPattern( "([(\\s])[*&]([a-zA-Z])" ); + gulbrandsenRegExp.setPattern( "\\b::\\b|->" ); + + tabSize = config.getInt( CONFIG_TABSIZE ); + setTabSize( tabSize ); + + int size = config.getInt( CONFIG_QUICK + Config::dot + CONFIG_INDENTSIZE ); + if ( size > 0 ) + setIndentSize( size ); +} + +void CppToQsConverter::terminate() +{ +} + +QString CppToQsConverter::convertCodeLine( Tree *qsTree, + const QStringList& program, + const QString& code, + const QSet<QString>& classesWithNoQ ) +{ + static QString dataTypeFmt = + "(?!return)(?:const\\b\\s*)?[A-Za-z_]+(?:\\s*[*&])?"; + static QRegExp funcPrototypeRegExp( + "(" + dataTypeFmt + ")\\s*\\b([A-Z][a-zA-Z_0-9]*::)?" + "([a-z][a-zA-Z_0-9]*)\\(([^);]*)(\\)?)(?:\\s*const)?" ); + static QRegExp paramRegExp( + "^\\s*(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*(,)?\\s*" ); + static QRegExp uninitVarRegExp( + "(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*);" ); + static QRegExp eqVarRegExp( + dataTypeFmt + "\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*=(\\s*)(.*)" ); + static QRegExp ctorVarRegExp( + "(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\((.*)\\);" ); + static QRegExp qdebugRegExp( + "q(?:Debug|Warning|Fatal)\\(\\s*(\"(?:\\\\.|[^\"])*\")\\s*" + "(?:,\\s*(\\S(?:[^,]*\\S)?))?\\s*\\);" ); + static QRegExp coutRegExp( "c(?:out|err)\\b(.*);" ); + static QRegExp lshiftRegExp( "\\s*<<\\s*" ); + static QRegExp endlRegExp( "^endl$" ); + + if ( code.isEmpty() || code == "{" || code == "}" ) + return code; + + QString result; + + if ( funcPrototypeRegExp.exactMatch(code) ) { + QString returnType = funcPrototypeRegExp.cap( 1 ); + QString className = funcPrototypeRegExp.cap( 2 ); + QString funcName = funcPrototypeRegExp.cap( 3 ); + QString params = funcPrototypeRegExp.cap( 4 ).trimmed(); + bool toBeContinued = funcPrototypeRegExp.cap( 5 ).isEmpty(); + // ### unused + Q_UNUSED(toBeContinued); + + className.replace( "::", "." ); + + result = "function " + className + funcName + "("; + + if ( !params.isEmpty() && params != "void" ) { + result += " "; + int i = funcPrototypeRegExp.pos( 4 ); + while ( (i = paramRegExp.indexIn(code, i, + QRegExp::CaretAtOffset)) != -1 ) { + QString dataType = paramRegExp.cap( 1 ); + QString paramName = paramRegExp.cap( 2 ); + QString comma = paramRegExp.cap( 3 ); + + result += paramName + " : " + + convertedDataType( qsTree, dataType ); + if ( comma.isEmpty() ) + break; + result += ", "; + i += paramRegExp.matchedLength(); + } + result += " "; + } + + result += ")"; + returnType = convertedDataType( qsTree, returnType ); + if ( !returnType.isEmpty() ) + result += " : " + returnType; + } else if ( uninitVarRegExp.exactMatch(code) ) { + QString dataType = uninitVarRegExp.cap( 1 ); + QString varName = uninitVarRegExp.cap( 2 ); + + result = "var " + varName; + dataType = convertedDataType( qsTree, dataType ); + if ( !dataType.isEmpty() ) + result += " : " + dataType; + result += ";"; + } else if ( eqVarRegExp.exactMatch(code) ) { + QString varName = eqVarRegExp.cap( 1 ); + QString value = eqVarRegExp.cap( 3 ); + + value = convertExpr( qsTree, value, classesWithNoQ ); + result += "var " + varName + " = " + value; + } else if ( ctorVarRegExp.exactMatch(code) ) { + QString dataType = ctorVarRegExp.cap( 1 ); + QString varName = ctorVarRegExp.cap( 2 ); + QString value = ctorVarRegExp.cap( 3 ).trimmed(); + + result += "var " + varName + " = "; + + dataType = convertedDataType( qsTree, dataType ); + value = convertExpr( qsTree, value, classesWithNoQ ); + + if ( dataType.isEmpty() || dataType == "String" ) { + if ( value.contains(",") ) { + result += "..."; + } else { + result += value; + } + } else { + result += "new " + dataType; + if ( !value.isEmpty() ) + result += "( " + value + " )"; + } + result += ";"; + } else if ( qdebugRegExp.exactMatch(code) ) { + QString fmt = qdebugRegExp.cap( 1 ); + QString arg1 = qdebugRegExp.cap( 2 ); + + result += "println "; + int i = 0; + while ( i < (int) fmt.length() ) { + if ( fmt[i] == '%' ) { + int percent = i; + i++; + while ( i < (int) fmt.length() && + QString("diouxXeEfFgGaAcsCSpn%\"").indexOf(fmt[i]) == -1 ) + i++; + if ( fmt[i] == '%' ) { + result += fmt[i++]; + } else if ( fmt[i] != '"' ) { + if ( percent == 1 ) { + result.truncate( result.length() - 1 ); + } else { + result += "\" + "; + } + i++; + if ( arg1.endsWith(".latin1()") ) + arg1.truncate( arg1.length() - 9 ); + result += arg1; + if ( i == (int) fmt.length() - 1 ) { + i++; + } else { + result += " + \""; + } + } + } else { + result += fmt[i++]; + } + } + result += ";"; + } else if ( coutRegExp.exactMatch(code) && + program.filter("var cout").isEmpty() ) { + QStringList args = coutRegExp.cap(1).split(lshiftRegExp); + args.replaceInStrings( endlRegExp, "\"\\n\"" ); + if ( args.last() == "\"\\n\"" ) { + args.erase( args.end() - 1 ); + if ( args.isEmpty() ) + args << "\"\""; + result += "println "; + } else { + result += "print "; + } + result += args.join( " + " ) + ";"; + } else { + result = convertExpr( qsTree, code, classesWithNoQ ); + } + return result; +} + +QString CppToQsConverter::convertComment( Tree * /* qsTree */, + const QString& comment, + const QSet<QString>& classesWithNoQ ) + +{ + QString result = comment; + + result.replace( "TRUE", "true" ); + result.replace( "FALSE", "false" ); + result.replace( addressOperatorRegExp, "\\1\\2" ); + result.replace( gulbrandsenRegExp, "." ); + + int i = 0; + while ( (i = result.indexOf(qClassRegExp, i)) != -1 ) { + if ( classesWithNoQ.contains(qClassRegExp.cap(1)) ) + result.remove( i, 1 ); + i++; + } + return result; +} + +QString CppToQsConverter::convertExpr( Tree *qsTree, const QString& expr, + const QSet<QString>& classesWithNoQ ) +{ + // suboptimal + return convertComment( qsTree, expr, classesWithNoQ ); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/cpptoqsconverter.h b/tools/qdoc3/cpptoqsconverter.h new file mode 100644 index 0000000..baf801c --- /dev/null +++ b/tools/qdoc3/cpptoqsconverter.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + cpptoqsconverter.h +*/ + +#ifndef CPPTOQSCONVERTER_H +#define CPPTOQSCONVERTER_H + +#include <qregexp.h> + +#include "tree.h" + +QT_BEGIN_NAMESPACE + +class CppToQsConverter +{ +public: + CppToQsConverter() { } + + ClassNode *findClassNode( Tree *qsTree, const QString& qtName ); + QString convertedDataType( Tree *qsTree, const QString& leftType, + const QString& rightType = "" ); + QString convertedCode( Tree *qsTree, const QString& code, + const QSet<QString>& classesWithNoQ ); + + static void initialize( const Config& config ); + static void terminate(); + +private: + void clearState(); + QString convertCodeLine( Tree *qsTree, const QStringList& program, + const QString& code, + const QSet<QString>& classesWithNoQ ); + QString convertComment( Tree *qsTree, const QString& comment, + const QSet<QString>& classesWithNoQ ); + QString convertExpr( Tree *qsTree, const QString& expr, + const QSet<QString>& classesWithNoQ ); + void updateDelimDepths( const QString& code ); + + static QRegExp qClassRegExp; + static QRegExp addressOperatorRegExp; + static QRegExp gulbrandsenRegExp; + static int tabSize; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/dcfsection.cpp b/tools/qdoc3/dcfsection.cpp new file mode 100644 index 0000000..5bff070 --- /dev/null +++ b/tools/qdoc3/dcfsection.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qfile.h> +#include <qfileinfo.h> +#include <qtextstream.h> + +#include "dcfsection.h" +#include "htmlgenerator.h" + +QT_BEGIN_NAMESPACE + +void appendDcfSubSection( DcfSection *dcfSect, const DcfSection& sub ) +{ + dcfSect->subsections.append( sub ); +} + +void appendDcfSubSections( DcfSection *dcfSect, const QList<DcfSection>& subs ) +{ + dcfSect->subsections += subs; +} + +void generateDcfSubSections( QString indent, QTextStream& out, const DcfSection& sect ) +{ + QList<DcfSection>::const_iterator ss = sect.subsections.constBegin(); + while ( ss != sect.subsections.constEnd() ) { + out << indent << "<section ref=\"" << HtmlGenerator::cleanRef(HtmlGenerator::protect((*ss).ref)) + << "\" title=\"" << HtmlGenerator::protect((*ss).title) << "\""; + if ((*ss).keywords.isEmpty() && (*ss).subsections.isEmpty()) { + out << "/>\n"; + } else { + out << ">\n"; + QString indentIndent = indent + " "; + QList<QPair<QString, QString> >::const_iterator k = (*ss).keywords.constBegin(); + while ( k != (*ss).keywords.constEnd() ) { + out << indentIndent << "<keyword ref=\"" << HtmlGenerator::cleanRef((*k).second) << "\">" + << HtmlGenerator::protect((*k).first) << "</keyword>\n"; + ++k; + } + + generateDcfSubSections( indentIndent, out, *ss ); + out << indent << "</section>\n"; + } + ++ss; + } + out.flush(); +} + +void generateDcfSections( const DcfSection& rootSect, const QString& fileName, + const QString& /* category */ ) +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + QTextStream out(&file); + + QString icon = QFileInfo(fileName).baseName() + ".png"; + + out << "<!DOCTYPE DCF>\n"; + out << "<DCF ref=\"" << HtmlGenerator::cleanRef(HtmlGenerator::protect(rootSect.ref)); + if (icon != "qmake.png") + out << "\" icon=\"" << HtmlGenerator::protect(icon); + out << "\" imagedir=\"../../gif\" title=\"" << HtmlGenerator::protect(rootSect.title) + + "\">\n"; + + generateDcfSubSections( "", out, rootSect ); + + out << "</DCF>\n"; + out.flush(); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/dcfsection.h b/tools/qdoc3/dcfsection.h new file mode 100644 index 0000000..91d31ed --- /dev/null +++ b/tools/qdoc3/dcfsection.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DCFSECTION_H +#define DCFSECTION_H + +#include <qlist.h> +#include <qpair.h> +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +class QTextStream; + +struct DcfSection +{ + QString title; + QString ref; + QList<QPair<QString, QString> > keywords; + QList<DcfSection> subsections; +}; + +inline bool operator<( const DcfSection& s1, const DcfSection& s2 ) { + QString title1 = s1.title; + QString title2 = s2.title; + + // cheat with Q3 classes + if (title1.startsWith("Q3")) + title1.insert(1, '~'); + if (title2.startsWith("Q3")) + title2.insert(1, '~'); + + int delta = title1.toLower().compare( title2.toLower() ); + if ( delta == 0 ) { + delta = title1.compare( title2 ); + if ( delta == 0 ) + delta = s1.ref.localeAwareCompare( s2.ref ); + } + return delta < 0; +} + +inline bool operator>( const DcfSection& s1, const DcfSection& s2 ) { return s2 < s1; } +inline bool operator<=( const DcfSection& s1, const DcfSection& s2 ) { return !( s2 < s1 ); } +inline bool operator>=( const DcfSection& s1, const DcfSection& s2 ) { return !( s1 < s2 ); } +inline bool operator==( const DcfSection& s1, const DcfSection& s2 ) { return &s1 == &s2; } +inline bool operator!=( const DcfSection& s1, const DcfSection& s2 ) { return !( s1 == s2 ); } + +void appendDcfSubSection(DcfSection *dcfSect, const DcfSection &sub); +void appendDcfSubSections(DcfSection *dcfSect, const QList<DcfSection> &subs); +void generateDcfSubSections(QString indent, QTextStream &out, const DcfSection §); +void generateDcfSections(const DcfSection &rootSect, const QString& fileName, + const QString& category ); + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/doc.cpp b/tools/qdoc3/doc.cpp new file mode 100644 index 0000000..1d3a7d4 --- /dev/null +++ b/tools/qdoc3/doc.cpp @@ -0,0 +1,4946 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 <qdebug.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qhash.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <ctype.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString) +Q_GLOBAL_STATIC(QStringList, null_QStringList) +Q_GLOBAL_STATIC(QList<Text>, null_QList_Text) +Q_GLOBAL_STATIC(QStringMap, null_QStringMap) + +struct Macro +{ + QString defaultDef; + Location defaultDefLocation; + QStringMap otherDefs; + int numParams; +}; + +enum { + CMD_A, CMD_ABSTRACT, CMD_BADCODE, CMD_BASENAME, CMD_BOLD, + CMD_BRIEF, CMD_C, CMD_CAPTION, CMD_CHAPTER, CMD_CODE, + CMD_CODELINE, CMD_DOTS, CMD_ELSE, CMD_ENDABSTRACT, + CMD_ENDCHAPTER, CMD_ENDCODE, CMD_ENDFOOTNOTE, CMD_ENDIF, + CMD_ENDLEGALESE, CMD_ENDLINK, CMD_ENDLIST, CMD_ENDOMIT, + CMD_ENDPART, CMD_ENDQUOTATION, CMD_ENDRAW, CMD_ENDSECTION1, + CMD_ENDSECTION2, CMD_ENDSECTION3, CMD_ENDSECTION4, + CMD_ENDSIDEBAR, CMD_ENDTABLE, CMD_EXPIRE, CMD_FOOTNOTE, + CMD_GENERATELIST, CMD_GRANULARITY, CMD_HEADER, CMD_I, + CMD_IF, CMD_IMAGE, CMD_INCLUDE, CMD_INLINEIMAGE, CMD_INDEX, + CMD_KEYWORD, CMD_L, CMD_LEGALESE, CMD_LINK, CMD_LIST, + CMD_META, CMD_NEWCODE, 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_SKIPLINE, CMD_SKIPTO, + CMD_SKIPUNTIL, CMD_SNIPPET, CMD_SUB, CMD_SUP, CMD_TABLE, + CMD_TABLEOFCONTENTS, CMD_TARGET, CMD_TT, CMD_UNDERLINE, + CMD_UNICODE, CMD_VALUE, CMD_WARNING, +#ifdef QDOC_QML + CMD_QML, CMD_ENDQML, CMD_CPP, CMD_ENDCPP, CMD_QMLTEXT, + CMD_ENDQMLTEXT, CMD_CPPTEXT, CMD_ENDCPPTEXT, +#endif + NOT_A_CMD +}; + +static struct { + const char *english; + int no; + QString *alias; +} cmds[] = { + { "a", CMD_A, 0 }, + { "abstract", CMD_ABSTRACT, 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}, + { "dots", CMD_DOTS, 0 }, + { "else", CMD_ELSE, 0 }, + { "endabstract", CMD_ENDABSTRACT, 0 }, + { "endchapter", CMD_ENDCHAPTER, 0 }, + { "endcode", CMD_ENDCODE, 0 }, + { "endfootnote", CMD_ENDFOOTNOTE, 0 }, + { "endif", CMD_ENDIF, 0 }, + { "endlegalese", CMD_ENDLEGALESE, 0 }, + { "endlink", CMD_ENDLINK, 0 }, + { "endlist", CMD_ENDLIST, 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 }, + { "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 }, + { "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 }, + { "link", CMD_LINK, 0 }, + { "list", CMD_LIST, 0 }, + { "meta", CMD_META, 0 }, + { "newcode", CMD_NEWCODE, 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 }, // ### don't document for now + { "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 }, // ### don't document for now + { "skipline", CMD_SKIPLINE, 0 }, + { "skipto", CMD_SKIPTO, 0 }, + { "skipuntil", CMD_SKIPUNTIL, 0 }, + { "snippet", CMD_SNIPPET, 0 }, + { "sub", CMD_SUB, 0 }, + { "sup", CMD_SUP, 0 }, + { "table", CMD_TABLE, 0 }, + { "tableofcontents", CMD_TABLEOFCONTENTS, 0 }, + { "target", CMD_TARGET, 0 }, + { "tt", CMD_TT, 0 }, + { "underline", CMD_UNDERLINE, 0 }, + { "unicode", CMD_UNICODE, 0 }, + { "value", CMD_VALUE, 0 }, + { "warning", CMD_WARNING, 0 }, +#ifdef QDOC_QML + { "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 }, +#endif + { 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::SectioningUnit granularity; + Doc::SectioningUnit sectioningUnit; // ### + QList<Atom*> tableOfContents; + QList<int> tableOfContentsLevels; + QList<Atom*> keywords; + QList<Atom*> targets; + QStringMap 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; +}; + +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. +} + +DocPrivate::~DocPrivate() +{ + delete extra; +} + +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(":"); + } 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); + + 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); + void startFormat(const QString& format, int cmd); + bool openCommand(int cmd); + bool closeCommand(int endCmd); + void startSection(Doc::SectioningUnit unit, int cmd); + void endSection(int unit, int endCmd); + void parseAlso(); + void append(Atom::Type type, const QString& string = ""); + void appendChar(QChar ch); + void appendWord(const QString &word); + void appendToCode(const QString &code); + 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); + Doc::SectioningUnit getSectioningUnit(); + QString getArgument(bool verbatim = false); + 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 ParaState { OutsidePara, InsideSingleLinePara, InsideMultiLinePara }; + ParaState paraState; + bool inTableHeader; + bool inTableRow; + bool inTableItem; + bool indexStartedPara; // ### rename + Atom::Type pendingParaLeftType; + Atom::Type pendingParaRightType; + QString pendingParaString; + + int braceDepth; + int minIndent; + Doc::SectioningUnit currentSectioningUnit; + QMap<QString, Location> targetMap; + QMap<int, QString> pendingFormats; + QStack<int> openedCommands; + QStack<OpenedList> openedLists; + Quoter quoter; +}; + +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) +{ + in = source; + pos = 0; + len = in.length(); + cachedLoc = docPrivate->start_loc; + cachedPos = 0; + priv = docPrivate; + priv->text << Atom::Nop; + + paraState = OutsidePara; + inTableHeader = false; + inTableRow = false; + inTableItem = false; + indexStartedPara = false; + pendingParaLeftType = Atom::Nop; + pendingParaRightType = Atom::Nop; + + braceDepth = 0; + minIndent = INT_MAX; + currentSectioningUnit = Doc::Book; + openedCommands.push(CMD_OMIT); + quoter.reset(); + + CodeMarker *marker = 0; + Atom *currentLinkAtom = 0; + QString x; + 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(); + x = getArgument(); + append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER); + append(Atom::String, x); + append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER); + priv->params.insert(x); + break; + case CMD_ABSTRACT: + if (openCommand(cmd)) { + leavePara(); + append(Atom::AbstractLeft); + } + break; + case CMD_BADCODE: + leavePara(); +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) + append(Atom::CodeBad,getUnmarkedCode(CMD_BADCODE)); + else + append(Atom::CodeBad,getCode(CMD_BADCODE, marker)); +#else + append(Atom::CodeBad,getCode(CMD_BADCODE, marker)); +#endif + break; + case CMD_BASENAME: + leavePara(); + insertBaseName(getArgument()); + break; + case CMD_BOLD: + startFormat(ATOM_FORMATTING_BOLD, cmd); + break; + case CMD_BRIEF: + leavePara(); + enterPara(Atom::BriefLeft, Atom::BriefRight); + break; + case CMD_C: + enterPara(); + x = untabifyEtc(getArgument(true)); +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) + append(Atom::C, x); + else { + marker = CodeMarker::markerForCode(x); + append(Atom::C, marker->markedUpCode(x, 0, "")); + } +#else + marker = CodeMarker::markerForCode(x); + append(Atom::C, marker->markedUpCode(x, 0, "")); +#endif + break; + case CMD_CAPTION: + leavePara(); + /* ... */ + break; + case CMD_CHAPTER: + startSection(Doc::Chapter, cmd); + break; + case CMD_CODE: + leavePara(); +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) + append(Atom::Code, getUnmarkedCode(CMD_CODE)); + else + append(Atom::Code, getCode(CMD_CODE, marker)); +#else + append(Atom::Code, getCode(CMD_CODE, marker)); +#endif + break; +#ifdef QDOC_QML + case CMD_QML: + leavePara(); + append(Atom::Qml, getCode(CMD_QML, marker)); + break; +#endif + case CMD_CODELINE: + { +#ifdef QDOC2DOX + if (!quoting && !DoxWriter::isDoxPass()) { + 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, " "); + } +#else + 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, " "); + } +#endif + } + break; + case CMD_DOTS: + { +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, " ..."); + } + else 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); + } +#else + 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); + } +#endif + } + 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(0, cmd); + break; + case CMD_ENDCODE: + closeCommand(cmd); + break; +#ifdef QDOC_QML + case CMD_ENDQML: + closeCommand(cmd); + break; +#endif + case CMD_ENDFOOTNOTE: + if (closeCommand(cmd)) { + leavePara(); + append(Atom::FootnoteRight); + paraState = InsideMultiLinePara; // ### + } + 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(" ")) + 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_ENDOMIT: + closeCommand(cmd); + break; + case CMD_ENDPART: + endSection(-1, 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(1, cmd); + break; + case CMD_ENDSECTION2: + endSection(2, cmd); + break; + case CMD_ENDSECTION3: + endSection(3, cmd); + break; + case CMD_ENDSECTION4: + endSection(4, 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 = OutsidePara; // ### + } + 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: + 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_INCLUDE: + include(getArgument()); + break; + case CMD_INLINEIMAGE: + enterPara(); + append(Atom::InlineImage, getArgument()); + append(Atom::ImageText, getRestOfLine()); + append(Atom::String, " "); + break; + case CMD_INDEX: + if (paraState == OutsidePara) { + 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()) { + x = getArgument(); + append(Atom::Link, x); + if (isLeftBraceAhead()) { + currentLinkAtom = priv->text.lastAtom(); + startFormat(ATOM_FORMATTING_LINK, cmd); + } else { + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + append(Atom::String, cleanLink(x)); + append(Atom::FormattingRight, ATOM_FORMATTING_LINK); + } + } else { + x = getArgument(); + append(Atom::Link, x); + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + append(Atom::String, cleanLink(x)); + 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(); + x = getArgument(); + append(Atom::Link, x); + append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); + skipSpacesOrOneEndl(); + } + break; + case CMD_LIST: + if (openCommand(cmd)) { + leavePara(); + openedLists.push(OpenedList(location(), + getOptionalArgument())); + } + break; + case CMD_META: + priv->constructExtra(); + x = getArgument(); + priv->extra->metaMap.insert(x, getRestOfLine()); + break; + case CMD_NEWCODE: + location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE))); + break; + case CMD_O: + 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) { + x = "1,1"; + if (isLeftBraceAhead()) + x = getArgument(); + + if (!inTableHeader && !inTableRow) { + location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'") + .arg(cmdName(CMD_HEADER)) + .arg(cmdName(CMD_ROW)) + .arg(cmdName(CMD_O))); + append(Atom::TableRowLeft); + inTableRow = true; + } else if (inTableItem) { + append(Atom::TableItemRight); + inTableItem = false; + } + + append(Atom::TableItemLeft, x); + 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(); +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) { + append(Atom::CodeOld, getUnmarkedCode(CMD_OLDCODE)); + append(Atom::CodeNew, getUnmarkedCode(CMD_NEWCODE)); + } + else { + append(Atom::CodeOld, getCode(CMD_OLDCODE, marker)); + append(Atom::CodeNew, getCode(CMD_NEWCODE, marker)); + } +#else + append(Atom::CodeOld, getCode(CMD_OLDCODE, marker)); + append(Atom::CodeNew, getCode(CMD_NEWCODE, marker)); +#endif + break; + case CMD_OMIT: + getUntilEnd(cmd); + break; + case CMD_OMITVALUE: + x = getArgument(); + if (!priv->enumItemList.contains(x)) + priv->enumItemList.append(x); + if (!priv->omitEnumItemList.contains(x)) + priv->omitEnumItemList.append(x); + 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(); + x = getRestOfLine(); + if (!quoting) { + quoter.quoteTo(location(), cmdStr, + slashed(marker->functionBeginRegExp(x))); + append(Atom::Code, + quoter.quoteUntil(location(), cmdStr, + slashed(marker->functionEndRegExp(x)))); + quoter.reset(); + } else { + append(Atom::CodeQuoteCommand, cmdStr); + append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(x))); + } + break; + case CMD_RAW: + leavePara(); + x = getRestOfLine(); + if (x.isEmpty()) + location().warning(tr("Missing format name after '\\%1") + .arg(cmdName(CMD_RAW))); + append(Atom::FormatIf, x); + append(Atom::RawString, untabifyEtc(getUntilEnd(cmd))); + append(Atom::FormatElse); + append(Atom::FormatEndif); + break; + case CMD_ROW: + if (openedCommands.top() == CMD_TABLE) { + leaveTableRow(); + append(Atom::TableRowLeft); + 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_SNIPPET: + leavePara(); + { + QString snippet = getArgument(); + QString identifier = getRestOfLine(); +#ifdef QDOC2DOX + if (quoting || DoxWriter::isDoxPass()) { + append(Atom::SnippetCommand, cmdStr); + append(Atom::SnippetLocation, snippet); + append(Atom::SnippetIdentifier, identifier); + } else { + Doc::quoteFromFile(location(),quoter,snippet); + appendToCode(quoter.quoteSnippet(location(), + identifier)); + } +#else + if (quoting) { + append(Atom::SnippetCommand, cmdStr); + append(Atom::SnippetLocation, snippet); + append(Atom::SnippetIdentifier, identifier); + } else { + Doc::quoteFromFile(location(),quoter,snippet); + appendToCode(quoter.quoteSnippet(location(), + identifier)); + } +#endif + } + break; + case CMD_SUB: + startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd); + break; + case CMD_SUP: + startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd); + break; + case CMD_TABLE: + x = getRestOfLine(); + if (openCommand(cmd)) { + leavePara(); + append(Atom::TableLeft, x); + inTableHeader = false; + inTableRow = false; + inTableItem = false; + } + break; + case CMD_TABLEOFCONTENTS: + x = "1"; + if (isLeftBraceAhead()) + x = getArgument(); + x += ","; + x += QString::number((int)getSectioningUnit()); + append(Atom::TableOfContents, x); + 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(); + x = getArgument(); + { + bool ok; + uint unicodeChar = x.toUInt(&ok, 0); + if (!ok || + (unicodeChar == 0x0000) || + (unicodeChar > 0xFFFE)) { + location().warning(tr("Invalid Unicode character '%1' specified " + "with '%2'") + .arg(x, cmdName(CMD_UNICODE))); + } else { + append(Atom::String, QChar(unicodeChar)); + } + } + break; + case CMD_VALUE: + leaveValue(); + if (openedLists.top().style() == OpenedList::Value) { + x = getArgument(); + if (!priv->enumItemList.contains(x)) + priv->enumItemList.append(x); + + openedLists.top().next(); + append(Atom::ListTagLeft, ATOM_LIST_VALUE); + append(Atom::String, x); + append(Atom::ListTagRight, ATOM_LIST_VALUE); + append(Atom::ListItemLeft, ATOM_LIST_VALUE); + + skipSpacesOrOneEndl(); + if (isBlankLine()) + append(Atom::Nop); + } else { + // ### problems + } + break; + case CMD_WARNING: + startNewPara(); + append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + append(Atom::String, "Warning:"); + append(Atom::FormattingRight, ATOM_FORMATTING_BOLD); + append(Atom::String, " "); + break; + case CMD_OVERLOAD: // qdoc --> doxygen + priv->metacommandsUsed.insert(cmdStr); + x.clear(); + if (!isBlankLine()) + x = getRestOfLine(); + if (!x.isEmpty()) { + append(Atom::ParaLeft); + append(Atom::String, "This function overloads "); + append(Atom::AutoLink,x); + append(Atom::String, "."); + append(Atom::ParaRight); + } + else { + append(Atom::ParaLeft); + append(Atom::String, + "This is an overloaded member function, " + "provided for convenience."); + append(Atom::ParaRight); + x = getMetaCommandArgument(cmdStr); + } + priv->metaCommandMap[cmdStr].append(x); + break; + case NOT_A_CMD: + if (metaCommandSet.contains(cmdStr)) { + priv->metacommandsUsed.insert(cmdStr); + QString xxx = getMetaCommandArgument(cmdStr); + priv->metaCommandMap[cmdStr].append(xxx); + } + 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, macro.defaultDef); + 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 == OutsidePara) { + if (ch.isSpace()) { + ++pos; + newWord = false; + } else { + enterPara(); + newWord = true; + } + } + else { + if (ch.isSpace()) { + ++pos; + if ((ch == '\n') && + (paraState == InsideSingleLinePara || + 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))); + } + + while (currentSectioningUnit > Doc::Chapter) { + int delta = currentSectioningUnit - priv->extra->sectioningUnit; + append(Atom::SectionRight, QString::number(delta)); + currentSectioningUnit = Doc::SectioningUnit(int(currentSectioningUnit) - 1); + } + + if (priv->extra && priv->extra->granularity < priv->extra->sectioningUnit) + priv->extra->granularity = priv->extra->sectioningUnit; + 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 (currentSectioningUnit == priv->extra->sectioningUnit) { + priv->extra->baseName = baseName; + } else { + Atom *atom = priv->text.firstAtom(); + Atom *sectionLeft = 0; + + int delta = currentSectioningUnit - priv->extra->sectioningUnit; + + 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) +{ + 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 leaf file '%1'").arg(fileName)); + } else { + QFile inFile(filePath); + if (!inFile.open(QFile::ReadOnly)) { + location().warning(tr("Cannot open leaf file '%1'") + .arg(userFriendlyFilePath)); + } else { + location().push(userFriendlyFilePath); + + QTextStream inStream(&inFile); + QString includedStuff = inStream.readAll(); + inFile.close(); + + in.insert(pos, includedStuff); + len = in.length(); + openedInputs.push(pos + includedStuff.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; + } + } + + if (ok) { + openedCommands.push(cmd); + } else { + location().warning(tr("Cannot use '\\%1' within '\\%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::SectioningUnit unit, int cmd) +{ + leavePara(); + + if (currentSectioningUnit == Doc::Book) { + if (unit > Doc::Section1) + location().warning(tr("Unexpected '\\%1' without '\\%2'") + .arg(cmdName(cmd)) + .arg(cmdName(CMD_SECTION1))); + currentSectioningUnit = (Doc::SectioningUnit) (unit - 1); + priv->constructExtra(); + priv->extra->sectioningUnit = currentSectioningUnit; + } + + if (unit <= priv->extra->sectioningUnit) { + location().warning(tr("Unexpected '\\%1' in this documentation") + .arg(cmdName(cmd))); + } else if (unit - currentSectioningUnit > 1) { + location().warning(tr("Unexpected '\\%1' at this point") + .arg(cmdName(cmd))); + } else { + if (currentSectioningUnit >= unit) + endSection(unit, cmd); + + int delta = unit - priv->extra->sectioningUnit; + append(Atom::SectionLeft, QString::number(delta)); + priv->constructExtra(); + priv->extra->tableOfContents.append(priv->text.lastAtom()); + priv->extra->tableOfContentsLevels.append(unit); + enterPara(Atom::SectionHeadingLeft, + Atom::SectionHeadingRight, + QString::number(delta)); + currentSectioningUnit = unit; + } +} + +void DocParser::endSection(int unit, int endCmd) +{ + leavePara(); + + if (unit < priv->extra->sectioningUnit) { + location().warning(tr("Unexpected '\\%1' in this documentation") + .arg(cmdName(endCmd))); + } else if (unit > currentSectioningUnit) { + location().warning(tr("Unexpected '\\%1' at this point") + .arg(cmdName(endCmd))); + } else { + while (currentSectioningUnit >= unit) { + int delta = currentSectioningUnit - priv->extra->sectioningUnit; + append(Atom::SectionRight, QString::number(delta)); + currentSectioningUnit = + (Doc::SectioningUnit) (currentSectioningUnit - 1); + } + } +} + +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))); + } + } +} + +void DocParser::append(Atom::Type type, const QString &string) +{ + Atom::Type lastType = priv->text.lastAtom()->type(); +#ifdef QDOC_QML + if (((lastType == Atom::Code) || (lastType == Atom::Code)) && +#else + if ((lastType == Atom::Code) && +#endif + priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n"))) + priv->text.lastAtom()->chopString(); + priv->text << Atom(type, string); +} + +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) + append(Atom::Qml); +#else + if (lastType != Atom::Code) + append(Atom::Code); +#endif + priv->text.lastAtom()->appendString(markedCode); +} + +void DocParser::startNewPara() +{ + leavePara(); + enterPara(); +} + +void DocParser::enterPara(Atom::Type leftType, + Atom::Type rightType, + const QString& string) +{ + if (paraState == OutsidePara) { + if (priv->text.lastAtom()->type() != Atom::ListItemLeft) + leaveValueList(); + append(leftType, string); + indexStartedPara = false; + pendingParaLeftType = leftType; + pendingParaRightType = rightType; + pendingParaString = string; + if ( +#if 0 + leftType == Atom::BriefLeft || +#endif + leftType == Atom::SectionHeadingLeft) { + paraState = InsideSingleLinePara; + } else { + paraState = InsideMultiLinePara; + } + skipSpacesOrOneEndl(); + } +} + +void DocParser::leavePara() +{ + if (paraState != OutsidePara) { + 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(" ")) { + priv->text.lastAtom()->chopString(); + } + append(pendingParaRightType, pendingParaString); + } + paraState = OutsidePara; + 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 ((def[j] == '\\') && (j < def.size() - 1) && + ((paramNo = def[j + 1].digitValue()) >= 1) && + (paramNo <= numParams)) { + if (!rawString.isEmpty()) { + append(Atom::RawString, rawString); + rawString = ""; + } + append(Atom::String, args[paramNo - 1]); + j += 2; + } else { + rawString += def[j++]; + } + } + if (!rawString.isEmpty()) + append(Atom::RawString, rawString); + } +} + +Doc::SectioningUnit 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::Section4; + } else { + location().warning(tr("Invalid sectioning unit '%1'").arg(name)); + return Doc::Book; + } +} + +QString DocParser::getArgument(bool verbatim) +{ + QString arg; + int delimDepth = 0; + + skipSpacesOrOneEndl(); + + int startPos = pos; + + /* + 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. + */ + if (pos < (int) in.length() && in[pos] == '{') { + pos++; + while (pos < (int) in.length() && delimDepth >= 0) { + switch (in[pos].unicode()) { + case '{': + delimDepth++; + arg += "{"; + pos++; + break; + case '}': + delimDepth--; + if (delimDepth >= 0) + arg += "}"; + 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 '}'")); + } else { + 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); + marker = CodeMarker::markerForCode(code); + return marker->markedUpCode(code, 0, ""); +} + +/*! + Used only for generating doxygen output. + */ +QString DocParser::getUnmarkedCode(int cmd) +{ + QString code = getUntilEnd(cmd); +#if 0 + int indent = indentLevel(code); + if (indent < minIndent) + minIndent = indent; + code = unindent(minIndent, code); +#endif + 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] == '{'; +} + +void DocParser::skipSpacesOnLine() +{ + while ((pos < in.length()) && + in[pos].isSpace() && + (in[pos].unicode() != '\n')) + ++pos; +} + +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) + "|" + + cmdName(CMD_ELSE) + "|" + + 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; +#ifdef QDOC_QML + case CMD_QML: + return CMD_ENDQML; +#endif + 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; + 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("\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("/", "\\/"); + return "/" + result + "/"; +} + +#define COMMAND_BRIEF Doc::alias("brief") + +#ifdef QDOC_QML +#define COMMAND_QMLBRIEF Doc::alias("qmlbrief") +#endif + +#ifdef QDOC2DOX +#define DOXYGEN_INDENT 2 +#define DOXYGEN_TAB_SIZE 4 +#define DOXYGEN_INDENT_STRING " " +#define DOXYGEN_TAB_STRING " " + +static QRegExp ws_rx("\\s"); +static QRegExp not_ws_rx("\\S"); + +int DoxWriter::doxPass = 0; +QString DoxWriter::currentClass; +QSet<QString> DoxWriter::anchors; +QStringMap DoxWriter::exampleTitles; +QStringMap DoxWriter::headerFileTitles; +QStringMap DoxWriter::fileTitles; +QStringMap DoxWriter::groupTitles; +QStringMap DoxWriter::moduleTitles; +QStringMap DoxWriter::pageTitles; +QStringMap DoxWriter::externalPageTitles; +QStringMap DoxWriter::exampleTitlesInverse; +QStringMap DoxWriter::headerFileTitlesInverse; +QStringMap DoxWriter::fileTitlesInverse; +QStringMap DoxWriter::groupTitlesInverse; +QStringMap DoxWriter::moduleTitlesInverse; +QStringMap DoxWriter::pageTitlesInverse; +QStringMap DoxWriter::externalPageTitlesInverse; +QStringMultiMap DoxWriter::variables; +QStringMultiMap DoxWriter::properties; +QStringMultiMap DoxWriter::enums; +#endif + +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); +#ifdef QDOC2DOX + if (DoxWriter::isDoxPass()) { + DoxWriter doxWriter(source,priv); + if (DoxWriter::isDoxPass(1)) + doxWriter.pass1(); + else + doxWriter.pass2(); + } +#endif +} + +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() const +{ + return body().subText(Atom::BriefLeft, Atom::BriefRight); +} + +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 && (atom->type() == Atom::AutoLink || atom->type() == Atom::String)) { + briefStr += atom->string(); + atom = atom->next(); + } + + QStringList w = briefStr.split(" "); + 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() == "widget" + || w.first() == "namespace" || w.first() == "header")) + w.removeFirst(); + else { + location().warning( + tr("Nonstandard wording in '\\%1' text for '%2' (" + "expected 'class', '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(".")) + 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::SectioningUnit Doc::granularity() const +{ + if (priv == 0 || priv->extra == 0) { + return DocPrivateExtra().granularity; + } else { + return priv->extra->granularity; + } +} + +#if notyet // ### +Doc::SectioningUnit Doc::sectioningUnit() const +{ + if (priv == 0 || priv->extra == 0) { + return DocPrivateExtra().sectioningUnit; + } else { + return priv->extra->sectioningUnit; + } +} +#endif + +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; +} + +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 QStringMap &Doc::metaTagMap() const +{ + return priv && priv->extra ? priv->extra->metaMap : *null_QStringMap(); +} + +void Doc::initialize(const Config& config) +{ + DocParser::tabSize = config.getInt(CONFIG_TABSIZE); + DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES); + DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS); + DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES); + DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS); + DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION); + + 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(macro.defaultDef); + 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 example file '%1'").arg(fileName)); + } else { + QFile inFile(filePath); + if (!inFile.open(QFile::ReadOnly)) { + location.warning(tr("Cannot open example file '%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, dirPath)); + 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 slurping = 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; + slurping = false; + lastAlnum = result.size(); + } else if (!slurping) { + if (begun) + result += QLatin1Char('-'); + slurping = true; + } else { + // !alnum && slurping -> nothin + } + } + 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; +} + +#ifdef QDOC2DOX +/*! + Sets the doxygen writer pass to \a pass. You can use + isDoxPass(), with or without a parameter, to test if + you are in a doxygen writer run or in a specific pass + of a doxygen writer run. + + This function is only called from main() if either the + \e doxygen1 or \e doxygen2 flag is passed to qdoc3 on + the command line. + */ +void DoxWriter::setDoxPass(int pass) +{ + qDebug() << "SETTING doxygen pass to " << pass + << " in DoxWriter::setDoxPass()"; + doxPass = pass; +} + +/*! + Returns true if the doxygen pass is set to \a pass, + which means we are in the specified \a pass of a doxygen + writer run of qdoc3. + */ +bool DoxWriter::isDoxPass(int pass) { return (doxPass == pass); } + +/*! + Returns true if the doxygen pass is 1 or 2, which + means this is a doxygen writer run to transform qdoc + comments into doxygen comments. + */ +bool DoxWriter::isDoxPass() { return (doxPass > 0); } + +bool DoxWriter::conversionRequired() const +{ + /* + Loop through all the topic commands searching for + one that must be transformed to doxygen format. If + one is found, return true. + */ + QCommandMap::const_iterator i; + i = priv->metaCommandMap.constBegin(); + while (i != priv->metaCommandMap.constEnd()) { + QString s = i.key(); + if (s == "enum") + return true; + else if (s == "example") + return true; + else if (s == "externalpage") + return true; + else if (s == "group") + return true; + else if (s == "headerfile") + return true; + else if (s == "module") + return true; + else if (s == "page") + return true; + else if (s == "property") + return true; + else if (s == "typedef") + return true; + else if (s == "variable") + return true; + else if (s == "overload") + return true; + else if (s == "reimp") + return true; + else if (s == "relates") + return true; + else if (s == "macro") + return true; + else { +#if 0 + if (s == "class") + else if (s == "namespace") + else if (s == "service") + else if (s == "inheaderfile") + else if (s == "file") + else if (s == "fn") + else if (s == "contentspage") + else if (s == "nextpage") + else if (s == "previous") + else if (s == "indexpage") + else if (s == "startpage") +#endif + } + ++i; + } + + /* + Loop through all the qdoc atoms searching for one + that must be transformed to doxygen format. If one + is found, return true. + */ + const Atom* next = priv->text.firstAtom(); + while (next != 0) { + Atom::Type atomType = next->type(); + switch (atomType) { + case Atom::C: + case Atom::CaptionLeft: + case Atom::Code: + case Atom::CodeBad: + case Atom::CodeNew: + case Atom::CodeOld: + case Atom::CodeQuoteArgument: + case Atom::CodeQuoteCommand: + case Atom::FootnoteLeft: + case Atom::FormatElse: + case Atom::FormatEndif: + case Atom::FormatIf: + case Atom::GeneratedList: + case Atom::Image: + case Atom::ImageText: + case Atom::InlineImage: + case Atom::LegaleseLeft: + case Atom::LineBreak: + case Atom::Link: + case Atom::LinkNode: + case Atom::ListLeft: + case Atom::ListItemNumber: + case Atom::ListTagLeft: + case Atom::ListItemLeft: + case Atom::QuotationLeft: + case Atom::RawString: + case Atom::SectionLeft: + case Atom::SectionHeadingLeft: + case Atom::SidebarLeft: + case Atom::SnippetCommand: + case Atom::SnippetIdentifier: + case Atom::SnippetLocation: + case Atom::TableLeft: + case Atom::TableHeaderLeft: + case Atom::TableRowLeft: + case Atom::TableItemLeft: + case Atom::TableOfContents: + case Atom::Target: + return true; + case Atom::AbstractLeft: + case Atom::AbstractRight: + case Atom::AutoLink: + case Atom::BaseName: + case Atom::BriefLeft: + case Atom::BriefRight: + case Atom::CaptionRight: + case Atom::FormattingLeft: + case Atom::FormattingRight: + case Atom::Nop: + case Atom::ParaLeft: + case Atom::ParaRight: + case Atom::FootnoteRight: + case Atom::LegaleseRight: + case Atom::ListTagRight: + case Atom::ListItemRight: + case Atom::ListRight: + case Atom::QuotationRight: + case Atom::SectionRight: + case Atom::SectionHeadingRight: + case Atom::SidebarRight: + case Atom::String: + case Atom::TableRight: + case Atom::TableHeaderRight: + case Atom::TableRowRight: + case Atom::TableItemRight: + default: + break; + } + next = next->next(); + } + return false; +} + +/*! + A convenience function to write a qdoc metacommand as a + doxygen command, without conversion. i.e., some of the + qdoc metacommands don't require conversion for doxygen. + */ +void DoxWriter::writeCommand(QCommandMap::const_iterator cmd) +{ + concatenate("\\" + cmd.key() + " " + cmd.value()[0]); + newLine(); +} + +/*! + Convert the qdoc commands in the metacommand map to + doxygen format. This function is called only in pass2(). + The metacommand map contains all the metacommands that + were found in the qdoc comment that is being converted. + The metacommands are the ones that begin with the '\'. + These are not considered part of the text of the comment. + The text is converted by convertText(). + */ +void DoxWriter::convertMetaCommands() +{ + QCommandMap& metaCmdMap = priv->metaCommandMap; + QCommandMap::iterator cmd; + int c; + + currentPage.clear(); + currentFn.clear(); + currentTitle.clear(); + currentEnum.clear(); + currentProperty.clear(); + currentVariable.clear(); + currentClass.clear(); + currentExample.clear(); + currentGroup.clear(); + currentModule.clear(); + currentMacro.clear(); + currentService.clear(); + currentTypedef.clear(); + currentHeaderFile.clear(); + commentType = OtherComment; + + if ((cmd = metaCmdMap.find("class")) != metaCmdMap.end()) { + currentClass = cmd.value()[0]; + if ((c = currentClass.indexOf(' ')) > 0) + currentClass = currentClass.left(c); + writeCommand(cmd); + metaCmdMap.erase(cmd); + commentType = ClassComment; + } + else if ((cmd = metaCmdMap.find("fn")) != metaCmdMap.end()) { + currentFn = cmd.value()[0]; + writeCommand(cmd); + metaCmdMap.erase(cmd); + commentType = FnComment; + } + else if ((cmd = metaCmdMap.find("enum")) != metaCmdMap.end()) { + currentEnum = cmd.value()[0]; + if ((c = currentEnum.lastIndexOf("::")) > 0) { + currentClass = currentEnum.left(c); + currentEnum = currentEnum.right(currentEnum.size()-c-2); + qDebug() << "currentEnum =" << currentEnum; + qDebug() << "currentClass =" << currentClass; + } + writeCommand(cmd); + metaCmdMap.erase(cmd); + commentType = EnumComment; + } + else if ((cmd = metaCmdMap.find("property")) != metaCmdMap.end()) { + currentClass = cmd.value()[0]; + if ((c = currentClass.lastIndexOf("::")) > 0) { + currentProperty = currentClass.right(currentClass.size()-c-2); + currentClass = currentClass.left(c); + qDebug() << "currentProperty =" << currentProperty; + qDebug() << "currentClass =" << currentClass; + } + writeCommand(cmd); + metaCmdMap.erase(cmd); + commentType = PropertyComment; + } + else if ((cmd = metaCmdMap.find("variable")) != metaCmdMap.end()) { + currentClass = cmd.value()[0]; + if ((c = currentClass.lastIndexOf("::")) > 0) { + currentVariable = currentClass.right(currentClass.size()-c-2); + currentClass = currentClass.left(c); + qDebug() << "currentVariable =" << currentVariable; + qDebug() << "currentClass =" << currentClass; + } + concatenate("\\var " + cmd.value()[0]); + newLine(); + metaCmdMap.erase(cmd); + commentType = VariableComment; + } + + if ((cmd = metaCmdMap.find("page")) != metaCmdMap.end()) { + currentPage = cmd.value()[0]; + QString htmlFile = currentPage; + const QString* title = getPageTitle(htmlFile); + QStringList parts = htmlFile.split('.'); + metaCmdMap.erase(cmd); + if (title) { + concatenate("\\page " + parts[0] + " " + *title); + newLine(); + } + commentType = PageComment; + qDebug() << "currentPage =" << currentPage; + } + + if ((cmd = metaCmdMap.find("example")) != metaCmdMap.end()) { + currentExample = cmd.value()[0]; + metaCmdMap.erase(cmd); + commentType = ExampleComment; + qDebug() << "currentExample =" << currentExample; + } + + if ((cmd = metaCmdMap.find("macro")) != metaCmdMap.end()) { + currentMacro = cmd.value()[0]; + metaCmdMap.erase(cmd); + commentType = MacroComment; + qDebug() << "currentMacro =" << currentMacro; + } + + if ((cmd = metaCmdMap.find("group")) != metaCmdMap.end()) { + currentGroup = cmd.value()[0]; + metaCmdMap.erase(cmd); + commentType = GroupComment; + qDebug() << "currentGroup =" << currentGroup; + } + + if ((cmd = metaCmdMap.find("module")) != metaCmdMap.end()) { + currentModule = cmd.value()[0]; + metaCmdMap.erase(cmd); + commentType = ModuleComment; + qDebug() << "currentModule =" << currentModule; + } + + if ((cmd = metaCmdMap.find("headerfile")) != metaCmdMap.end()) { + currentHeaderFile = cmd.value()[0]; + metaCmdMap.erase(cmd); + commentType = HeaderFileComment; + qDebug() << "currentHeaderFile =" << currentHeaderFile; + } + + if ((cmd = metaCmdMap.find("typedef")) != metaCmdMap.end()) { + currentClass = cmd.value()[0]; + if ((c = currentClass.lastIndexOf("::")) > 0) { + currentTypedef = currentClass.right(currentClass.size()-c-2); + currentClass = currentClass.left(c); + } + metaCmdMap.erase(cmd); + commentType = TypedefComment; + qDebug() << "currentTypedef =" << currentTypedef; + qDebug() << "currentClass =" << currentClass; + } + + cmd = priv->metaCommandMap.begin(); + while (cmd != priv->metaCommandMap.end()) { + for (int i=0; i<cmd.value().size(); i++) { + concatenate("\\" + cmd.key() + " " + cmd.value()[i]); + newLine(); + } + //qDebug() << " " << cmd.key() << ": " << cmd.value(); + ++cmd; + } +} + +/*! + Convert the qdoc text to doxygen format. The metacommands + are converted by convertMetaCommands(). This function is + called in pass2(). + */ +void DoxWriter::convertText() +{ + const Atom* prev = 0; + const Atom* next = priv->text.firstAtom(); + while (next != 0) { + next->dump(); + Atom::Type atomType = next->type(); + switch (atomType) { + case Atom::AbstractLeft: + break; + case Atom::AbstractRight: + break; + case Atom::AutoLink: + concatenate(next->string()); + break; + case Atom::BaseName: + break; + case Atom::BriefLeft: + concatenate("\\brief "); + break; + case Atom::BriefRight: + newLine(); + break; + case Atom::C: + tt(next); + break; + case Atom::CaptionLeft: + unhandled(next); + break; + case Atom::CaptionRight: + unhandled(next); + break; + case Atom::Code: + code(next); + break; + case Atom::CodeBad: + code(next); + break; + case Atom::CodeNew: + newLine(); + concatenate("you can rewrite it as"); + code(next); + break; + case Atom::CodeOld: + newLine(); + concatenate("For example, if you have code like"); + code(next); + break; + case Atom::CodeQuoteArgument: + unhandled(next); + break; + case Atom::CodeQuoteCommand: + next = codeQuoteCommand(next); + break; + case Atom::FootnoteLeft: + break; + case Atom::FootnoteRight: + break; + case Atom::FormatElse: + formatElse(); + break; + case Atom::FormatEndif: + formatEndif(); + break; + case Atom::FormatIf: + formatIf(next); + break; + case Atom::FormattingLeft: + formattingLeft(next,next->next()); + break; + case Atom::FormattingRight: + formattingRight(next,prev); + break; + case Atom::GeneratedList: + break; + case Atom::Image: + break; + case Atom::ImageText: + break; + case Atom::InlineImage: + break; + case Atom::LegaleseLeft: + break; + case Atom::LegaleseRight: + break; + case Atom::LineBreak: + break; + case Atom::Link: + next = link(next); + break; + case Atom::LinkNode: + break; + case Atom::ListLeft: + { + bool nested = false; + if (structs.isEmpty()) { + const Atom* i = next->next(); + while (i->type() != Atom::ListRight) { + if ((i->type() == Atom::ListLeft) || + (i->type() == Atom::TableLeft)) { + nested = true; + break; + } + i = i->next(); + } + } + else + nested = true; + StructDesc d(BulletList,nested); + if (next->string() == "numeric") + d.structType = NumericList; + else if (next->string() == "value") { + d.structType = ValueList; + } + else if (next->string() != "bullet") + qDebug() << "UNKNOWN LIST TYPE" << next->string(); + structs.push(d); + if (nested || (d.structType != BulletList)) { + if (d.structType == BulletList) + concatenate("<ul>"); + else if (d.structType == NumericList) + concatenate("<ol>"); + else if (d.structType == ValueList) + concatenate("<dl>"); + newLine(); + } + } + break; + case Atom::ListItemNumber: + structs.top().count = next->string().toInt(); + break; + case Atom::ListTagLeft: + { + structs.top().count++; + concatenate("<dt>"); + const Atom* n = next->next(); + if (n->type() == Atom::String) { + qDebug() << "ENUM VALUE" << n->string(); + } + else + qDebug() << "NOT EN ENUM"; + } + break; + case Atom::ListTagRight: + concatenate("</dt>"); + break; + case Atom::ListItemLeft: + { + newLine(); + const StructDesc& d = structs.top(); + if (d.structType == BulletList) { + if (!d.nested) + concatenate("\\arg "); + else + concatenate("<li>"); + } + else if (d.structType == NumericList) + concatenate("<li>"); + else if (d.structType == ValueList) + concatenate("<dd>"); + } + break; + case Atom::ListItemRight: + { + const StructDesc& d = structs.top(); + if (d.structType == BulletList) { + if (d.nested) { + concatenate("</li>"); + newLine(); + } + } + else if (d.structType == NumericList) { + concatenate("</li>"); + newLine(); + } + else if (d.structType == ValueList) { + concatenate("</dd>"); + newLine(); + } + } + break; + case Atom::ListRight: + { + if (!structs.isEmpty()) { + const StructDesc& d = structs.top(); + if (d.nested || (d.structType != BulletList)) { + if (d.structType == BulletList) + concatenate("</ul>"); + else if (d.structType == NumericList) + concatenate("</ol>"); + else if (d.structType == ValueList) + concatenate("</dl>"); + newLine(); + } + structs.pop(); + } + } + break; + case Atom::Nop: + // nothing. + break; + case Atom::ParaLeft: + if (structs.isEmpty()) + newLine(); + break; + case Atom::ParaRight: + { + if (structs.isEmpty()) + newLine(); + else { + const StructDesc& d = structs.top(); + if (d.nested || (d.structType != BulletList)) { + Atom::Type t = next->next()->type(); + if ((t != Atom::ListItemRight) && + (t != Atom::TableItemRight)) + newLine(); + } + else + newLine(); + } + } + break; + case Atom::QuotationLeft: + break; + case Atom::QuotationRight: + break; + case Atom::RawString: + concatenate(next->string()); + break; + case Atom::SectionLeft: + // nothing. + break; + case Atom::SectionRight: + // nothing. + break; + case Atom::SectionHeadingLeft: + next = sectionHeading(next); + break; + case Atom::SectionHeadingRight: + newLine(); + break; + case Atom::SidebarLeft: + break; + case Atom::SidebarRight: + break; + case Atom::SnippetCommand: + newLine(); + concatenate("\\snippet "); + break; + case Atom::SnippetIdentifier: + newText += next->string(); + lineLength += next->string().size(); + newLine(); + break; + case Atom::SnippetLocation: + newText += next->string() + " "; + lineLength += next->string().size() + 1; + break; + case Atom::String: + wrap(next->string()); + break; + case Atom::TableLeft: + { + bool nested = false; + if (structs.isEmpty()) { + const Atom* i = next->next(); + while (i->type() != Atom::TableRight) { + if ((i->type() == Atom::ListLeft) || + (i->type() == Atom::TableLeft)) { + nested = true; + break; + } + i = i->next(); + } + } + else + nested = true; + StructDesc d(Table,nested); + structs.push(d); + if (next->string().isEmpty()) + concatenate("<table>"); + else { + QString attrs = "width=\"" + next->string() + "\""; + attrs += " align=\"center\""; + concatenate("<table " + attrs + ">"); + } + newLine(); + } + break; + case Atom::TableRight: + concatenate("</table>"); + if (!structs.isEmpty()) + structs.pop(); + newLine(); + break; + case Atom::TableHeaderLeft: + concatenate("<tr>"); + if (!structs.isEmpty()) + structs.top().inTableHeader = true; + newLine(); + break; + case Atom::TableHeaderRight: + concatenate("</tr>"); + if (!structs.isEmpty()) + structs.top().inTableHeader = false; + newLine(); + break; + case Atom::TableRowLeft: + if (!structs.isEmpty()) { + structs.top().inTableRow = true; + concatenate("<tr valign=\"top\" class=\""); + if (structs.top().odd) + concatenate("odd\">"); + else + concatenate("even\">"); + structs.top().odd = !structs.top().odd; + } + newLine(); + break; + case Atom::TableRowRight: + concatenate("</tr>"); + if (!structs.isEmpty()) + structs.top().inTableRow = false; + newLine(); + break; + case Atom::TableItemLeft: + if (!structs.isEmpty()) { + structs.top().inTableItem = true; + concatenate("<td>"); + if (structs.top().inTableHeader) + concatenate("<b> "); + } + break; + case Atom::TableItemRight: + if (!structs.isEmpty()) { + structs.top().inTableItem = false; + if (structs.top().inTableHeader) + concatenate(" </b>"); + concatenate("</td>"); + } + newLine(); + break; + case Atom::TableOfContents: + break; + case Atom::Target: + { + QString text = next->string(); + text.remove(ws_rx); + newLine(); + concatenate("\\anchor "); + newText += text; + lineLength += text.size(); + newLine(); + } + break; + case Atom::UnhandledFormat: + unhandled(next); + break; + case Atom::UnknownCommand: + unhandled(next); + break; + default: + //next->dump(); + break; + } + prev = next; + next = next->next(); + } +} + +/*! + + Pass one looks for topic commands and target and section + commands, and maybe other stuff. These are serialized to + text files, which are read back in by pass2(). + */ +void DoxWriter::pass1() +{ + QCommandMap& metaCmdMap = priv->metaCommandMap; + if (!metaCmdMap.isEmpty()) { + int c; + QCommandMap::iterator cmd; + if ((cmd = metaCmdMap.find("enum")) != metaCmdMap.end()) { + commentType = EnumComment; + currentEnum = cmd.value()[0]; + if ((c = currentEnum.lastIndexOf("::")) > 0) { + currentClass = currentEnum.left(c); + currentEnum = currentEnum.right(currentEnum.size()-c-2); + qDebug() << "currentEnum =" << currentEnum; + qDebug() << "currentClass =" << currentClass; + if (enums.contains(currentEnum,currentClass)) { + qWarning() << "DoxWriter::pass1():" + << "Duplicate enum:" + << currentClass << currentEnum; + } + else + enums.insert(currentEnum,currentClass); + } + } + else if ((cmd = metaCmdMap.find("property")) != metaCmdMap.end()) { + commentType = PropertyComment; + currentClass = cmd.value()[0]; + if ((c = currentClass.lastIndexOf("::")) > 0) { + currentProperty = currentClass.right(currentClass.size()-c-2); + currentClass = currentClass.left(c); + qDebug() << "currentProperty =" << currentProperty; + qDebug() << "currentClass =" << currentClass; + if (properties.contains(currentProperty,currentClass)) { + qWarning() << "DoxWriter::pass1():" + << "Duplicate property:" + << currentClass << currentProperty; + } + else + properties.insert(currentProperty,currentClass); + } + } + else if ((cmd = metaCmdMap.find("variable")) != metaCmdMap.end()) { + commentType = VariableComment; + currentClass = cmd.value()[0]; + if ((c = currentClass.lastIndexOf("::")) > 0) { + currentVariable = currentClass.right(currentClass.size()-c-2); + currentClass = currentClass.left(c); + qDebug() << "currentVariable =" << currentVariable; + qDebug() << "currentClass =" << currentClass; + if (variables.contains(currentVariable,currentClass)) { + qWarning() << "DoxWriter::pass1():" + << "Duplicate variable:" + << currentClass << currentVariable; + } + else + variables.insert(currentVariable,currentClass); + } + } + } + + /* + */ + const Atom* next = priv->text.firstAtom(); + while (next != 0) { + switch (next->type()) { + case Atom::SectionHeadingLeft: + { + QString text; + next = next->next(); + while (next) { + if (next->type() == Atom::SectionHeadingRight) + break; + else + text += next->string(); + next = next->next(); + } + //text.remove(ws_rx); + insertAnchor(text); + } + break; + case Atom::Target: + { + QString text = next->string(); + //text.remove(ws_rx); + insertAnchor(text); + } + default: + break; + } + next = next->next(); + } +} + +/*! + Output a parsed, tokenized qdoc comment as a doxygen + comment in diff format for input to the patch command. + */ +void DoxWriter::pass2() +{ + if (!conversionRequired()) { + qDebug() << "NO CONVERSION - FILE:" << priv->start_loc.fileName() + << "START:" << priv->start_loc.lineNo() + << "END:" << priv->end_loc.lineNo() - 1; + return; + } + + /* + Transformation to doxygen required... + */ + newText = "\n/*! \n"; + convertMetaCommands(); + convertText(); + if (newText[newText.size()-1] == ' ') + newText.remove(newText.size()-1,1); + newText += " */\n"; + qDebug() << "CONVERTED COMMENT - FILE:" << priv->start_loc.fileName() + << "START:" << priv->start_loc.lineNo() + << "END:" << priv->end_loc.lineNo() - 1; + qDebug() << newText; +} + +/*! + Unparse the second parameter of a "\l" command. + */ +const Atom* DoxWriter::link(const Atom* atom) +{ + QString first_text = atom->string(); + QString second_text; + const QString* value = 0; + + const Atom* next = atom->next(Atom::FormattingLeft,Atom::LINK_); + if (next) { + next->dump(); + while (1) { + next = next->next(); + next->dump(); + if (next->type() == Atom::FormattingRight) { + if (next->string() == Atom::LINK_) + break; + else { + // ignore it. + } + } + else + second_text += next->string(); + } + int i = first_text.indexOf('#'); + if (i >= 0) + first_text = first_text.right(first_text.size() - i - 1); + //newLine(); + if ((value = getExternalPage(first_text))) { + //qDebug() << "USED AN EXTERNAL PAGE TITLE" << first_text; + QString href = "<a href=\""+*value+"\">"+first_text+"</a>"; + concatenate(href); + } + else if (first_text.startsWith("http:",Qt::CaseInsensitive)) { + if (first_text == second_text) { + concatenate(first_text); + } + else { + QString href = "<a href=\""+first_text+"\">"+second_text+"</a>"; + concatenate(href); + } + } + else if ((value = getPageFile(first_text))) { + //qDebug() << "USED A PAGE TITLE" << first_text; + QStringList parts = (*value).split('.'); + QString ref = "\\ref " + parts[0] + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getGroup(first_text))) { + //qDebug() << "USED A GROUP TITLE" << first_text; + concatenate("\\ref " + *value + " \"" + second_text + "\""); + } + else if ((value = getModule(first_text))) { + //qDebug() << "USED A MODULE TITLE" << first_text; + concatenate("\\ref " + *value + " \"" + second_text + "\""); + } + else if ((value = getExamplePath(first_text))) { + //qDebug() << "USED AN EXAMPLE TITLE" << first_text; + first_text.remove(ws_rx); + QString ref = "\\ref " + first_text + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getFile(first_text))) { + //qDebug() << "USED A FILE TITLE" << first_text; + // I think this command is no longer available. + first_text.remove(ws_rx); + QString ref = "\\ref " + first_text + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getHeaderFile(first_text))) { + //qDebug() << "USED A HEADER FILE TITLE" << first_text; + first_text.remove(ws_rx); + QString ref = "\\ref " + first_text + " \"" + second_text + "\""; + concatenate(ref); + } + else if (isAnchor(first_text)) { + //qDebug() << "USED AN ANCHOR" << first_text; + first_text.remove(ws_rx); + QString ref = "\\ref " + first_text + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getPageTitle(first_text))) { + //qDebug() << "USED AN INVERSE PAGE TITLE" << first_text; + QStringList parts = first_text.split('.'); + QString ref = "\\ref " + parts[0] + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getExampleTitle(first_text))) { + //qDebug() << "USED AN INVERSE EXAMPLE TITLE" << first_text; + QString title = *value; + title.remove(ws_rx); + QString ref = "\\ref " + title + " \"" + second_text + "\""; + concatenate(ref); + } + else if ((value = getGroupTitle(first_text))) { + //qDebug() << "USED AN INVERSE GROUP TITLE" << first_text; + concatenate("\\ref " + first_text + " \"" + second_text + "\""); + } + else if ((value = getModuleTitle(first_text))) { + //qDebug() << "USED AN INVERSE MODULE TITLE" << first_text; + concatenate("\\ref " + first_text + " \"" + second_text + "\""); + } + else if ((value = getFileTitle(first_text))) { + qDebug() << "USED AN INVERSE FILE TITLE" << first_text; + } + else if ((value = getHeaderFileTitle(first_text))) { + qDebug() << "USED AN INVERSE HEADER FILE TITLE" << first_text; + } + else if ((first_text.indexOf("::") >= 0) || + (first_text.indexOf("()") >= 0) || + (first_text[0] == 'Q')) { + //qDebug() << "AUTO-LINKABLE" << first_text; + if (first_text == second_text) + concatenate(first_text); + else { + QString link = first_text + " " + second_text; + concatenate("\\link " + link + "\\endlink"); + } + } + else { + QString link; + QStringList propertyClasses; + QStringList variableClasses; + QStringList enumClasses; + bool p = isProperty(first_text,propertyClasses); + bool v = isVariable(first_text,variableClasses); + bool e = isEnum(first_text,enumClasses); + if (e) { + if (enumClasses.size() == 1) + link = enumClasses[0]; + else if (enumClasses.contains(currentClass)) + link = currentClass; + else { + QString msg = "Unqualified enum name: " + first_text; + QString details = "Classes: " + enumClasses.join(", "); + priv->start_loc.error(msg,details); + } + if (!link.isEmpty()) + qDebug() << "FOUND ENUM" << link << first_text; + } + else if (p && v) { + if (propertyClasses.size() == 1) { + if (variableClasses.size() == 1) { + if (propertyClasses[0] == variableClasses[0]) + link = propertyClasses[0]; + } + } + if (link.isEmpty()) { + if (propertyClasses.contains(currentClass) || + variableClasses.contains(currentClass)) + link = currentClass; + else { + propertyClasses += variableClasses; + QString msg = "Unqualified property or variable name: " + + first_text; + QString details = "Classes: " + + propertyClasses.join(", "); + priv->start_loc.error(msg,details); + } + } + } + else if (p) { + if (propertyClasses.size() == 1) + link = propertyClasses[0]; + else if (propertyClasses.contains(currentClass)) + link = currentClass; + else { + QString msg = "Unqualified property name: " + first_text; + QString details = "Classes: " + propertyClasses.join(", "); + priv->start_loc.error(msg,details); + } + } + else if (v) { + if (variableClasses.size() == 1) + link = variableClasses[0]; + else if (variableClasses.contains(currentClass)) + link = currentClass; + else { + QString msg = "Unqualified variable name: " + first_text; + QString details = "Classes: " + variableClasses.join(", "); + priv->start_loc.error(msg,details); + } + } + else { + qDebug() << "NOT AUTO-LINKABLE" << first_text; + QString s = first_text + " " + second_text; + concatenate("\\link " + s + "\\endlink"); + } + if (!link.isEmpty()) { + link += "::" + first_text + " " + second_text; + concatenate("\\link " + link + "\\endlink"); + } + } + } + else + qDebug() << "LINK with no second parameter!!!!"; + return next? next : atom; +} + +/*! + If the current line length is 0, the current line is + indented according to the context. + */ +void DoxWriter::indentLine() +{ + if (lineLength == 0) { + newText += DOXYGEN_INDENT_STRING; + lineLength = DOXYGEN_INDENT; + if (!structs.isEmpty()) { + for (int i=1; i<structs.size(); ++i) { + newText += DOXYGEN_TAB_STRING; + lineLength += DOXYGEN_TAB_SIZE; + } + } + } +} + +/*! + Concatenates a newline to the doxygen text, increments the + line count, and resets the line length to 0. + */ +void DoxWriter::newLine() +{ + newText += "\n"; + ++lineCount; + lineLength = 0; +} + +static const int maxLineLength = 70; + +/*! + Concatenate the \a text to the doxygen comment currently + under construction and increment the current line length + by the size of the \a text. + + If incrementing the current line length by the \a text size + would make the current line length longer than the maximum + line length, then call newLine() and indentLine() \e before + concatenating the \a text. + */ +void DoxWriter::concatenate(QString text) +{ + if ((lineLength + text.size()) > maxLineLength) + newLine(); + indentLine(); + newText += text; + lineLength += text.size(); +} + +static bool punctuation(QChar c) +{ + switch (c.toAscii()) { + case '.': + case ',': + case ':': + case ';': + case '/': + case '+': + case '-': + case '?': + case '!': + case '\"': + return true; + default: + break; + } + return false; +} + +/*! + Concatenate the \a text string to the doxygen text, doing + line wrapping where necessary. + */ +void DoxWriter::wrap(QString text) +{ + int from = 0; + int to = -1; + + if ((lineLength == 0) || (lineLength >= maxLineLength)) { + if (!text.isEmpty() && (text[0] == ' ')) + text = text.right(text.size() - 1); + } + + indentLine(); + while (text.size()) { + int avail = maxLineLength - lineLength; + from = text.indexOf(' ',from); + if (from >= 0) { + if (from < avail) + to = from++; + else if (from == 1 && punctuation(text[0])) + to = from++; + else { + if (to >= 0) { + newText += text.left(to+1); + lineLength += to + 1; + text = text.right(text.size() - to - 1); + } + else { + newLine(); + indentLine(); + newText += text.left(from+1); + lineLength += from + 1; + text = text.right(text.size() - from - 1); + } + from = 0; + to = -1; + if (text.size() && (lineLength > maxLineLength)) { + newLine(); + indentLine(); + } + } + } + else + break; + } + if (text.size()) { + if (lineLength >= maxLineLength) { + newLine(); + indentLine(); + } + newText += text; + lineLength += text.size(); + } +} + +/*! + This will output something, but it depends on what the + \a atom string and the \a next atom string are. + */ +void DoxWriter::formattingLeft(const Atom* atom, const Atom* next) +{ + if (atom->string() == "parameter") { + concatenate("\\a "); + return; + } + else if (atom->string() == "underline") { + concatenate("<u>"); + return; + } + else if (atom->string() == "superscript") { + concatenate("<sup>"); + return; + } + else if (atom->string() == "subscript") { + concatenate("<sub>"); + return; + } + int ws = -1; + if (next) + ws = next->string().indexOf(ws_rx); + if (atom->string() == "bold") { + if (ws < 0) + concatenate("\\b "); + else + concatenate("<b>"); + } + else if (atom->string() == "italic") { + if (ws < 0) + concatenate("\\e "); + else + concatenate("<i>"); + } + else if (atom->string() == "teletype") { + if (ws < 0) + concatenate("\\c "); + else + concatenate("<tt>"); + } + else + qDebug() << "UNHANDLED FormattingLeft: " << atom->string(); +} + +/*! + This will output something, but it depends on what the + \a atom string and the \a prev atom string are. + */ +void DoxWriter::formattingRight(const Atom* atom, const Atom* prev) +{ + if (atom->string() == "parameter") + return; + else if (atom->string() == "underline") { + concatenate("</u>"); + return; + } + else if (atom->string() == "superscript") { + concatenate("</sup>"); + return; + } + else if (atom->string() == "subscript") { + concatenate("</sub>"); + return; + } + int ws = -1; + if (prev) + ws = prev->string().indexOf(ws_rx); + if (ws < 0) + return; + if (atom->string() == "bold") + concatenate("</b>"); + else if (atom->string() == "italic") + concatenate("</i>"); + else if (atom->string() == "teletype") + concatenate("</tt>"); + else + qDebug() << "UNHANDLED FormattingRight: " << atom->string(); +} + +/*! + Output a \c or a <tt>...</tt>. + */ +void DoxWriter::tt(const Atom* atom) +{ + if (atom->string().indexOf(ws_rx) < 0) { + concatenate("\\c "); + concatenate(atom->string()); + } + else { + concatenate("<tt>"); + concatenate(atom->string()); + concatenate("</tt>"); + } +} + +/*! + */ +void DoxWriter::formatIf(const Atom* atom) +{ + if (atom->string() == "HTML") { + newLine(); + concatenate("\\htmlonly"); + newLine(); + } +} + +/*! + */ +void DoxWriter::formatEndif() +{ + newLine(); + concatenate("\\endhtmlonly"); + newLine(); +} + +/*! + */ +void DoxWriter::formatElse() +{ + // nothing. +} + +/*! + Pass 1: Construct a section identifier and insert it into + the anchor set. + + Pass 2: Convert section1, section2, and section3 commands + to section, subsection, and subsubsection respectively. + Warn if a section command higher than 3 is seen. + */ +const Atom* DoxWriter::sectionHeading(const Atom* atom) +{ + QString heading_level = atom->string(); + QString heading_text; + const Atom* next = atom->next(); + while (next) { + next->dump(); + if (next->type() == Atom::SectionHeadingRight) { + if (next->string() == heading_level) + break; + else { + qDebug() << "WRONG SectionHeading number!!!!"; + } + } + else + heading_text += next->string(); + next = next->next(); + } + + QString heading_identifier = heading_text; + heading_identifier.remove(ws_rx); + + newLine(); + if (heading_level == "1") + heading_level = "\\section "; + else if (heading_level == "2") + heading_level = "\\subsection "; + else if (heading_level == "3") + heading_level = "\\subsubsection "; + else if (heading_level == "4") { + heading_level = "\\subsubsection "; + qDebug() << "WARNING section4 converted to \\subsubsection"; + } + else { + heading_level = "\\subsubsection "; + qDebug() << "WARNING section5 converted to \\subsubsection"; + } + concatenate(heading_level); + newText += heading_identifier + " "; + lineLength += heading_identifier.size() + 1; + newText += heading_text; + lineLength += heading_text.size(); + newLine(); + return next? next : atom; +} + +/*! + Report an unhandled atom. + */ +void DoxWriter::unhandled(const Atom* atom) +{ + qDebug() << "UNHANDLED ATOM"; + atom->dump(); +} + +/*! + Output a code/endcode block. + */ +void DoxWriter::code(const Atom* atom) +{ + newLine(); + concatenate("\\code"); + writeCode(atom->string()); + concatenate("\\endcode"); + newLine(); +} + +/*! + Output a code/endcode block depending on the + CodeQuote Command and CodeQuoteArgument parameters. + */ +const Atom* DoxWriter::codeQuoteCommand(const Atom* atom) +{ + QString command = atom->string(); + atom = atom->next(); + concatenate("\\code"); + if (command == "codeline") { + newLine(); + concatenate(atom->string()); + newLine(); + } + else if (command == "dots") { + newLine(); + concatenate(atom->string()); + newLine(); + } + else { + writeCode(atom->string()); + } + concatenate("\\endcode"); + return atom; +} + +/*! + Appends a block of code to the comment. + */ +void DoxWriter::writeCode(QString text) +{ + int cr_count = text.count('\n') - 1; + if (cr_count >= 0) { + int last_cr = text.lastIndexOf('\n'); + newText += text.left(last_cr); + lineCount += cr_count; + } + else + newText += text; + newLine(); +} + +/*! + Inserts \a text into the anchor set. This function is called + during doxygen pass 1. + */ +void DoxWriter::insertAnchor(const QString& text) +{ + anchors.insert(text); +} + +/*! + Returns true if \a text identifies an anchor, section, + subsection, subsubsection, or page. + */ +bool DoxWriter::isAnchor(const QString& text) +{ + return anchors.contains(text); +} + +/*! + Write the set of anchors to a file, one per line. + */ +void DoxWriter::writeAnchors() +{ + QFile file("anchors.txt"); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning("Unable to open anchors.txt for writing."); + return; + } + + QTextStream out(&file); + QSet<QString>::const_iterator i = anchors.constBegin(); + while (i != anchors.constEnd()) { + out << *i << "\n"; + ++i; + } + file.close(); +} + +/*! + Read the set of anchors from the anchors file, one per line, + and insert each one into the anchor set. + */ +void DoxWriter::readAnchors() +{ + QFile file("anchors.txt"); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Unable to open anchors.txt for reading."); + return; + } + + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + anchors.insert(line); + } + file.close(); +#if 0 + QSet<QString>::const_iterator i = anchors.constBegin(); + while (i != anchors.constEnd()) { + qDebug() << *i; + ++i; + } +#endif +} + +/*! + Inserts \a title into one of the title maps. \a title is + mapped to the \a node name. This function is called during + doxygen pass 1. + */ +void DoxWriter::insertTitle(FakeNode* node, const QString& title) +{ + switch (node->subType()) { + case FakeNode::Example: + if (exampleTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate example title:" + << title; + } + else { + exampleTitles[title] = node->name(); + exampleTitlesInverse[node->name()] = title; + } + break; + case FakeNode::HeaderFile: + if (headerFileTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate header file title:" + << title; + } + else { + headerFileTitles[title] = node->name(); + headerFileTitlesInverse[node->name()] = title; + } + break; + case FakeNode::File: + if (fileTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate file title:" + << title; + } + else { + fileTitles[title] = node->name(); + fileTitlesInverse[node->name()] = title; + } + break; + case FakeNode::Group: + if (groupTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate group title:" + << title; + } + else { + groupTitles[title] = node->name(); + groupTitlesInverse[node->name()] = title; + } + break; + case FakeNode::Module: + if (moduleTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate module title:" + << title; + } + else { + moduleTitles[title] = node->name(); + moduleTitlesInverse[node->name()] = title; + } + break; + case FakeNode::Page: + if (pageTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate page title:" + << title; + } + else { + pageTitles[title] = node->name(); + pageTitlesInverse[node->name()] = title; + } + break; + case FakeNode::ExternalPage: + if (externalPageTitles.contains(title)) { + qWarning() << "DoxWriter::insertTitle():" + << "Duplicate external page title:" + << title; + } + else { + externalPageTitles[title] = node->name(); + externalPageTitlesInverse[node->name()] = title; + } + break; + default: + break; + } +} + +/*! + */ +const QString* DoxWriter::getPageFile(const QString& title) +{ + QStringMapEntry entry = pageTitles.find(title); + return (entry == pageTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getExamplePath(const QString& title) +{ + QStringMapEntry entry = exampleTitles.find(title); + return (entry == exampleTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getFile(const QString& title) +{ + QStringMapEntry entry = fileTitles.find(title); + return (entry == fileTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getHeaderFile(const QString& title) +{ + QStringMapEntry entry = headerFileTitles.find(title); + return (entry == headerFileTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getGroup(const QString& title) +{ + QStringMapEntry entry = groupTitles.find(title); + return (entry == groupTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getModule(const QString& title) +{ + QStringMapEntry entry = moduleTitles.find(title); + return (entry == moduleTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getExternalPage(const QString& title) +{ + QStringMapEntry entry = externalPageTitles.find(title); + return (entry == externalPageTitles.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getPageTitle(const QString& text) +{ + QStringMapEntry entry = pageTitlesInverse.find(text); + return (entry == pageTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getExampleTitle(const QString& text) +{ + QStringMapEntry entry = exampleTitlesInverse.find(text); + return (entry == exampleTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getFileTitle(const QString& text) +{ + QStringMapEntry entry = fileTitlesInverse.find(text); + return (entry == fileTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getHeaderFileTitle(const QString& text) +{ + QStringMapEntry entry = headerFileTitlesInverse.find(text); + return (entry == headerFileTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getGroupTitle(const QString& text) +{ + QStringMapEntry entry = groupTitlesInverse.find(text); + return (entry == groupTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getModuleTitle(const QString& text) +{ + QStringMapEntry entry = moduleTitlesInverse.find(text); + return (entry == moduleTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + */ +const QString* DoxWriter::getExternalPageTitle(const QString& text) +{ + QStringMapEntry entry = externalPageTitlesInverse.find(text); + return (entry == externalPageTitlesInverse.end()) ? 0 : &entry.value(); +} + +/*! + Serialize \a map to file \a name. + */ +void DoxWriter::writeMap(const QStringMap& map, const QString& name) +{ + + QFile file(name); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "Unable to open" << name << "for writing."; + return; + } + + QTextStream out(&file); + QStringMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + out << i.key() << "\n"; + out << i.value() << "\n"; + ++i; + } + file.close(); +} + +/*! + Read file \a name into the \a map. + */ +void DoxWriter::readMap(QStringMap& map, QStringMap& inverseMap, const QString& name) +{ + QFile file(name); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Unable to open" << name << "for reading."; + return; + } + + QTextStream in(&file); + while (!in.atEnd()) { + QString title = in.readLine(); + QString value = in.readLine(); + map[title] = value; + inverseMap[value] = title; + } + file.close(); +} + +/*! + Write the sets of titles to text files, one per line. + */ +void DoxWriter::writeTitles() +{ + if (!pageTitles.isEmpty()) + writeMap(pageTitles,"pagetitles.txt"); + if (!fileTitles.isEmpty()) + writeMap(fileTitles,"filetitles.txt"); + if (!headerFileTitles.isEmpty()) + writeMap(headerFileTitles,"headerfiletitles.txt"); + if (!exampleTitles.isEmpty()) + writeMap(exampleTitles,"exampletitles.txt"); + if (!moduleTitles.isEmpty()) + writeMap(moduleTitles,"moduletitles.txt"); + if (!groupTitles.isEmpty()) + writeMap(groupTitles,"grouptitles.txt"); + if (!externalPageTitles.isEmpty()) + writeMap(externalPageTitles,"externalpagetitles.txt"); +} + +/*! + Read the sets of titles from the titles files, one per line, + and insert each one into the appropriate title set. + */ +void DoxWriter::readTitles() +{ + readMap(pageTitles,pageTitlesInverse,"pagetitles.txt"); + readMap(fileTitles,fileTitlesInverse,"filetitles.txt"); + readMap(headerFileTitles,headerFileTitlesInverse,"headerfiletitles.txt"); + readMap(exampleTitles,exampleTitlesInverse,"exampletitles.txt"); + readMap(moduleTitles,moduleTitlesInverse,"moduletitles.txt"); + readMap(groupTitles,groupTitlesInverse,"grouptitles.txt"); + readMap(externalPageTitles, + externalPageTitlesInverse, + "externalpagetitles.txt"); +} + +/*! + Serialize \a map to file \a name. + */ +void DoxWriter::writeMultiMap(const QStringMultiMap& map, const QString& name) +{ + + QFile file(name); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qWarning() << "Unable to open" << name << "for writing."; + return; + } + + QTextStream out(&file); + QStringMultiMap::const_iterator i = map.constBegin(); + while (i != map.constEnd()) { + out << i.key() << "\n"; + out << i.value() << "\n"; + ++i; + } + file.close(); +} + +/*! + Write the4 property names and variable names to text files. + */ +void DoxWriter::writeMembers() +{ + if (!variables.isEmpty()) + writeMultiMap(variables,"variables.txt"); + if (!properties.isEmpty()) + writeMultiMap(properties,"properties.txt"); + if (!enums.isEmpty()) + writeMultiMap(enums,"enums.txt"); +} + +/*! + Read file \a name into the \a map. + */ +void DoxWriter::readMultiMap(QStringMultiMap& map, const QString& name) +{ + QFile file(name); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Unable to open" << name << "for reading."; + return; + } + + QTextStream in(&file); + while (!in.atEnd()) { + QString member = in.readLine(); + QString className = in.readLine(); + map.insert(member,className); + } + file.close(); +} + +/*! + Read the property names and variable names from the test files. + */ +void DoxWriter::readMembers() +{ + readMultiMap(variables,"variables.txt"); + readMultiMap(properties,"properties.txt"); + readMultiMap(enums,"enums.txt"); +} + +/*! + Return true if \a name is a property. Loads \a classes with + the names of all the classes in which \a name is a property. + */ +bool DoxWriter::isProperty(const QString& name, QStringList& classes) +{ + classes = properties.values(name); + return !classes.isEmpty(); +} + +/*! + Return true if \a name is a variable. Loads \a classes with + the names of all the classes in which \a name is a variable. + */ +bool DoxWriter::isVariable(const QString& name, QStringList& classes) +{ + classes = variables.values(name); + return !classes.isEmpty(); +} + +/*! + Return true if \a name is an enum type. Loads \a classes with + the names of all the classes in which \a name is an enum type. + */ +bool DoxWriter::isEnum(const QString& name, QStringList& classes) +{ + classes = enums.values(name); + return !classes.isEmpty(); +} +#endif + +QT_END_NAMESPACE diff --git a/tools/qdoc3/doc.h b/tools/qdoc3/doc.h new file mode 100644 index 0000000..6cb6f0a --- /dev/null +++ b/tools/qdoc3/doc.h @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + doc.h +*/ + +#ifndef DOC_H +#define DOC_H + +#include <QSet> +#include <QString> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Atom; +class CodeMarker; +class Config; +class DocPrivate; +class Quoter; +class Text; +class FakeNode; + +typedef QMap<QString, QStringList> QCommandMap; +typedef QMap<QString, QString> QStringMap; +typedef QStringMap::const_iterator QStringMapEntry; +typedef QMultiMap<QString, QString> QStringMultiMap; + +class Doc +{ + public: + // the order is important + enum SectioningUnit { + Book = -2, + Part, + Chapter, + Section1, + Section2, + Section3, + Section4 + }; + + Doc() : priv(0) {} + Doc(const Location &start_loc, + const Location &end_loc, + const QString &source, + const QSet<QString> &metaCommandSet); + 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 Location &location() const; + bool isEmpty() const; + const QString& source() const; + const Text& body() const; + Text briefText() const; + Text trimmedBriefText(const QString &className) const; + Text legaleseText() const; + const QString& baseName() const; + SectioningUnit granularity() const; + const QSet<QString> ¶meterNames() const; + const QStringList &enumItemNames() const; + const QStringList &omitEnumItemNames() const; + const QSet<QString> &metaCommandsUsed() 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 QStringMap &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; +}; + +#ifdef QDOC2DOX + +class DoxWriter +{ + public: + DoxWriter(const QString& source, DocPrivate* docPrivate) + : commentType(OtherComment), + lineLength(0), + lineCount(0), + priv(docPrivate), + oldText(source) {} + ~DoxWriter() {} + + void pass1(); + void pass2(); + + static void setDoxPass(int pass); + static bool isDoxPass(int pass); + static bool isDoxPass(); + static void insertTitle(FakeNode* node, const QString& title); + static void writeTitles(); + static void readTitles(); + static void writeMembers(); + static void readMembers(); + static void writeAnchors(); + static void readAnchors(); + + private: + void indentLine(); + void newLine(); + void concatenate(QString text); + void wrap(QString text); + bool conversionRequired() const; + void convertMetaCommands(); + void convertText(); + const Atom* link(const Atom* atom); + void formattingLeft(const Atom* atom, const Atom* next); + void formattingRight(const Atom* atom, const Atom* prev); + void tt(const Atom* atom); + void formatIf(const Atom* atom); + void formatEndif(); + void formatElse(); + const Atom* sectionHeading(const Atom* atom); + void unhandled(const Atom* atom); + void code(const Atom* atom); + const Atom* codeQuoteCommand(const Atom* atom); + void writeCode(QString text); + void writeCommand(QCommandMap::const_iterator cmd); + + static void insertAnchor(const QString& text); + static bool isAnchor(const QString& text); + + static const QString* getPageFile(const QString& title); + static const QString* getFile(const QString& title); + static const QString* getExamplePath(const QString& title); + static const QString* getHeaderFile(const QString& title); + static const QString* getGroup(const QString& title); + static const QString* getModule(const QString& title); + static const QString* getExternalPage(const QString& title); + static const QString* getPageTitle(const QString& text); + static const QString* getFileTitle(const QString& text); + static const QString* getExampleTitle(const QString& text); + static const QString* getHeaderFileTitle(const QString& text); + static const QString* getGroupTitle(const QString& text); + static const QString* getModuleTitle(const QString& text); + static const QString* getExternalPageTitle(const QString& text); + + static bool isProperty(const QString& title, QStringList& classes); + static bool isVariable(const QString& title, QStringList& classes); + static bool isEnum(const QString& title, QStringList& classes); + + private: + static void writeMap(const QStringMap& map, const QString& name); + static void readMap(QStringMap& map, + QStringMap& inverseMap, + const QString& name); + static void writeMultiMap(const QStringMultiMap& map, const QString& name); + static void readMultiMap(QStringMultiMap& map, const QString& name); + + public: // VS 6, SunCC need this to be public + enum StructType { BulletList, NumericList, ValueList, Table }; + private: + struct StructDesc { + StructType structType; + int count; + bool nested; + bool inTableHeader; + bool inTableRow; + bool inTableItem; + bool odd; + + StructDesc() + : structType(BulletList), + count(0), + nested(false), + inTableHeader(false), + inTableRow(false), + inTableItem(false), + odd(true) { } + + StructDesc(StructType t, bool n) + : structType(t), + count(0), + nested(n), + inTableHeader(false), + inTableRow(false), + inTableItem(false), + odd(true) { } + }; + + typedef QStack<StructDesc> StructStack; + + enum CommentType { + ClassComment, + EnumComment, + ExampleComment, + FnComment, + GroupComment, + HeaderFileComment, + MacroComment, + ModuleComment, + PageComment, + PropertyComment, + ServiceComment, + TypedefComment, + VariableComment, + OtherComment + }; + + private: + CommentType commentType; + int lineLength; + int lineCount; + DocPrivate* priv; + QString oldText; + QString newText; + StructStack structs; + + QString currentPage; + QString currentFn; + QString currentTitle; + QString currentEnum; + QString currentProperty; + QString currentVariable; + QString currentExample; + QString currentGroup; + QString currentModule; + QString currentMacro; + QString currentService; + QString currentTypedef; + QString currentHeaderFile; + static QString currentClass; + + static int doxPass; + static QSet<QString> anchors; + static QStringMap exampleTitles; + static QStringMap headerFileTitles; + static QStringMap fileTitles; + static QStringMap groupTitles; + static QStringMap moduleTitles; + static QStringMap pageTitles; + static QStringMap externalPageTitles; + static QStringMap exampleTitlesInverse; + static QStringMap headerFileTitlesInverse; + static QStringMap fileTitlesInverse; + static QStringMap groupTitlesInverse; + static QStringMap moduleTitlesInverse; + static QStringMap pageTitlesInverse; + static QStringMap externalPageTitlesInverse; + + static QStringMultiMap variables; + static QStringMultiMap properties; + static QStringMultiMap enums; +}; + +#endif + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/documentation.pri b/tools/qdoc3/documentation.pri new file mode 100644 index 0000000..d952183 --- /dev/null +++ b/tools/qdoc3/documentation.pri @@ -0,0 +1,5 @@ +# NOTE: THIS FILE IS SHARED BY qdoc3.pro AND projects.pro +# +# So while changing this file, please make sure to that your changes +# work in root qt soure dir with (n)make docs and for building with qdoc +# diff --git a/tools/qdoc3/editdistance.cpp b/tools/qdoc3/editdistance.cpp new file mode 100644 index 0000000..11cc18f --- /dev/null +++ b/tools/qdoc3/editdistance.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 ) +{ + 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/tools/qdoc3/editdistance.h b/tools/qdoc3/editdistance.h new file mode 100644 index 0000000..d69f6b9 --- /dev/null +++ b/tools/qdoc3/editdistance.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/tools/qdoc3/generator.cpp b/tools/qdoc3/generator.cpp new file mode 100644 index 0000000..8a90636 --- /dev/null +++ b/tools/qdoc3/generator.cpp @@ -0,0 +1,931 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + generator.cpp +*/ + +#include <qdir.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" + +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; +QString Generator::outDir; +QString Generator::project; + +static Text stockLink(const QString &target) +{ + return Text() << Atom(Atom::Link, target) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << target << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK); +} + +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.getStringSet(CONFIG_OUTPUTFORMATS); + if ( !outputFormats.isEmpty() ) { + outDir = config.getString(CONFIG_OUTPUTDIR); + if ( outDir.isEmpty() ) + config.lastLocation().fatal(tr("No output directory specified in configuration file")); + + 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")); + } + + imageFiles = config.getStringList(CONFIG_IMAGES); + imageDirs = config.getStringList(CONFIG_IMAGEDIRS); + + 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.getStringList(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; + } + } + ++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); +} + +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 = ""; +} + +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 */ ) +{ +} + +void Generator::generateText( const Text& text, const Node *relative, + CodeMarker *marker ) +{ + if ( text.firstAtom() != 0 ) { + int numAtoms = 0; + startText( relative, marker ); + generateAtomList( text.firstAtom(), relative, marker, true, numAtoms ); + endText( relative, marker ); + } +} + +void Generator::generateBody( const Node *node, CodeMarker *marker ) +{ + bool quiet = false; + + if ( node->type() == Node::Function ) { +#if 0 + const FunctionNode *func = (const FunctionNode *) node; + if ( func->isOverload() && func->metaness() != FunctionNode::Ctor ) + generateOverload( node, marker ); +#endif + } else if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + if (fake->subType() == FakeNode::Example) + generateExampleFiles(fake, marker); + else if (fake->subType() == FakeNode::File) + quiet = true; + } + + if (node->doc().isEmpty()) { + if (!quiet) // ### might be unnecessary + node->location().warning(tr("No documentation for '%1'") + .arg(marker->plainFullName(node))); + } else { + generateText(node->doc().body(), node, marker); + + 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) + 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 ( func->reimplementedFrom() != 0 ) + generateReimplementedFrom( func, marker ); + } + } + + if (node->type() == Node::Fake) { + const FakeNode *fake = static_cast<const FakeNode *>(node); + if (fake->subType() == FakeNode::File) { + Text text; + Quoter quoter; + Doc::quoteFromFile(fake->doc().location(), quoter, fake->name()); + QString code = quoter.quoteTo(fake->location(), "", ""); + text << Atom(Atom::Code, code); + generateText(text, fake, marker); + } + } +} + +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 << "See also "; + + for (int i = 0; i < alsoList.size(); ++i) + text << alsoList.at(i) << separator(i, alsoList.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 << "Inherits "; + + 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 ); + } +} + +void Generator::generateInheritedBy( const ClassNode *classe, + CodeMarker *marker ) +{ + if ( !classe->derivedClasses().isEmpty() ) { + Text text; + text << Atom::ParaLeft << "Inherited by "; + + appendSortedNames(text, classe, classe->derivedClasses(), marker); + text << Atom::ParaRight; + generateText( text, classe, marker ); + } +} + +void Generator::generateExampleFiles(const FakeNode *fake, CodeMarker *marker) +{ + if (fake->childNodes().isEmpty()) + return; + + OpenedList openedList(OpenedList::Bullet); + + Text text; + text << Atom::ParaLeft << "Files:" << Atom::ParaRight + << Atom(Atom::ListLeft, openedList.styleString()); + foreach (const Node *child, fake->childNodes()) { + QString exampleFile = child->name(); + openedList.next(); + text << Atom(Atom::ListItemNumber, openedList.numberString()) + << Atom(Atom::ListItemLeft, openedList.styleString()) << Atom::ParaLeft + << Atom(Atom::Link, exampleFile) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << exampleFile + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << Atom::ParaRight << Atom(Atom::ListItemRight, openedList.styleString()); + } + text << Atom(Atom::ListRight, openedList.styleString()); + generateText(text, fake, marker); +} + +void Generator::generateModuleWarning(const ClassNode *classe, CodeMarker *marker) +{ + QString module = classe->moduleName(); + if (!module.isEmpty()) { + Text text; + if (Tokenizer::isTrue("defined(consoleedition)") + && !editionModuleMap["Console"].contains(module)) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This class is not part of the Qt Console Edition." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom::ParaRight; + } else if (Tokenizer::isTrue("defined(desktoplightedition)") + && !editionModuleMap["DesktopLight"].contains(module)) { + text << Atom::ParaLeft + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "This class is not part of the Qt Desktop Light Edition." + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom::ParaRight; + } else if (module == "Qt3Support" && Tokenizer::isTrue("defined(opensourceedition)")) { + text << Atom::ParaLeft << Atom( Atom::FormattingLeft, ATOM_FORMATTING_BOLD ) + << "Note to Qt Desktop Light Edition users:" + << Atom( Atom::FormattingRight, ATOM_FORMATTING_BOLD ) + << " This class is only available in the " + << Atom(Atom::AutoLink, "Qt Desktop Edition") + << "." << Atom::ParaRight; + } + + generateText(text, classe, marker); + } +} + +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('<') ) { + while ( i < (int) markedCode.length() ) { + t += markedCode.at(i++); + if ( markedCode.at(i - 1) == QLatin1Char('>') ) + break; + } + } else { + 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: + default: + return "documentation"; + case Node::Enum: + return "enum"; + case Node::Typedef: + return "typedef"; + case Node::Function: + return "function"; + case Node::Property: + return "property"; + } +} + +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(); + + return QLatin1String("images/") + + Config::copyFile(relative->doc().location(), + filePath, userFriendlyFilePath, + outputDir() + QLatin1String("/images")); +} + +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()]; +} + +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 parent = node->parent()->inheritedThreadSafeness(); + + switch (node->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 " << stockLink("reentrant") << "." << Atom::ParaRight; + break; + case Node::Reentrant: + case Node::ThreadSafe: + text << Atom::ParaLeft << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + if (parent == Node::ThreadSafe) { + text << "Warning:"; + } else { + text << "Note:"; + } + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) << " "; + + if (node->threadSafeness() == Node::ThreadSafe) + theStockLink = stockLink("thread-safe"); + else + theStockLink = stockLink("reentrant"); + + if (node->isInnerNode()) { + const InnerNode *innerNode = static_cast<const InnerNode *>(node); + text << "All the functions in this " << typeString(node) << " are " + << theStockLink; + + NodeList except; + NodeList::ConstIterator c = innerNode->childNodes().begin(); + while (c != innerNode->childNodes().end()) { + if ((*c)->threadSafeness() != Node::UnspecifiedSafeness) + except.append(*c); + ++c; + } + if (except.isEmpty()) { + text << "."; + } else { + text << ", except "; + + NodeList::ConstIterator e = except.begin(); + int index = 0; + while (e != except.end()) { + appendFullName(text, *e, innerNode, marker); + text << separator(index++, except.count()); + ++e; + } + } + } else { + text << "This " << typeString(node) << " is " << theStockLink << "."; + } + 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) + << " was introduced in "; + if (project.isEmpty()) + text << "version"; + else + text << project; + text << " " << node->since() << "." << Atom::ParaRight; + generateText(text, node, marker); + } +} + +/*! + No longer in use. + */ +void Generator::generateOverload( const Node *node, CodeMarker *marker ) +{ + Text text; + text << Atom::ParaLeft + << "This function overloads "; + QString t = node->name() + "()"; + text << Atom::AutoLink << t + << 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 "; + appendFullName( text, from->parent(), func, marker, 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").arg(format())); + 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::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() ); + } +} + +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) + return static_cast<const FakeNode *>(node)->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); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/generator.h b/tools/qdoc3/generator.h new file mode 100644 index 0000000..2c06480 --- /dev/null +++ b/tools/qdoc3/generator.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 + +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, CodeMarker *marker) = 0; + + static void initialize( const Config& config ); + static void terminate(); + static Generator *generatorForFormat( const QString& format ); + +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 void generateText( const Text& text, const Node *relative, + CodeMarker *marker ); + virtual void generateBody( const Node *node, CodeMarker *marker ); + virtual void generateAlsoList( const Node *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 generateExampleFiles(const FakeNode *fake, CodeMarker *marker); + void generateModuleWarning( const ClassNode *classe, CodeMarker *marker); + + virtual int skipAtoms(const Atom *atom, Atom::Type type) const; + virtual QString fullName(const Node *node, const Node *relative, + CodeMarker *marker) const; + + const QString& outputDir() { return outDir; } + 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); + + private: + void generateOverload( const Node *node, CodeMarker *marker ); + void generateReimplementedFrom( const FunctionNode *func, + CodeMarker *marker ); + void appendFullName( Text& text, const Node *apparentNode, + const Node *relative, CodeMarker *marker, + const Node *actualNode = 0 ); + void appendSortedNames(Text& text, const ClassNode *classe, + const QList<RelatedClass> &classes, + CodeMarker *marker); + + 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 QString outDir; + static QString project; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/helpprojectwriter.cpp b/tools/qdoc3/helpprojectwriter.cpp new file mode 100644 index 0000000..d0fa7c0 --- /dev/null +++ b/tools/qdoc3/helpprojectwriter.cpp @@ -0,0 +1,653 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtXml> +#include <QHash> +#include <QMap> + +#include "atom.h" +#include "helpprojectwriter.h" +#include "htmlgenerator.h" +#include "config.h" +#include "node.h" +#include "tree.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.getString(CONFIG_OUTPUTDIR); + + 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"); + 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; + + QHash<QString, FakeNode::SubType> subTypeHash; + subTypeHash["example"] = FakeNode::Example; + subTypeHash["headerfile"] = FakeNode::HeaderFile; + subTypeHash["file"] = FakeNode::File; + subTypeHash["group"] = FakeNode::Group; + subTypeHash["module"] = FakeNode::Module; + subTypeHash["page"] = FakeNode::Page; + subTypeHash["externalpage"] = FakeNode::ExternalPage; + + QSet<FakeNode::SubType> allSubTypes = QSet<FakeNode::SubType>::fromList(subTypeHash.values()); + + foreach (const QString &selector, selectors) { + QStringList pieces = selector.split(":"); + 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(","); + if (typeHash.contains(lower)) { + QSet<FakeNode::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->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); + details << fake->fullTitle(); + details << fake->fullTitle(); + } else { + details << node->name(); + details << node->name(); + } + details << tree->fullDocumentLocation(node); + + 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 = tree->fullDocumentName(node); + + // 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() != FakeNode::ExternalPage && + !fakeNode->fullTitle().isEmpty()) + + project.subprojects[name].nodes[objName] = node; + } + } + } + + switch (node->type()) { + + case Node::Class: + project.keywords.append(keywordDetails(node)); + project.files.insert(tree->fullDocumentLocation(node)); + break; + + case Node::Namespace: + project.keywords.append(keywordDetails(node)); + project.files.insert(tree->fullDocumentLocation(node)); + 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 << tree->fullDocumentLocation(node); + project.keywords.append(details); + } + } + break; + + case Node::Property: + 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(tree->fullDocumentLocation(node->relates())); + } 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] = tree->fullDocumentLocation(enumNode); + + project.keywords.append(typedefDetails); + } + 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() != FakeNode::ExternalPage && + !fakeNode->fullTitle().isEmpty()) { + + if (fakeNode->subType() != FakeNode::File) { + if (fakeNode->doc().hasKeywords()) { + foreach (const Atom *keyword, fakeNode->doc().keywords()) { + if (!keyword->string().isEmpty()) { + QStringList details; + details << keyword->string() + << keyword->string() + << tree->fullDocumentLocation(node) + "#" + Doc::canonicalTitle(keyword->string()); + project.keywords.append(details); + } else + fakeNode->doc().location().warning( + tr("Bad keyword in %1").arg(tree->fullDocumentLocation(node)) + ); + } + } + 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 + << tree->fullDocumentLocation(node) + "#" + Doc::canonicalTitle(title); + project.keywords.append(details); + } else + fakeNode->doc().location().warning( + tr("Bad contents item in %1").arg(tree->fullDocumentLocation(node)) + ); + } + } +*/ + project.files.insert(tree->fullDocumentLocation(node)); + } + 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("/"); + 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) + 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[tree->fullDocumentName(node)] = 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 = tree->fullDocumentLocation(node); + 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()); + // qDebug() << "Title:" << fakeNode->fullTitle(); + + if (fakeNode->subType() == FakeNode::HeaderFile) { + + // 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; + 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"); + QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(project.indexTitle)); + if (indexPath.isEmpty()) + indexPath = "index.html"; + writer.writeAttribute("ref", HtmlGenerator::cleanRef(indexPath)); + writer.writeAttribute("title", project.indexTitle); + project.files.insert(tree->fullDocumentLocation(rootNode)); + + generateSections(project, writer, rootNode); + + foreach (const QString &name, project.subprojects.keys()) { + SubProject subproject = project.subprojects[name]; + + if (!name.isEmpty()) { + writer.writeStartElement("section"); + QString indexPath = tree->fullDocumentLocation(tree->findFakeNodeByTitle(subproject.indexTitle)); + writer.writeAttribute("ref", HtmlGenerator::cleanRef(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. + 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()) + break; + nextPage = const_cast<FakeNode *>(tree->findFakeNodeByTitle(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", HtmlGenerator::cleanRef(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/tools/qdoc3/helpprojectwriter.h b/tools/qdoc3/helpprojectwriter.h new file mode 100644 index 0000000..f90b2a4 --- /dev/null +++ b/tools/qdoc3/helpprojectwriter.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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; + 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/tools/qdoc3/htmlgenerator.cpp b/tools/qdoc3/htmlgenerator.cpp new file mode 100644 index 0000000..a0fc743 --- /dev/null +++ b/tools/qdoc3/htmlgenerator.cpp @@ -0,0 +1,3139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + htmlgenerator.cpp +*/ + +#include "codemarker.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> + +QT_BEGIN_NAMESPACE + +#define COMMAND_VERSION Doc::alias("version") + +static bool showBrokenLinks = false; + +HtmlGenerator::HtmlGenerator() + : helpProjectWriter(0), inLink(false), inContents(false), + inSectionHeading(false), inTableHeader(false), numTableRows(0), + threeColumnEnumValueTable(true), funcLeftParen("\\S(\\()"), + tre(0), slow(false) +{ +} + +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); + 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 + HTMLGENERATOR_STYLE); + postHeader = config.getString(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_POSTHEADER); + 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); + + project = config.getString(CONFIG_PROJECT); + + projectDescription = config.getString(CONFIG_DESCRIPTION); + if (projectDescription.isEmpty() && !project.isEmpty()) + projectDescription = project + " Reference Documentation"; + + projectUrl = config.getString(CONFIG_URL); + + 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; + } + + slow = config.getBool(CONFIG_SLOW); + + stylesheets = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_STYLESHEETS); + customHeadElements = config.getStringList(HtmlGenerator::format() + Config::dot + HTMLGENERATOR_CUSTOMHEADELEMENTS); + codeIndent = config.getInt(CONFIG_CODEINDENT); + + helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp"); +} + +void HtmlGenerator::terminateGenerator() +{ + Generator::terminateGenerator(); +} + +QString HtmlGenerator::format() +{ + return "HTML"; +} + +/*! + This is where the html files and dcf files are written. + \note The html file generation is done in the base class, + PageGenerator::generateTree(). + */ +void HtmlGenerator::generateTree(const Tree *tree, CodeMarker *marker) +{ + // Copy the stylesheets from the directory containing the qdocconf file. + // ### This should be changed to use a special directory in doc/src. + QStringList::ConstIterator styleIter = stylesheets.begin(); + QDir configPath = QDir::current(); + while (styleIter != stylesheets.end()) { + QString filePath = configPath.absoluteFilePath(*styleIter); + Config::copyFile(Location(), filePath, filePath, outputDir()); + ++styleIter; + } + + tre = tree; + nonCompatClasses.clear(); + mainClasses.clear(); + compatClasses.clear(); + moduleClassMap.clear(); + moduleNamespaceMap.clear(); + funcIndex.clear(); + legaleseTexts.clear(); + serviceClasses.clear(); + findAllClasses(tree->root()); + findAllFunctions(tree->root()); + findAllLegaleseTexts(tree->root()); + findAllNamespaces(tree->root()); +#ifdef ZZZ_QDOC_QML + findAllQmlClasses(tree->root()); +#endif + + PageGenerator::generateTree(tree, marker); + + dcfClassesRoot.ref = "classes.html"; + dcfClassesRoot.title = "Classes"; + qSort(dcfClassesRoot.subsections); + + dcfOverviewsRoot.ref = "overviews.html"; + dcfOverviewsRoot.title = "Overviews"; + qSort(dcfOverviewsRoot.subsections); + + dcfExamplesRoot.ref = "examples.html"; + dcfExamplesRoot.title = "Tutorial & Examples"; + qSort(dcfExamplesRoot.subsections); + + DcfSection qtRoot; + appendDcfSubSection(&qtRoot, dcfClassesRoot); + appendDcfSubSection(&qtRoot, dcfOverviewsRoot); + appendDcfSubSection(&qtRoot, dcfExamplesRoot); + + generateDcf(project.toLower().simplified().replace(" ", "-"), + "index.html", + projectDescription, qtRoot); + generateDcf("designer", + "designer-manual.html", + "Qt Designer Manual", + dcfDesignerRoot); + generateDcf("linguist", + "linguist-manual.html", + "Qt Linguist Manual", + dcfLinguistRoot); + generateDcf("assistant", + "assistant-manual.html", + "Qt Assistant Manual", + dcfAssistantRoot); + generateDcf("qmake", + "qmake-manual.html", + "qmake Manual", + dcfQmakeRoot); + + generateIndex(project.toLower().simplified().replace(" ", "-"), + projectUrl, + projectDescription); + + helpProjectWriter->generate(tre); +} + +void HtmlGenerator::startText(const Node * /* relative */, + CodeMarker * /* marker */) +{ + inLink = false; + inContents = false; + inSectionHeading = false; + inTableHeader = false; + numTableRows = 0; + threeColumnEnumValueTable = true; + link.clear(); + sectionNumber.clear(); +} + +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: + 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() << protect(atom->string()); + } + } + else { + out() << protect(atom->string()); + } + break; + case Atom::BaseName: + break; + case Atom::BriefLeft: + if (relative->type() == Node::Fake) { + 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.right(1) == ".") + str.truncate(str.length() - 1); + out() << "This "; + if (relative->type() == Node::Property) + out() << "property"; + else + out() << "variable"; + QStringList words = str.split(" "); + 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: + out() << formattingLeftMap()[ATOM_FORMATTING_TELETYPE]; + if (inLink) { + out() << protect(plainCode(atom->string())); + } else { + out() << highlightedCode(atom->string(), marker, relative); + } + out() << formattingRightMap()[ATOM_FORMATTING_TELETYPE]; + break; + case Atom::Code: + out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, atom->string()), + marker, relative)) + << "</pre>\n"; + break; +#ifdef QDOC_QML + case Atom::Qml: + out() << "<pre>" << 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>" << 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><font color=\"#404040\">" + << trimmedTrailing(protect(plainCode(indent(codeIndent, atom->string())))) + << "</font></pre>\n"; + 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: + 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 { + out() << formattingRightMap()[atom->string()]; + } + break; + case Atom::GeneratedList: + if (atom->string() == "annotatedclasses") { + generateAnnotatedList(relative, marker, nonCompatClasses); + } else if (atom->string() == "classes") { + generateCompactList(relative, marker, nonCompatClasses); + } 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. + QMap<QString, const Node *> 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 = tre->groups(); + foreach (const QString &groupName, editionGroupMap[editionName]) { + QList<Node *> groupClasses; + if (groupName.startsWith("-")) { + 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); + } 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); + } else if (atom->string() == "services") { + generateCompactList(relative, marker, serviceClasses); + } 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()) { + QMap<QString, const Node *> 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::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 align=\"center\">"; + if (fileName.isEmpty()) { + out() << "<font color=\"red\">[Missing image " + << protect(atom->string()) << "]</font>"; + } else { + out() << "<img src=\"" << protect(fileName) << "\""; + if (!text.isEmpty()) + out() << " alt=\"" << protect(text) << "\""; + out() << " />"; + helpProjectWriter->addExtraFile(fileName); + } + if (atom->type() == Atom::Image) + out() << "</p>"; + } + break; + case Atom::ImageText: + break; + case Atom::LegaleseLeft: + out() << "<div style=\"padding: 0.5em; background: #e0e0e0; color: black\">"; + 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()) + relative->doc().location().warning(tr("Cannot link to '%1' in %2") + .arg(atom->string()) + .arg(marker->plainFullName(relative))); + 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() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"100%\">\n" + "<tr><th width=\"25%\">Constant</th><th width=\"15%\">Value</th>" + "<th width=\"60%\">Description</th></tr>\n"; + } else { + out() << "<p><table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" width=\"40%\">\n" + << "<tr><th width=\"60%\">Constant</th><th width=\"40%\">Value</th></tr>\n"; + } + } else { + out() << "<ol type="; + if (atom->string() == ATOM_LIST_UPPERALPHA) { + out() << "\"A\""; + } 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 valign=\"top\"><tt>" + << protect(plainCode(marker->markedUpEnumValue(atom->next()->string(), + relative))) + << "</tt></td><td align=\"center\" valign=\"top\">"; + + 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>" << protect(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 valign=\"top\">"; + 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></p>\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: +#if 0 + { + int nextLevel = atom->string().toInt(); + 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); + } + out() << "<a name=\"sec-" << sectionNumber.join("-") << "\"></a>\n"; + } +#else + out() << "<a name=\"" << Doc::canonicalTitle(Text::sectionHeading(atom).toString()) + << "\"></a>\n"; +#endif + break; + case Atom::SectionRight: + break; + case Atom::SectionHeadingLeft: + out() << "<h" + QString::number(atom->string().toInt() + hOffset(relative)) + ">"; + 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() << protect(atom->string()); + } + break; + case Atom::TableLeft: + if (in_para) { + out() << "</p>\n"; + in_para = false; + } + if (!atom->string().isEmpty()) { + if (atom->string().contains("%")) + out() << "<p><table width=\"" << atom->string() << "\" " + << "align=\"center\" cellpadding=\"2\" " + << "cellspacing=\"1\" border=\"0\">\n"; + else + out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n"; + } else { + out() << "<p><table align=\"center\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n"; + } + numTableRows = 0; + break; + case Atom::TableRight: + out() << "</table></p>\n"; + break; + case Atom::TableHeaderLeft: + out() << "<thead><tr valign=\"top\" class=\"qt-style\">"; + inTableHeader = true; + break; + case Atom::TableHeaderRight: + out() << "</tr>"; + if (matchAhead(atom, Atom::TableHeaderLeft)) { + skipAhead = 1; + out() << "\n<tr valign=\"top\" class=\"qt-style\">"; + } else { + out() << "</thead>\n"; + inTableHeader = false; + } + break; + case Atom::TableRowLeft: + 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"; + + QStringList spans = atom->string().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) << "\""; + out() << ">"; + } + if (matchAhead(atom, Atom::ParaLeft)) + skipAhead = 1; + } + break; + case Atom::TableItemRight: + if (inTableHeader) + out() << "</th>"; + else + out() << "</td>"; + if (matchAhead(atom, Atom::ParaLeft)) + skipAhead = 1; + break; + case Atom::TableOfContents: + { + int numColumns = 1; + const Node *node = relative; + + Doc::SectioningUnit sectioningUnit = Doc::Section4; + QStringList params = atom->string().split(","); + QString columnText = params.at(0); + QStringList pieces = columnText.split(" ", 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); + sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt(); + } + + if (node) + generateTableOfContents(node, marker, sectioningUnit, numColumns, + relative); + } + break; + case Atom::Target: + out() << "<a name=\"" << Doc::canonicalTitle(atom->string()) << "\"></a>"; + break; + case Atom::UnhandledFormat: + out() << "<font color=\"red\"><b><Missing HTML></b></font>"; + break; + case Atom::UnknownCommand: + out() << "<font color=\"red\"><b><code>\\" << protect(atom->string()) + << "</code></b></font>"; + break; + default: + unknownAtom(atom); + } + return skipAhead; +} + +void HtmlGenerator::generateClassLikeNode(const InnerNode *inner, + CodeMarker *marker) +{ + QList<Section> sections; + QList<Section>::ConstIterator s; + + const ClassNode *classe = 0; + const NamespaceNode *namespasse = 0; + + QString title; + QString rawTitle; + QString fullTitle; + if (inner->type() == Node::Namespace) { + namespasse = static_cast<const NamespaceNode *>(inner); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Namespace Reference"; + } else if (inner->type() == Node::Class) { + classe = static_cast<const ClassNode *>(inner); + rawTitle = marker->plainName(inner); + fullTitle = marker->plainFullName(inner); + title = rawTitle + " Class Reference"; + } + + DcfSection classSection; + classSection.title = title; + classSection.ref = linkForNode(inner, 0); + classSection.keywords += qMakePair(inner->name(), classSection.ref); + + Text subtitleText; + if (rawTitle != fullTitle) + subtitleText << "(" << Atom(Atom::AutoLink, fullTitle) << ")" + << Atom(Atom::LineBreak); + + QString fixedModule = inner->moduleName(); + if (fixedModule == "Qt3SupportLight") + fixedModule = "Qt3Support"; + if (!fixedModule.isEmpty()) + subtitleText << "[" << Atom(Atom::AutoLink, fixedModule) << " module]"; + + if (fixedModule.isEmpty()) { + QMultiMap<QString, QString> publicGroups = tre->publicGroups(); + QList<QString> groupNames = publicGroups.values(inner->name()); + if (!groupNames.isEmpty()) { + qSort(groupNames.begin(), groupNames.end()); + subtitleText << "["; + for (int j=0; j<groupNames.count(); j++) { + subtitleText << Atom(Atom::AutoLink, groupNames[j]); + if (j<groupNames.count()-1) + subtitleText <<", "; + } + subtitleText << "]"; + } + } + + generateHeader(title, inner, marker, true); + generateTitle(title, subtitleText, SmallSubTitle, inner, marker); + + generateBrief(inner, marker); + generateIncludes(inner, marker); + generateStatus(inner, marker); + if (classe) { + generateModuleWarning(classe, marker); + generateInherits(classe, marker); + generateInheritedBy(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 = marker->sections(inner, CodeMarker::Summary, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + if (s->members.isEmpty()) { + if (!s->inherited.isEmpty()) + needOtherSection = true; + } else { + out() << "<a name=\"" << registerRef((*s).name.toLower()) << "\"></a>\n"; + out() << "<h3>" << protect((*s).name) << "</h3>\n"; + + generateSectionList(*s, inner, marker, CodeMarker::Summary); + } + ++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>\n"; + + if (!inner->doc().isEmpty()) { + out() << "<hr />\n" + << "<h2>" << "Detailed Description" << "</h2>\n"; + generateBody(inner, marker); + generateAlsoList(inner, marker); + } + + sections = marker->sections(inner, CodeMarker::Detailed, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + out() << "<hr />\n"; + out() << "<h2>" << protect((*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)); + } + foreach (const QString &name, names) + classSection.keywords += qMakePair(name, linkForNode(*m, 0)); + } + ++m; + } + ++s; + } + generateFooter(inner); + + if (!membersLink.isEmpty()) { + DcfSection membersSection; + membersSection.title = "List of all members"; + membersSection.ref = membersLink; + appendDcfSubSection(&classSection, membersSection); + } + if (!obsoleteLink.isEmpty()) { + DcfSection obsoleteSection; + obsoleteSection.title = "Obsolete members"; + obsoleteSection.ref = obsoleteLink; + appendDcfSubSection(&classSection, obsoleteSection); + } + if (!compatLink.isEmpty()) { + DcfSection compatSection; + compatSection.title = "Qt 3 support members"; + compatSection.ref = compatLink; + appendDcfSubSection(&classSection, compatSection); + } + + appendDcfSubSection(&dcfClassesRoot, classSection); +} + +void HtmlGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) +{ + SubTitleSize subTitleSize = LargeSubTitle; + DcfSection fakeSection; + fakeSection.title = fake->fullTitle(); + fakeSection.ref = linkForNode(fake, 0); + + QList<Section> sections; + QList<Section>::const_iterator s; + + QString htmlTitle = fake->fullTitle(); + if (fake->subType() == FakeNode::File && !fake->subTitle().isEmpty()) { + subTitleSize = SmallSubTitle; + htmlTitle += " (" + fake->subTitle() + ")"; + } + + generateHeader(htmlTitle, fake, marker, true); + generateTitle(fake->fullTitle(), Text() << fake->subTitle(), subTitleSize, + fake, marker); + + if (fake->subType() == FakeNode::Module) { + // Generate brief text and status for modules. + generateBrief(fake, marker); + generateStatus(fake, marker); + + if (moduleNamespaceMap.contains(fake->name())) { + out() << "<h2>Namespaces</h2>\n"; + generateAnnotatedList(fake, marker, moduleNamespaceMap[fake->name()]); + } + if (moduleClassMap.contains(fake->name())) { + out() << "<h2>Classes</h2>\n"; + generateAnnotatedList(fake, marker, moduleClassMap[fake->name()]); + } + } + else if (fake->subType() == FakeNode::HeaderFile) { + // Generate brief text and status for modules. + generateBrief(fake, marker); + generateStatus(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"; + + if (!membersLink.isEmpty()) { + DcfSection membersSection; + membersSection.title = "List of all members"; + membersSection.ref = membersLink; + appendDcfSubSection(&fakeSection, membersSection); + } + if (!obsoleteLink.isEmpty()) { + DcfSection obsoleteSection; + obsoleteSection.title = "Obsolete members"; + obsoleteSection.ref = obsoleteLink; + appendDcfSubSection(&fakeSection, obsoleteSection); + } + if (!compatLink.isEmpty()) { + DcfSection compatSection; + compatSection.title = "Qt 3 support members"; + compatSection.ref = compatLink; + appendDcfSubSection(&fakeSection, compatSection); + } + } + + sections = marker->sections(fake, CodeMarker::Summary, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + out() << "<a name=\"" << registerRef((*s).name) << "\"></a>\n"; + out() << "<h3>" << protect((*s).name) << "</h3>\n"; + generateSectionList(*s, fake, marker, CodeMarker::Summary); + ++s; + } + + Text brief = fake->doc().briefText(); + if (fake->subType() == FakeNode::Module && !brief.isEmpty()) { + out() << "<a name=\"" << registerRef("details") << "\"></a>\n"; + out() << "<h2>" << "Detailed Description" << "</h2>\n"; + } + + generateBody(fake, marker); +#ifdef QDOC_QML + if (fake->subType() == FakeNode::QmlClass) { + qDebug() << "generateFakeNode(): QML CLASS" << fake->name(); + const QmlNode* qmlNode = static_cast<const QmlNode*>(fake); + const ClassNode* cn = qmlNode->classNode(); + if (cn) { + qDebug() << " CPP CLASS" << cn->name(); + generateBody(cn, marker); + } + } +#endif + + generateAlsoList(fake, marker); + + if (!fake->groupMembers().isEmpty()) { + QMap<QString, const Node *> groupMembersMap; + foreach (const Node *node, fake->groupMembers()) { + if (node->type() == Node::Class || node->type() == Node::Namespace) + groupMembersMap[node->name()] = node; + } + generateAnnotatedList(fake, marker, groupMembersMap); + } + + fakeSection.keywords += qMakePair(fakeSection.title, fakeSection.ref); + + sections = marker->sections(fake, CodeMarker::Detailed, CodeMarker::Okay); + s = sections.begin(); + while (s != sections.end()) { + out() << "<hr />\n"; + out() << "<h2>" << protect((*s).name) << "</h2>\n"; + + NodeList::ConstIterator m = (*s).members.begin(); + while (m != (*s).members.end()) { + generateDetailedMember(*m, fake, marker); + fakeSection.keywords += qMakePair((*m)->name(), linkForNode(*m, 0)); + ++m; + } + ++s; + } + generateFooter(fake); + + if (fake->subType() == FakeNode::Example) { + appendDcfSubSection(&dcfExamplesRoot, fakeSection); + } else if (fake->subType() != FakeNode::File) { + QString contentsPage = fake->links().value(Node::ContentsLink).first; + + if (contentsPage == "Qt Designer Manual") { + appendDcfSubSection(&dcfDesignerRoot, fakeSection); + } else if (contentsPage == "Qt Linguist Manual") { + appendDcfSubSection(&dcfLinguistRoot, fakeSection); + } else if (contentsPage == "Qt Assistant Manual") { + appendDcfSubSection(&dcfAssistantRoot, fakeSection); + } else if (contentsPage == "qmake Manual") { + appendDcfSubSection(&dcfQmakeRoot, fakeSection); + } else { + appendDcfSubSection(&dcfOverviewsRoot, fakeSection); + } + } +} + +QString HtmlGenerator::fileExtension(const Node * /* node */) +{ + return "html"; +} + +void HtmlGenerator::generateHeader(const QString& title, + const Node *node, + CodeMarker *marker, + bool mainPage) +{ + out() << "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"; + + out() << "<!DOCTYPE html\n" + " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"; + + QString shortVersion; + if ((project != "Qtopia") && (project != "Qt Extended")) { + shortVersion = project + " " + shortVersion + ": "; + if (node && !node->doc().location().isEmpty()) + out() << "<!-- " << node->doc().location().filePath() << " -->\n"; + + shortVersion = tre->version(); + if (shortVersion.count(QChar('.')) == 2) + shortVersion.truncate(shortVersion.lastIndexOf(QChar('.'))); + if (!shortVersion.isEmpty()) { + if (project == "QSA") + shortVersion = "QSA " + shortVersion + ": "; + else + shortVersion = "Qt " + shortVersion + ": "; + } + } + + out() << "<head>\n" + " <title>" << shortVersion << protect(title) << "</title>\n"; + if (!style.isEmpty()) + out() << " <style type=\"text/css\">" << style << "</style>\n"; + + const QMap<QString, QString> &metaMap = node->doc().metaTagMap(); + if (!metaMap.isEmpty()) { + QMapIterator<QString, QString> i(metaMap); + while (i.hasNext()) { + i.next(); + out() << " <meta name=\"" << protect(i.key()) << "\" contents=\"" + << protect(i.value()) << "\" />\n"; + } + } + + 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::ContentsLink)) { + linkPair = node->links()[Node::ContentsLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + if (!linkNode || linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + + out() << " <link rel=\"contents\" href=\"" + << anchorPair.first << "\" />\n"; + + navigationLinks += "[<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::IndexLink)) { + linkPair = node->links()[Node::IndexLink]; + linkNode = findNodeForTarget(linkPair.first, node, marker); + if (!linkNode || linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + out() << " <link rel=\"index\" href=\"" + << anchorPair.first << "\" />\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"; + } + } + + foreach (const QString &stylesheet, stylesheets) { + out() << " <link href=\"" << stylesheet << "\" rel=\"stylesheet\" " + << "type=\"text/css\" />\n"; + } + + foreach (const QString &customHeadElement, customHeadElements) { + out() << " " << customHeadElement << "\n"; + } + + out() << "</head>\n" + "<body>\n"; + if (mainPage) + generateMacRef(node, marker); + out() << QString(postHeader).replace("\\" + COMMAND_VERSION, tre->version()); + + + if (node && !node->links().empty()) + out() << "<p>\n" << navigationLinks << "</p>\n"; +} + +void HtmlGenerator::generateTitle(const QString& title, + const Text &subTitle, + SubTitleSize subTitleSize, + const Node *relative, + CodeMarker *marker) +{ + out() << "<h1 class=\"title\">" << protect(title); + if (!subTitle.isEmpty()) { + out() << "<br />"; + if (subTitleSize == SmallSubTitle) + out() << "<span class=\"small-subtitle\">"; + else + out() << "<span class=\"subtitle\">"; + generateText(subTitle, relative, marker); + out() << "</span>\n"; + } + out() << "</h1>\n"; +} + +void HtmlGenerator::generateFooter(const Node *node) +{ + if (node && !node->links().empty()) + out() << "<p>\n" << navigationLinks << "</p>\n"; + + out() << QString(footer).replace("\\" + COMMAND_VERSION, tre->version()) + << QString(address).replace("\\" + COMMAND_VERSION, tre->version()) + << "</body>\n" + "</html>\n"; +} + +void HtmlGenerator::generateBrief(const Node *node, CodeMarker *marker, + const Node *relative) +{ + Text brief = node->doc().briefText(); + if (!brief.isEmpty()) { + 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"; + } +} + +void HtmlGenerator::generateIncludes(const InnerNode *inner, CodeMarker *marker) +{ + if (!inner->includes().isEmpty()) { + out() << "<pre>" << trimmedTrailing(highlightedCode(indent(codeIndent, + marker->markedUpIncludes( + inner->includes())), + marker, inner)) + << "</pre>"; + } +} + +void HtmlGenerator::generateTableOfContents(const Node *node, CodeMarker *marker, + Doc::SectioningUnit sectioningUnit, + int numColumns, const Node *relative) + +{ + 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() << "<p><table width=\"100%\">\n<tr valign=\"top\">" << 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)sectioningUnit) + 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() << "<a href=\"" << nodeName << "#" << Doc::canonicalTitle(headingText.toString()) + << "\">"; + generateAtomList(headingText.firstAtom(), node, marker, true, numAtoms); + out() << "</a></li>\n"; + + ++columnSize; + } + while (!sectionNumber.isEmpty()) { + out() << "</ul>\n"; + sectionNumber.removeLast(); + } + + if (numColumns > 1) + out() << "</td></tr></table></p>\n"; + + inContents = false; + inLink = false; +} + +#if 0 +void HtmlGenerator::generateNavigationBar(const NavigationBar& bar, + const Node *node, + CodeMarker *marker) +{ + if (bar.prev.begin() != 0 || bar.current.begin() != 0 || + bar.next.begin() != 0) { + out() << "<p align=\"right\">"; + if (bar.prev.begin() != 0) { +#if 0 + out() << "[<a href=\"" << section.previousBaseName() + << ".html\">Prev: "; + generateText(section.previousHeading(), node, marker); + out() << "</a>]\n"; +#endif + } + if (bar.current.begin() != 0) { + out() << "[<a href=\"" << "home" + << ".html\">Home</a>]\n"; + } + if (bar.next.begin() != 0) { + out() << "[<a href=\"" << fileBase(node, bar.next) + << ".html\">Next: "; + generateText(Text::sectionHeading(bar.next.begin()), node, marker); + out() << "</a>]\n"; + } + out() << "</p>\n"; + } +} +#endif + +QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker) +{ + QList<Section> sections; + QList<Section>::ConstIterator s; + + sections = marker->sections(inner, CodeMarker::SeparateList, CodeMarker::Okay); + if (sections.isEmpty()) + return QString(); + + QString fileName = fileBase(inner) + "-members." + fileExtension(inner); + beginSubPage(inner->location(), fileName); + QString title = "List of All Members for " + inner->name(); + generateHeader(title, inner, marker, false); + 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::SeparateList); + + 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->location(), fileName); + generateHeader(title, inner, marker, false); + 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) << "\">" << protect(inner->name()) + << " class reference</a></li></ul></p>\n"; + + for (i = 0; i < sections.size(); ++i) { + out() << "<h3>" << protect(sections.at(i).name) << "</h3>\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>" << protect(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; + + QMap<QString, const Node *> topLevel; + QMap<QString, const Node *>::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<QMap<QString, const Node *> > 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()); + + QMap<QString, const Node *> newTop; + foreach (const RelatedClass &d, child->derivedClasses()) { + if (d.access != Node::Private) + 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 QMap<QString, const Node *> &nodeMap) +{ + out() << "<p><table width=\"100%\" class=\"annotated\" cellpadding=\"2\" cellspacing=\"1\" border=\"0\">\n"; + + int row = 0; + foreach (const QString &name, nodeMap.keys()) { + const Node *node = nodeMap[name]; + + if (++row % 2 == 1) + out() << "<tr valign=\"top\" class=\"odd\">"; + else + out() << "<tr valign=\"top\" class=\"even\">"; + out() << "<th>"; + generateFullName(node, relative, marker); + out() << "</th>"; + + if (!(node->type() == Node::Fake)) { + Text brief = node->doc().trimmedBriefText(name); + if (!brief.isEmpty()) { + out() << "<td>"; + generateText(brief, node, marker); + out() << "</td>"; + } + } else { + out() << "<td>"; + out() << protect(node->doc().briefText().toString()); + out() << "</td>"; + } + out() << "</tr>\n"; + } + out() << "</table></p>\n"; +} + +void HtmlGenerator::generateCompactList(const Node *relative, CodeMarker *marker, + const QMap<QString, const Node *> &classMap) +{ + const int NumParagraphs = 37; // '0' to '9', 'A' to 'Z', '_' + const int NumColumns = 4; // number of columns in the result + + if (classMap.isEmpty()) + return; + + /* + First, find out the common prefix of all non-namespaced classes. + For Qt, the prefix is Q. It can easily be derived from the first + and last classes in alphabetical order (QAccel and QXtWidget in Qt 2.1). + */ + int commonPrefixLen = 0; + QString commonPrefix; + QString first; + QString last; + + QMap<QString, const Node *>::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); + + /* + 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 + QMap<QString, const Node *>. + */ + QMap<QString, const Node *> paragraph[NumParagraphs]; + QString paragraphName[NumParagraphs]; + + QMap<QString, const Node *>::ConstIterator c = classMap.begin(); + while (c != classMap.end()) { + QStringList pieces = c.key().split("::"); + QString key; + if (pieces.size() == 1) + key = pieces.last().mid(commonPrefixLen).toLower(); + else + key = pieces.last().toLower(); + + int paragraphNo = NumParagraphs - 1; + + if (key[0].digitValue() != -1) { + paragraphNo = key[0].digitValue(); + } else if (key[0] >= QLatin1Char('a') && key[0] <= QLatin1Char('z')) { + paragraphNo = 10 + key[0].unicode() - 'a'; + } + + paragraphName[paragraphNo] = key[0].toUpper(); + paragraph[paragraphNo].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]; + int i, j, k; + + paragraphOffset[0] = 0; + for (j = 0; j < NumParagraphs; j++) + paragraphOffset[j + 1] = paragraphOffset[j] + paragraph[j].count(); + + int firstOffset[NumColumns + 1]; + int currentOffset[NumColumns]; + int currentParagraphNo[NumColumns]; + int currentOffsetInParagraph[NumColumns]; + + int numRows = (classMap.count() + NumColumns - 1) / NumColumns; + int curParagNo = 0; + + for (i = 0; i < NumColumns; i++) { + firstOffset[i] = qMin(i * numRows, classMap.size()); + currentOffset[i] = firstOffset[i]; + + for (j = curParagNo; j < NumParagraphs; j++) { + if (paragraphOffset[j] > firstOffset[i]) + break; + if (paragraphOffset[j] <= firstOffset[i]) + curParagNo = j; + } + currentParagraphNo[i] = curParagNo; + currentOffsetInParagraph[i] = firstOffset[i] - + paragraphOffset[curParagNo]; + } + firstOffset[NumColumns] = classMap.count(); + + out() << "<p><table width=\"100%\">\n"; + for (k = 0; k < numRows; k++) { + out() << "<tr>\n"; + for (i = 0; i < NumColumns; i++) { + if (currentOffset[i] >= firstOffset[i + 1]) { + // this column is finished + out() << "<td>\n</td>\n"; + } else { + while (currentOffsetInParagraph[i] == paragraph[currentParagraphNo[i]].count()) { + ++currentParagraphNo[i]; + currentOffsetInParagraph[i] = 0; + } + + out() << "<td align=\"right\">"; + if (currentOffsetInParagraph[i] == 0) { + // start a new paragraph + out() << "<b>" << paragraphName[currentParagraphNo[i]] << " </b>"; + } + out() << "</td>\n"; + + // bad loop + QMap<QString, const Node *>::Iterator it; + it = paragraph[currentParagraphNo[i]].begin(); + for (j = 0; j < currentOffsetInParagraph[i]; j++) + ++it; + + out() << "<td>"; + // Previously, we used generateFullName() for this, but we + // require some special formatting. + out() << "<a href=\"" << linkForNode(it.value(), relative) << "\">"; + QStringList pieces = fullName(it.value(), relative, marker).split("::"); + out() << protect(pieces.last()); + out() << "</a>"; + if (pieces.size() > 1) { + out() << " ("; + generateFullName(it.value()->parent(), relative, marker); + out() << ")"; + } + out() << "</td>\n"; + + currentOffset[i]++; + currentOffsetInParagraph[i]++; + } + } + out() << "</tr>\n"; + } + out() << "</table></p>\n"; +} + +void HtmlGenerator::generateFunctionIndex(const Node *relative, CodeMarker *marker) +{ + out() << "<p align=\"center\"><font size=\"+1\"><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></font></p>\n"; + + char nextLetter = 'a'; + char currentLetter; + +#if 1 + out() << "<ul>\n"; +#endif + QMap<QString, QMap<QString, const Node *> >::ConstIterator f = funcIndex.begin(); + while (f != funcIndex.end()) { +#if 1 + out() << "<li>"; +#else + out() << "<p>"; +#endif + out() << protect(f.key()) << ":"; + + currentLetter = f.key()[0].unicode(); + while (islower(currentLetter) && currentLetter >= nextLetter) { + out() << QString("<a name=\"%1\"></a>").arg(nextLetter); + nextLetter++; + } + + QMap<QString, const Node *>::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::generateSynopsis(const Node *node, const Node *relative, + CodeMarker *marker, CodeMarker::SynopsisStyle style) +{ + QString marked = marker->markedUpSynopsis(node, relative, style); + QRegExp templateTag("(<[^@>]*>)"); + if (marked.indexOf(templateTag) != -1) { + QString contents = protect(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.replace("@name>", "b>"); + + if (style == CodeMarker::SeparateList) { + QRegExp extraRegExp("<@extra>.*</@extra>"); + extraRegExp.setMinimal(true); + marked.replace(extraRegExp, ""); + } else { + marked.replace("<@extra>", " <tt>"); + marked.replace("</@extra>", "</tt>"); + } + + if (style != CodeMarker::Detailed) { + marked.replace("<@type>", ""); + marked.replace("</@type>", ""); + } + out() << highlightedCode(marked, marker, relative); +} + +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 = tre->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() == FakeNode::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() == FakeNode::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 *>(tre->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( + protect(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) << "\">" + << protect(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) << "\">" + << protect(title) << "</a></li>\n"; + } + out() << "</ul>\n"; + } +} + +void HtmlGenerator::generateSectionList(const Section& section, const Node *relative, + CodeMarker *marker, CodeMarker::SynopsisStyle style) +{ + if (!section.members.isEmpty()) { + bool twoColumn = false; + if (style == CodeMarker::SeparateList) { + twoColumn = (section.members.count() >= 16); + } else if (section.members.first()->type() == Node::Property) { + twoColumn = (section.members.count() >= 5); + } + if (twoColumn) + out() << "<p><table width=\"100%\" border=\"0\" cellpadding=\"0\"" + " cellspacing=\"0\">\n" + << "<tr><td width=\"45%\" valign=\"top\">"; + 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 (twoColumn && i == (int) (section.members.count() + 1) / 2) + out() << "</ul></td><td valign=\"top\"><ul>\n"; + + out() << "<li><div class=\"fn\"></div>"; + if (style == CodeMarker::Accessors) + out() << "<b>"; + generateSynopsis(*m, relative, marker, style); + if (style == CodeMarker::Accessors) + out() << "</b>"; + out() << "</li>\n"; + i++; + ++m; + } + out() << "</ul>\n"; + if (twoColumn) + out() << "</td></tr>\n</table></p>\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<ClassNode *, int> >::ConstIterator p = section.inherited.begin(); + while (p != section.inherited.end()) { + out() << "<li><div class=\"fn\"></div>"; + 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()) << "\">" + << protect(marker->plainFullName((*p).first, relative)) + << "</a></li>\n"; + ++p; + } +} + +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() << protect(atom->string().left(k)); + if (link.isEmpty()) { + if (showBrokenLinks) + out() << "</i>"; + } else { + out() << "</a>"; + } + inLink = false; + out() << protect(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) + out() << "<tt>"; + if (func) { + out() << protect(atom->string().left(atom->string().length() - 2)); + } else { + out() << protect(atom->string()); + } + out() << "</tt>"; + } else { + out() << protect(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 += "#"; + } 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::protect(const QString& string) +{ +#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 (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 +} + +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)>"); +static QRegExp unknownTag("</?@[^>]*>"); + +bool parseArg(const QString &src, + const QString &tag, + int *pos, + int n, + QStringRef *contents, + QStringRef *par1 = 0, + bool debug = false) +{ +#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 +} + +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; + } +} + +QString HtmlGenerator::highlightedCode(const QString& markedCode, + CodeMarker *marker, + const Node *relative) +{ + QString src = markedCode; + QString html; + QStringRef arg; + QStringRef par1; + + const QChar charLangle = '<'; + const QChar charAt = '@'; + + // replace all <@link> tags: "(<@link node=\"([^\"]+)\">).*(</@link>)" + static const QString linkTag("link"); + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + if (parseArg(src, linkTag, &i, n, &arg, &par1)) { + QString link = linkForNode( + CodeMarker::nodeForString(par1.toString()), relative); + addLink(link, arg, &html); + } + else { + html += charLangle; + html += charAt; + } + } + else { + html += src.at(i++); + } + } + + + if (slow) { + // is this block ever used at all? + // replace all <@func> tags: "(<@func target=\"([^\"]*)\">)(.*)(</@func>)" + src = html; + html = QString(); + static const QString funcTag("func"); + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + if (parseArg(src, funcTag, &i, n, &arg, &par1)) { + QString link = linkForNode( + marker->resolveTarget(par1.toString(), + tre, + relative), + 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(); + static const QString typeTags[] = { "type", "headerfile", "func" }; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle && src.at(i + 1) == charAt) { + i += 2; + bool handled = false; + for (int k = 0; k != 3; ++k) { + if (parseArg(src, typeTags[k], &i, n, &arg, &par1)) { + par1 = QStringRef(); + QString link = linkForNode( + marker->resolveTarget(arg.toString(), tre, relative), + relative); + addLink(link, arg, &html); + handled = true; + break; + } + } + 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\">"; + // "</@(?:comment|preprocessor|string|char)>" -> "</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\">", + "</@comment>", "</span>", + "</@preprocessor>","</span>", + "</@string>", "</span>", + "</@char>", "</span>" + // "<@char>", "<font color=blue>", + // "</@char>", "</font>", + // "<@func>", "<font color=green>", + // "</@func>", "</font>", + // "<@id>", "<i>", + // "</@id>", "</i>", + // "<@keyword>", "<b>", + // "</@keyword>", "</b>", + // "<@number>", "<font color=yellow>", + // "</@number>", "</font>", + // "<@op>", "<b>", + // "</@op>", "</b>", + // "<@param>", "<i>", + // "</@param>", "</i>", + // "<@string>", "<font color=green>", + // "</@string>", "</font>", + }; + for (int i = 0, n = src.size(); i < n;) { + if (src.at(i) == charLangle) { + bool handled = false; + for (int k = 0; k != 8; ++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; +} + +QString HtmlGenerator::fileBase(const Node *node) +{ + 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; +} + +#if 0 +QString HtmlGenerator::fileBase(const Node *node, + const SectionIterator& section) +{ + QStringList::ConstIterator s = section.sectionNumber().end(); + QStringList::ConstIterator b = section.baseNameStack().end(); + + QString suffix; + QString base = fileBase(node); + + while (s != section.sectionNumber().begin()) { + --s; + --b; + if (!(*b).isEmpty()) { + base = *b; + break; + } + suffix.prepend("-" + *s); + } + return base + suffix; +} +#endif + +QString HtmlGenerator::fileName(const Node *node) +{ + if (node->type() == Node::Fake) { + if (static_cast<const FakeNode *>(node)->subType() == FakeNode::ExternalPage) + 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::Property: + ref = node->name() + "-prop"; + break; + case Node::Variable: + ref = node->name() + "-var"; + break; + case Node::Target: + return protect(node->name()); + } + return registerRef(ref); +} + +QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) +{ + QString link; + QString fn; + QString ref; + + 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(); + + fn = fileName(node); +/* if (!node->url().isEmpty()) + return fn;*/ +#if 0 + // ### reintroduce this test, without breaking .dcf files + if (fn != outFileName()) +#endif + link += fn; + + if (!node->isInnerNode()) { + ref = refForNode(node); + if (relative && fn == fileName(relative) && ref == refForNode(relative)) + return QString(); + + link += "#"; + link += ref; + } + 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() << protect(fullName(apparentNode, relative, marker)); + out() << "</a>"; +} + +void HtmlGenerator::generateDetailedMember(const Node *node, + const InnerNode *relative, + CodeMarker *marker) +{ + const EnumNode *enume; + + generateMacRef(node, marker); + if (node->type() == Node::Enum + && (enume = static_cast<const EnumNode *>(node))->flagsType()) { + generateMacRef(enume->flagsType(), marker); + 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>\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>Access functions:</p>\n"; + generateSectionList(section, node, marker, CodeMarker::Accessors); + } + } + else if (node->type() == Node::Enum) { + const EnumNode *enume = static_cast<const EnumNode *>(node); + if (enume->flagsType()) { + out() << "<p>The " << protect(enume->flagsType()->name()) + << " type is a typedef for " + << "<a href=\"qflags.html\">QFlags</a><" + << protect(enume->name()) + << ">. It stores an OR combination of " << protect(enume->name()) + << " values.</p>\n"; + } + } + generateAlsoList(node, marker); +} + +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 { + 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)->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->metaness() != FunctionNode::Ctor + && func->metaness() != FunctionNode::Dtor) { + funcIndex[(*c)->name()].insert((*c)->parent()->name(), *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; + } +} + +#ifdef ZZZ_QDOC_QML +/*! + This function finds all the qml element nodes and + stores them in a map for later use. + */ +void HtmlGenerator::findAllQmlClasses(const InnerNode *node) +{ + NodeList::const_iterator c = node->childNodes().constBegin(); + while (c != node->childNodes().constEnd()) { + if ((*c)->type() == Node::Fake) { + const FakeNode* fakeNode = static_cast<const FakeNode *>(*c); + if (fakeNode->subType() == FakeNode::QmlClass) { + const QmlNode* qmlNode = static_cast<const QmlNode*>(fakeNode); + qDebug() << "HtmlGenerator: QML CLASS" << qmlNode->name(); + const Node* n = qmlNode->classNode(); + if (n) + qDebug() << " FOUND IT!" << n->name(); + } + qmlClasses.insert(fakeNode->name(),*c); + } + ++c; + } +} +#endif + +#if 0 + else if ((*c)->isInnerNode()) { + findAllClasses(static_cast<InnerNode *>(*c)); + } +#endif + +int HtmlGenerator::hOffset(const Node *node) +{ + switch (node->type()) { + case Node::Namespace: + case Node::Class: + return 2; + case Node::Fake: + if (node->doc().briefText().isEmpty()) + return 1; + else + return 2; + 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 = tre->root()->findNode(target, Node::Fake); + } else if (marker) { + node = marker->resolveTarget(target, tre, relative); + if (!node) + node = tre->findFakeNodeByTitle(target); + if (!node && atom) { + node = tre->findUnambiguousTarget(target, + *const_cast<Atom**>(&atom)); + } + } + + 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; + + 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 = tre->root()->findNode(first, Node::Fake); + } else { + node = marker->resolveTarget(first, tre, relative); + if (!node) + node = tre->findFakeNodeByTitle(first); + if (!node) + node = tre->findUnambiguousTarget(first, targetAtom); + } + + if (node) { + if (!node->url().isEmpty()) + return node->url(); + else + path.removeFirst(); + } else { + node = relative; + } + + while (!path.isEmpty()) { + targetAtom = tre->findTarget(path.first(), node); + if (targetAtom == 0) + break; + path.removeFirst(); + } + + if (path.isEmpty()) { + link = linkForNode(node, relative); + if (targetAtom) + link += "#" + refForAtom(targetAtom, node); + } + } + return link; +} + +void HtmlGenerator::generateDcf(const QString &fileBase, + const QString &startPage, + const QString &title, + DcfSection &dcfRoot) +{ + dcfRoot.ref = startPage; + dcfRoot.title = title; + generateDcfSections(dcfRoot, outputDir() + "/" + fileBase + ".dcf", fileBase + "/reference"); +} + +void HtmlGenerator::generateIndex(const QString &fileBase, + const QString &url, + const QString &title) +{ + tre->generateIndex(outputDir() + "/" + 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 = tre->findFakeNodeByTitle("Porting To Qt 4"); + Atom *targetAtom = 0; + if (fakeNode && node->type() == Node::Class) { + QString oldName(node->name()); + targetAtom = tre->findTarget(oldName.replace("3", ""), + fakeNode); + } + + if (targetAtom) { + text << Atom(Atom::Link, linkForNode(fakeNode, node) + "#" + + 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); + } +} + +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 << "\" />\n"; +} + +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 { + out() << "</a>"; + } + } + inLink = false; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/htmlgenerator.h b/tools/qdoc3/htmlgenerator.h new file mode 100644 index 0000000..de64190 --- /dev/null +++ b/tools/qdoc3/htmlgenerator.h @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + htmlgenerator.h +*/ + +#ifndef HTMLGENERATOR_H +#define HTMLGENERATOR_H + +#include <qmap.h> +#include <qregexp.h> + +#include "codemarker.h" +#include "config.h" +#include "dcfsection.h" +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +#if 0 +struct NavigationBar +{ + SectionIterator prev; + SectionIterator current; + SectionIterator next; +}; +#endif + +class HelpProjectWriter; + +class HtmlGenerator : public PageGenerator +{ + public: + HtmlGenerator(); + ~HtmlGenerator(); + + virtual void initializeGenerator(const Config& config); + virtual void terminateGenerator(); + virtual QString format(); + virtual void generateTree(const Tree *tree, CodeMarker *marker); + + static QString protect(const QString& string); + static QString cleanRef(const QString& ref); + + 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); + virtual QString refForNode(const Node *node); + virtual QString linkForNode(const Node *node, const Node *relative); + virtual QString refForAtom(Atom *atom, const Node *node); + + 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 QString& title, const Node *node = 0, + CodeMarker *marker = 0, bool mainPage = true); + 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); +#if 0 + void generateNavigationBar(const NavigationBar& bar, + const Node *node, + CodeMarker *marker); +#endif + void generateTableOfContents(const Node *node, + CodeMarker *marker, + Doc::SectioningUnit sectioningUnit, + int numColumns, + const Node *relative = 0); + QString generateListOfAllMemberFile(const InnerNode *inner, CodeMarker *marker); + QString generateLowStatusMemberFile(const InnerNode *inner, + CodeMarker *marker, + CodeMarker::Status status); + void generateClassHierarchy(const Node *relative, + CodeMarker *marker, + const QMap<QString,const Node *> &classMap); + void generateAnnotatedList(const Node *relative, + CodeMarker *marker, + const QMap<QString, const Node *> &nodeMap); + void generateCompactList(const Node *relative, + CodeMarker *marker, + const QMap<QString, const Node *> &classMap); + void generateFunctionIndex(const Node *relative, CodeMarker *marker); + void generateLegaleseList(const Node *relative, CodeMarker *marker); + void generateOverviewList(const Node *relative, CodeMarker *marker); + void generateSynopsis(const Node *node, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style); + void generateSectionList(const Section& section, + const Node *relative, + CodeMarker *marker, + CodeMarker::SynopsisStyle style); + void generateSectionInheritedList(const Section& section, + const Node *relative, + CodeMarker *marker); + 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); + QString highlightedCode(const QString& markedCode, CodeMarker *marker, const Node *relative); + QString fileBase(const Node *node); +#if 0 + QString fileBase(const Node *node, const SectionIterator& section); +#endif + 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); +#ifdef ZZZ_QDOC_QML + void findAllQmlClasses(const InnerNode *node); +#endif + static int hOffset(const Node *node); + static bool isThreeColumnEnumValueTable(const Atom *atom); + virtual QString getLink(const Atom *atom, + const Node *relative, + CodeMarker *marker, + const Node *node = 0); + virtual void generateDcf(const QString &fileBase, + const QString &startPage, + const QString &title, DcfSection &dcfRoot); + virtual void generateIndex(const QString &fileBase, + const QString &url, + const QString &title); + void generateMacRef(const Node *node, CodeMarker *marker); + void beginLink(const QString &link, + const Node *node, + const Node *relative, + CodeMarker *marker); + void endLink(); + +#if 0 + NavigationBar currentNavigationBar; +#endif + QMap<QString, QString> refMap; + int codeIndent; + DcfSection dcfClassesRoot; + DcfSection dcfOverviewsRoot; + DcfSection dcfExamplesRoot; + DcfSection dcfDesignerRoot; + DcfSection dcfLinguistRoot; + DcfSection dcfAssistantRoot; + DcfSection dcfQmakeRoot; + HelpProjectWriter *helpProjectWriter; + bool inLink; + bool inContents; + bool inSectionHeading; + bool inTableHeader; + int numTableRows; + bool threeColumnEnumValueTable; + QString link; + QStringList sectionNumber; + QRegExp funcLeftParen; + QString style; + QString postHeader; + QString footer; + QString address; + bool pleaseGenerateMacRef; + QString project; + QString projectDescription; + QString projectUrl; + QString navigationLinks; + QStringList stylesheets; + QStringList customHeadElements; + const Tree *tre; + bool slow; + QMap<QString, QMap<QString, const Node *> > moduleClassMap; + QMap<QString, QMap<QString, const Node *> > moduleNamespaceMap; + QMap<QString, const Node *> nonCompatClasses; + QMap<QString, const Node *> mainClasses; + QMap<QString, const Node *> compatClasses; + QMap<QString, const Node *> namespaceIndex; + QMap<QString, const Node *> serviceClasses; +#ifdef QDOC_QML + QMap<QString, const Node *> qmlClasses; +#endif + QMap<QString, QMap<QString, const Node *> > funcIndex; + QMap<Text, const Node *> legaleseTexts; +}; + +#define HTMLGENERATOR_ADDRESS "address" +#define HTMLGENERATOR_FOOTER "footer" +#define HTMLGENERATOR_GENERATEMACREFS "generatemacrefs" // ### document me +#define HTMLGENERATOR_POSTHEADER "postheader" +#define HTMLGENERATOR_STYLE "style" +#define HTMLGENERATOR_STYLESHEETS "stylesheets" +#define HTMLGENERATOR_CUSTOMHEADELEMENTS "customheadelements" + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/jambiapiparser.cpp b/tools/qdoc3/jambiapiparser.cpp new file mode 100644 index 0000000..73d10a6 --- /dev/null +++ b/tools/qdoc3/jambiapiparser.cpp @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + jambiapiparser.cpp +*/ + +#include <QtXml> + +#include "cppcodeparser.h" +#include "jambiapiparser.h" +#include "node.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +static const char USED_INTERNALLY[] = ""; + +static Text textWithFixedBrief(const Text &text, const Text &beforeBrief, + const Text &afterBrief) +{ + Text result; + + const Atom *atom = text.firstAtom(); + while (atom) { + if (atom->type() == Atom::BriefLeft) { + result << Atom::ParaLeft << beforeBrief; + } else if (atom->type() == Atom::BriefRight) { + result << afterBrief << Atom::ParaRight; + } else { + result << *atom; + } + atom = atom->next(); + } + + return result; +} + +static void setPass1JambifiedDoc(Node *javaNode, const Node *cppNode, const QString &qName = "") +{ + Doc newDoc(cppNode->doc()); + + if (javaNode->type() == Node::Function) { + const FunctionNode *javaFunc = static_cast<const FunctionNode *>(javaNode); + if (cppNode->type() == Node::Function) { + const FunctionNode *cppFunc = static_cast<const FunctionNode *>(cppNode); + if (const PropertyNode *property = cppFunc->associatedProperty()) { + newDoc = property->doc(); + Text text(newDoc.body()); + + Node *mutableCppNode = const_cast<Node *>(cppNode); + if (property->getters().contains(mutableCppNode)) { + text = textWithFixedBrief(text, Text("Returns "), Text(".")); + } else if (property->setters().contains(mutableCppNode)) { + Text afterBrief; + if (javaFunc->parameterNames().count() == 1 + && !javaFunc->parameterNames().first().isEmpty()) { + afterBrief << " to " + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER) + << javaFunc->parameterNames().first() + << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER); + } + afterBrief << "."; + text = textWithFixedBrief(text, Text("Sets "), afterBrief); + } else if (property->resetters().contains(mutableCppNode)) { + text = textWithFixedBrief(text, Text("Resets "), Text(".")); + } + + newDoc.setBody(text); + } else { + QStringList javaParams = javaFunc->parameterNames(); + QStringList cppParams = cppFunc->parameterNames(); + newDoc.renameParameters(cppParams, javaParams); + + if (cppNode->access() == Node::Private) { + Text text; + text << Atom::ParaLeft; + if (cppFunc->reimplementedFrom()) { + text << "This function is reimplemented for internal reasons."; + } else { + text << USED_INTERNALLY; + } + text << Atom::ParaRight; + newDoc.setBody(text); + } + } + } else if (cppNode->type() == Node::Variable) { + Text text(newDoc.body()); + + if (qName == "variablegetter") { + text = textWithFixedBrief(text, Text("Returns "), Text(".")); + } else if (qName == "variablesetter") { + Text afterBrief; + if (javaFunc->parameterNames().count() == 1 + && !javaFunc->parameterNames().first().isEmpty()) { + afterBrief << " to " + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER) + << javaFunc->parameterNames().first() + << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER); + } + afterBrief << "."; + text = textWithFixedBrief(text, Text("Sets "), afterBrief); + } + + newDoc.setBody(text); + } + } else { // ### enum value names? + + } + + javaNode->setDoc(newDoc, true); +} + +static void setStatus(Node *javaNode, const Node *cppNode) +{ + if (cppNode->status() == Node::Compat) { + javaNode->setStatus(Node::Obsolete); + } else { + javaNode->setStatus(cppNode->status()); + } +} + +static Text findEnumText(Node *javaEnum, const QString &enumItemName) +{ + const Text &body = javaEnum->doc().body(); + const Atom *atom = body.firstAtom(); + while (atom) { + if (atom->type() == Atom::ListTagLeft && atom->string() == ATOM_LIST_VALUE) { + atom = atom->next(); + if (atom) { + // ### paras? + if (atom->string() == enumItemName) + return body.subText(Atom::ListItemLeft, Atom::ListItemRight, atom); + } + } else { + atom = atom->next(); + } + } + return Text(); +} + +JambiApiParser::JambiApiParser(Tree *cppTree) + : cppTre(cppTree), javaTre(0), metJapiTag(false) +{ +} + +JambiApiParser::~JambiApiParser() +{ +} + +void JambiApiParser::initializeParser(const Config &config) +{ + CodeParser::initializeParser(config); +} + +void JambiApiParser::terminateParser() +{ + CodeParser::terminateParser(); +} + +QString JambiApiParser::language() +{ + return "Java"; +} + +QString JambiApiParser::sourceFileNameFilter() +{ + return "*.japi"; +} + +void JambiApiParser::parseSourceFile(const Location &location, const QString &filePath, Tree *tree) +{ + javaTre = tree; + metJapiTag = false; + + QXmlSimpleReader reader; + reader.setContentHandler(this); + reader.setErrorHandler(this); + + QFile file(filePath); + if (!file.open(QFile::ReadOnly)) { + location.warning(tr("Cannot open JAPI file '%1'").arg(filePath)); + return; + } + + japiLocation = Location(filePath); + QXmlInputSource xmlSource(&file); + reader.parse(xmlSource); +} + +void JambiApiParser::doneParsingSourceFiles(Tree * /* tree */) +{ + /* + Also import the overview documents. + */ + foreach (Node *cppNode, cppTre->root()->childNodes()) { + if (cppNode->type() == Node::Fake) { + FakeNode *cppFake = static_cast<FakeNode *>(cppNode); + if (cppFake->subType() == FakeNode::Page) { + FakeNode *javaFake = new FakeNode(javaTre->root(), cppFake->name(), + cppFake->subType()); + javaFake->setModuleName("com.trolltech.qt"); // ### hard-coded + javaFake->setTitle(cppFake->title()); + javaFake->setSubTitle(cppFake->subTitle()); + setStatus(javaFake, cppFake); + setPass1JambifiedDoc(javaFake, cppFake); + } + } + } + + /* + Fix the docs. + */ + if (javaTre) { + javaTre->resolveInheritance(); + jambifyDocsPass2(javaTre->root()); + javaTre = 0; + } +} + +bool JambiApiParser::startElement(const QString & /* namespaceURI */, + const QString & /* localName */, + const QString &qName, + const QXmlAttributes &attributes) +{ + if (!metJapiTag && qName != "japi") { + // ### The file is not a JAPI file. + return true; + } + metJapiTag = true; + + EnumNode *javaEnum = 0; + EnumNode *cppEnum = 0; + InnerNode *javaParent = javaTre->root(); + InnerNode *cppParent = cppTre->root(); + + for (int i = 0; i < classAndEnumStack.count(); ++i) { + const ClassOrEnumInfo &info = classAndEnumStack.at(i); + if (info.cppNode) { + if (info.cppNode->type() == Node::Enum) { + Q_ASSERT(info.javaNode->type() == Node::Enum); + javaEnum = static_cast<EnumNode *>(info.javaNode); + cppEnum = static_cast<EnumNode *>(info.cppNode); + } else { + Q_ASSERT(info.javaNode->type() == Node::Class + || info.javaNode->type() == Node::Namespace); + javaParent = static_cast<InnerNode *>(info.javaNode); + cppParent = static_cast<InnerNode *>(info.cppNode); + } + } + } + + if (qName == "class" || qName == "enum") { + Node::Type type = (qName == "class") ? Node::Class : Node::Enum; + + QString javaExtends = attributes.value("java-extends"); + QString javaImplements = attributes.value("javaimplements"); + + ClassOrEnumInfo info; + info.tag = qName; + info.javaName = attributes.value("java"); + info.cppName = attributes.value("cpp"); + info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent); + if (!info.cppNode && type == Node::Class) { + type = Node::Namespace; + info.cppNode = cppTre->findNode(info.cppName.split("::"), type, cppParent); + } + + if (!info.cppNode) { + japiLocation.warning(tr("Cannot find C++ class or enum '%1'").arg(info.cppName)); + } else { + if (qName == "class") { + ClassNode *javaClass = new ClassNode(javaParent, info.javaName); + javaClass->setModuleName(attributes.value("package")); + if (!javaExtends.isEmpty()) + javaTre->addBaseClass(javaClass, Node::Public, javaExtends.split('.'), + javaExtends); + if (!javaImplements.isEmpty()) + javaTre->addBaseClass(javaClass, Node::Public, javaImplements.split('.'), + javaExtends); + + info.javaNode = javaClass; + } else { + info.javaNode = new EnumNode(javaParent, info.javaName); + } + info.javaNode->setLocation(japiLocation); + setStatus(info.javaNode, info.cppNode); + + setPass1JambifiedDoc(info.javaNode, info.cppNode); + } + classAndEnumStack.push(info); + } else if (qName == "method" || qName == "signal") { + QString javaSignature = attributes.value("java"); + if (javaSignature.startsWith("private")) + return true; + + QString cppSignature = attributes.value("cpp"); + + CppCodeParser cppParser; + const FunctionNode *cppNode = cppParser.findFunctionNode(cppSignature, cppTre, + cppParent, + true /* fuzzy */); + if (!cppNode) { + bool quiet = false; + + /* + Default constructors sometimes don't exist in C++. + */ + if (!quiet && javaSignature == "public " + javaParent->name() + "()") + quiet = true; + + if (!quiet) + japiLocation.warning(tr("Cannot find C++ function '%1' ('%2')") + .arg(cppSignature).arg(cppParent->name())); + } + + FunctionNode *javaNode; + if (makeFunctionNode(javaParent, javaSignature, &javaNode)) { + javaNode->setLocation(japiLocation); + if (qName == "signal") + javaNode->setMetaness(FunctionNode::Signal); + + if (cppNode) { + setStatus(javaNode, cppNode); + + int overloadNo = cppNode->parameters().count() - javaNode->parameters().count() + 1; + if (overloadNo == 1) { + setPass1JambifiedDoc(javaNode, cppNode); + } else { + Text text; + + text << Atom::ParaLeft << "Equivalent to " + << Atom(Atom::Link, javaNode->name() + "()") + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) + << javaNode->name() + << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) + << "("; + + for (int i = 0; i < cppNode->parameters().count(); ++i) { + if (i > 0) + text << ", "; + if (i < javaNode->parameters().count()) { + text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_PARAMETER) + << javaNode->parameters().at(i).name() + << Atom(Atom::FormattingRight, ATOM_FORMATTING_PARAMETER); + } else { + // ### convert to Java + text << cppNode->parameters().at(i).defaultValue(); + } + } + + text << ")."; + + Doc doc; + doc.setBody(text); + javaNode->setDoc(doc, true); + } + javaNode->setOverload(overloadNo > 1); + } + } + } else if (qName == "variablesetter" || qName == "variablegetter") { + QString javaSignature = attributes.value("java"); + if (javaSignature.startsWith("private")) + return true; + + QString cppVariable = attributes.value("cpp"); + + VariableNode *cppNode = static_cast<VariableNode *>(cppParent->findNode(cppVariable, + Node::Variable)); + FunctionNode *javaNode; + if (makeFunctionNode(javaParent, javaSignature, &javaNode)) { + javaNode->setLocation(japiLocation); + + if (!cppNode) { +#if 0 + japiLocation.warning(tr("Cannot find C++ variable '%1' ('%2')") + .arg(cppVariable).arg(cppParent->name())); +#endif + javaNode->setDoc(Doc(japiLocation, japiLocation, + USED_INTERNALLY, + QSet<QString>()), true); + } else { + setPass1JambifiedDoc(javaNode, cppNode, qName); + setStatus(javaNode, cppNode); + } + } + } else if (qName == "enum-value") { + QString javaName = attributes.value("java"); + QString cppName = attributes.value("cpp"); + QString value = attributes.value("value"); + + if (javaEnum) { + EnumItem item(javaName, value, findEnumText(javaEnum, javaName)); + javaEnum->addItem(item); + } + } + + return true; +} + +bool JambiApiParser::endElement(const QString & /* namespaceURI */, + const QString & /* localName */, + const QString &qName) +{ + if (qName == "class" || qName == "enum") + classAndEnumStack.pop(); + return true; +} + +bool JambiApiParser::fatalError(const QXmlParseException &exception) +{ + japiLocation.setLineNo(exception.lineNumber()); + japiLocation.setColumnNo(exception.columnNumber()); + japiLocation.warning(tr("Syntax error in JAPI file (%1)").arg(exception.message())); + return true; +} + +void JambiApiParser::jambifyDocsPass2(Node *node) +{ + const Doc &doc = node->doc(); + if (!doc.isEmpty()) { + if (node->type() == Node::Enum) { + Doc newDoc(doc); + newDoc.simplifyEnumDoc(); + node->setDoc(newDoc, true); + } + } + + if (node->isInnerNode()) { + InnerNode *innerNode = static_cast<InnerNode *>(node); + foreach (Node *child, innerNode->childNodes()) + jambifyDocsPass2(child); + } +} + +bool JambiApiParser::makeFunctionNode(InnerNode *parent, const QString &synopsis, + FunctionNode **funcPtr) +{ + Node::Access access = Node::Public; + FunctionNode::Metaness metaness = FunctionNode::Plain; + bool final = false; + bool statique = false; + + QString mySynopsis = synopsis.simplified(); + int oldLen; + do { + oldLen = mySynopsis.length(); + + if (mySynopsis.startsWith("public ")) { + mySynopsis.remove(0, 7); + access = Node::Public; + } + if (mySynopsis.startsWith("protected ")) { + mySynopsis.remove(0, 10); + access = Node::Protected; + } + if (mySynopsis.startsWith("private ")) { + mySynopsis.remove(0, 8); + access = Node::Private; + } + if (mySynopsis.startsWith("native ")) { + mySynopsis.remove(0, 7); + metaness = FunctionNode::Native; + } + if (mySynopsis.startsWith("final ")) { + mySynopsis.remove(0, 6); + final = true; + } + if (mySynopsis.startsWith("static ")) { + mySynopsis.remove(0, 7); + statique = true; + } + } while (oldLen != mySynopsis.length()); + + // method or constructor + QRegExp funcRegExp("(?:(.*) )?([A-Za-z_0-9]+)\\((.*)\\)"); + if (!funcRegExp.exactMatch(mySynopsis)) + return false; + + QString retType = funcRegExp.cap(1); + QString funcName = funcRegExp.cap(2); + QStringList params = funcRegExp.cap(3).split(","); + + FunctionNode *func = new FunctionNode(parent, funcName); + func->setReturnType(retType); + func->setAccess(access); + func->setStatic(statique); + func->setConst(final); + func->setMetaness(metaness); + + QRegExp paramRegExp(" ?([^ ].*) ([A-Za-z_0-9]+) ?"); + + foreach (const QString ¶m, params) { + if (paramRegExp.exactMatch(param)) { + func->addParameter(Parameter(paramRegExp.cap(1), "", paramRegExp.cap(2))); + } else { + // problem + } + } + + if (funcPtr) { + *funcPtr = func; + } else if (!parent) { + delete func; + } + return true; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/jambiapiparser.h b/tools/qdoc3/jambiapiparser.h new file mode 100644 index 0000000..e0dfe3d --- /dev/null +++ b/tools/qdoc3/jambiapiparser.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + jambiapiparser.h +*/ + +#ifndef JAMBIAPIPARSER_H +#define JAMBIAPIPARSER_H + +#include <QStack> +#include <QXmlDefaultHandler> + +#include "codeparser.h" + +QT_BEGIN_NAMESPACE + +struct ClassOrEnumInfo +{ + QString tag; + QString javaName; + QString cppName; + Node *javaNode; + Node *cppNode; + + ClassOrEnumInfo() : javaNode(0), cppNode(0) {} +}; + +class JambiApiParser : public CodeParser, private QXmlDefaultHandler +{ +public: + JambiApiParser(Tree *cppTree); + ~JambiApiParser(); + + void initializeParser(const Config &config); + void terminateParser(); + QString language(); + QString sourceFileNameFilter(); + void parseSourceFile(const Location &location, const QString &filePath, Tree *tree); + virtual void doneParsingSourceFiles(Tree *tree); + +private: + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &attributes); + bool endElement(const QString &namespaceURI, const QString &localName, + const QString &qName); + bool fatalError(const QXmlParseException &exception); + void jambifyDocsPass2(Node *node); + bool makeFunctionNode(InnerNode *parent, const QString &synopsis, FunctionNode **funcPtr); + + Tree *cppTre; + Tree *javaTre; + + bool metJapiTag; + Location japiLocation; + QStack<ClassOrEnumInfo> classAndEnumStack; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/javacodemarker.cpp b/tools/qdoc3/javacodemarker.cpp new file mode 100644 index 0000000..813f505 --- /dev/null +++ b/tools/qdoc3/javacodemarker.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + javacodemarker.cpp +*/ + +#include "javacodemarker.h" +#include "node.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +JavaCodeMarker::JavaCodeMarker() +{ +} + +JavaCodeMarker::~JavaCodeMarker() +{ +} + +bool JavaCodeMarker::recognizeCode( const QString& /* code */ ) +{ + return true; +} + +bool JavaCodeMarker::recognizeExtension( const QString& ext ) +{ + return ext == "java"; +} + +bool JavaCodeMarker::recognizeLanguage( const QString& lang ) +{ + return lang == "Java"; +} + +QString JavaCodeMarker::plainName( const Node *node ) +{ + return node->name(); +} + +QString JavaCodeMarker::plainFullName( const Node *node, const Node * /* relative */ ) +{ + if (!node) + return QString(); + + QString fullName; + for ( ;; ) { + fullName.prepend( plainName(node) ); + if ( node->parent() && node->parent()->name().isEmpty() ) + break; + node = node->parent(); + if (!node) + break; + fullName.prepend("."); + } + return fullName; +} + +QString JavaCodeMarker::markedUpCode( const QString& code, + const Node * /* relative */, + const QString& /* dirPath */ ) +{ + return protect( code ); +} + +QString JavaCodeMarker::markedUpSynopsis(const Node * /* node */, + const Node * /* relative */, + SynopsisStyle /* style */) +{ + return QString(); +} + +QString JavaCodeMarker::markedUpName( const Node *node ) +{ + return linkTag(node, taggedNode(node)); +} + +QString JavaCodeMarker::markedUpFullName(const Node *node, const Node * /* relative */ ) +{ + QString fullName; + for ( ;; ) { + fullName.prepend( markedUpName(node) ); + if ( node->parent()->name().isEmpty() ) + break; + node = node->parent(); + fullName.prepend( "." ); + } + return fullName; +} + +QString JavaCodeMarker::markedUpEnumValue(const QString &enumValue, + const Node * /* relative */) +{ + return protect(enumValue); +} + +QString JavaCodeMarker::markedUpIncludes( const QStringList& /* includes */ ) +{ + return QString(); +} + +QString JavaCodeMarker::functionBeginRegExp( const QString& /* funcName */) +{ + return "^x$"; // ### invalid regexp +} + +QString JavaCodeMarker::functionEndRegExp( const QString& /* funcName */ ) +{ + return "^}"; +} + +QList<Section> JavaCodeMarker::sections(const InnerNode * /* inner */, SynopsisStyle /* style */, + Status /* status */) +{ + return QList<Section>(); +} + +const Node *JavaCodeMarker::resolveTarget(const QString &target, const Tree *tree, + const Node *relative) +{ + if (target.endsWith("()")) { + const FunctionNode *func; + QString funcName = target; + funcName.chop(2); + + QStringList path = funcName.split('.'); + if ((func = tree->findFunctionNode(path, relative, Tree::SearchBaseClasses))) + return func; + } else if (target.contains("#")) { + int hashAt = target.indexOf("#"); + 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; + if ((node = tree->findNode(path, relative, + Tree::SearchBaseClasses | Tree::SearchEnumValues))) + return node; + } + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/javacodemarker.h b/tools/qdoc3/javacodemarker.h new file mode 100644 index 0000000..3381fc3 --- /dev/null +++ b/tools/qdoc3/javacodemarker.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + javacodemarker.h +*/ + +#ifndef JAVACODEMARKER_H +#define JAVACODEMARKER_H + +#include "codemarker.h" + +QT_BEGIN_NAMESPACE + +class JavaCodeMarker : public CodeMarker +{ +public: + JavaCodeMarker(); + ~JavaCodeMarker(); + + bool recognizeCode( const QString& code ); + bool recognizeExtension( const QString& ext ); + bool recognizeLanguage( const QString& lang ); + QString plainName(const Node *node); + QString plainFullName(const Node *node, const Node *relative); + QString markedUpCode( const QString& code, const Node *relative, + const QString& dirPath ); + 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 ); + QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status); + QString functionBeginRegExp( const QString& funcName ); + QString functionEndRegExp( const QString& funcName ); + const Node *resolveTarget( const QString& target, const Tree *tree, const Node *relative ); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/javadocgenerator.cpp b/tools/qdoc3/javadocgenerator.cpp new file mode 100644 index 0000000..872699d --- /dev/null +++ b/tools/qdoc3/javadocgenerator.cpp @@ -0,0 +1,453 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "javadocgenerator.h" + +QT_BEGIN_NAMESPACE + +enum JavaSignatureSyntax { + GeneratedJdocFile, + JavadocRef, + SlotSignature +}; + +static QString javaSignature(const FunctionNode *func, JavaSignatureSyntax syntax, + int maxParams = 65535) +{ + maxParams = qMin(maxParams, func->parameters().count()); + + QString result; + + if (syntax == GeneratedJdocFile) { + if (func->access() == Node::Public) { + result += "public "; + } else if (func->access() == Node::Protected) { + result += "protected "; + } else { + result += "private "; + } + + if (func->metaness() == FunctionNode::Native) + result += "native "; + + if (func->isConst()) + result += "final "; + + // ### func->metaness() == FunctionNode::Abstract + + if (func->isStatic()) + result += "static "; + + if (!func->returnType().isEmpty()) { + result += func->returnType(); + result += ' '; + } + } + + if (syntax == SlotSignature) { + result += "void mySlot"; + } else { + result += func->name(); + } + result += '('; + for (int i = 0; i < maxParams; ++i) { + if (i != 0) + result += ", "; + result += func->parameters().at(i).leftType(); + if (syntax != JavadocRef) { + result += ' '; + result += func->parameters().at(i).name(); + } + } + result += ')'; + + return result; +} + +static QString packageName(const Node *node) +{ + while (node && node->type() != Node::Class && node->type() != Node::Fake) + node = node->parent(); + if (!node) + return QString(); + return node->moduleName(); +} + +JavadocGenerator::JavadocGenerator() + : oldDevice(0), currentDepth(0) +{ +} + +JavadocGenerator::~JavadocGenerator() +{ +} + +void JavadocGenerator::initializeGenerator(const Config &config) +{ + HtmlGenerator::initializeGenerator(config); + + formattingLeftMap().insert(ATOM_FORMATTING_PARAMETER, + formattingLeftMap().value(ATOM_FORMATTING_TELETYPE)); + formattingRightMap().insert(ATOM_FORMATTING_PARAMETER, + formattingRightMap().value(ATOM_FORMATTING_TELETYPE)); +} + +void JavadocGenerator::terminateGenerator() +{ + HtmlGenerator::terminateGenerator(); +} + +QString JavadocGenerator::format() +{ + return "javadoc"; +} + +void JavadocGenerator::generateTree(const Tree *tree, CodeMarker *marker) +{ + HtmlGenerator::generateTree(tree, marker); +} + +QString JavadocGenerator::fileExtension(const Node *node) +{ + if (node->type() == Node::Fake) { + return "html"; + } else { + return "jdoc"; + } +} + +QString JavadocGenerator::typeString(const Node *node) +{ + if (node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + return func->metaness() == FunctionNode::Signal ? "signal" : "method"; + } else { + return HtmlGenerator::typeString(node); + } +} + +QString JavadocGenerator::imageFileName(const Node *relative, const QString& fileBase) +{ + QString result = HtmlGenerator::imageFileName(relative, fileBase); + if (!result.isEmpty()) { + QString package = packageName(relative); + int numSubPackages = package.count('.') - 2; + while (numSubPackages > 0) { + result.prepend("%2E%2E/"); // javadoc 1.5.0_06 chokes on '../' + --numSubPackages; + } + } + return result; +} + +static int textDepth = 0; + +void JavadocGenerator::startText(const Node *relative, CodeMarker *marker) +{ + if (textDepth++ == 0 && relative->type() != Node::Fake) { + Q_ASSERT(!oldDevice); + oldDevice = out().device(); + Q_ASSERT(oldDevice); + out().setString(&buffer); + } + HtmlGenerator::startText(relative, marker); +} + +void JavadocGenerator::endText(const Node *relative, CodeMarker *marker) +{ + HtmlGenerator::endText(relative, marker); + if (--textDepth == 0 && relative->type() != Node::Fake) { + Q_ASSERT(oldDevice); + out().setDevice(oldDevice); + oldDevice = 0; + + /* + Need to escape XML metacharacters in .jdoc files. + */ + buffer.replace("*/", "*<!-- noop -->/"); + buffer.replace("&", "&"); + buffer.replace("\"", """); + buffer.replace("<", "<"); + buffer.replace(">", ">"); + out() << buffer; + buffer.clear(); + } +} + +int JavadocGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker) +{ + return HtmlGenerator::generateAtom(atom, relative, marker); +} + +void JavadocGenerator::generateClassLikeNode(const InnerNode *inner, CodeMarker *marker) +{ + generateIndent(); + out() << "<class name=\"" << protect(inner->name()) << "\""; + generateDoc(inner, marker); + out() << ">\n"; + + ++currentDepth; + foreach (Node *node, inner->childNodes()) { + if (node->isInnerNode()) { + generateClassLikeNode(static_cast<InnerNode *>(node), marker); + } else { + if (node->type() == Node::Enum) { + EnumNode *enume = static_cast<EnumNode *>(node); + + generateIndent(); + out() << "<enum name=\"" << protect(node->name()) << "\""; + generateDoc(node, marker); + out() << ">\n"; + + ++currentDepth; + const QList<EnumItem> &items = enume->items(); + for (int i = 0; i < items.count(); ++i) { + const EnumItem &item = items.at(i); + generateIndent(); + out() << "<enum-value name=\"" << protect(item.name()) << "\""; + generateEnumItemDoc(item.text(), enume, marker); + out() << "/>\n"; + } + --currentDepth; + + out() << "</enum>\n"; + } else if (node->type() == Node::Function) { + FunctionNode *func = static_cast<FunctionNode *>(node); + generateIndent(); + out() << (func->metaness() == FunctionNode::Signal ? "<signal" : "<method") + << " name=\"" + << protect(javaSignature(func, GeneratedJdocFile)) + << "\""; + generateDoc(node, marker); + out() << "/>\n"; + } + } + } + --currentDepth; + + generateIndent(); + out() << "</class>\n"; +} + +void JavadocGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) +{ + HtmlGenerator::generateFakeNode(fake, marker); +} + +void JavadocGenerator::generateText(const Text& text, const Node *relative, CodeMarker *marker) +{ + HtmlGenerator::generateText(text, relative, marker); +} + +void JavadocGenerator::generateBody(const Node *node, CodeMarker *marker) +{ + generateText(node->doc().body(), node, marker); +} + +void JavadocGenerator::generateAlsoList( const Node *node, CodeMarker *marker ) +{ + QList<Text> alsoList = node->doc().alsoList(); + supplementAlsoList(node, alsoList); + + if (node->type() == Node::Fake + || (node->type() == Node::Function + && static_cast<const FunctionNode *>(node)->metaness() == FunctionNode::Signal)) { + Text text; + + if (!alsoList.isEmpty()) { + text << Atom(Atom::ListLeft, ATOM_LIST_TAG) + << Atom(Atom::ListTagLeft, ATOM_LIST_TAG) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD) + << "See Also:" + << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom(Atom::ListTagRight, ATOM_LIST_TAG) + << Atom(Atom::ListItemLeft, ATOM_LIST_TAG); + + for (int i = 0; i < alsoList.count(); ++i) { + if (i != 0) + text << ", "; + text << alsoList.at(i); + } + text << Atom(Atom::ListItemRight, ATOM_LIST_TAG) + << Atom(Atom::ListRight, ATOM_LIST_TAG); + } + + generateText(text, node, marker); + } else { + foreach (const Text &text, alsoList) { + out() << "\n@see "; + generateText(text, node, marker); + } + } +} + +QString JavadocGenerator::refForNode( const Node *node ) +{ + if (node->type() == Node::Function) + return javaSignature(static_cast<const FunctionNode *>(node), JavadocRef); + + return HtmlGenerator::refForNode(node); +} + +QString JavadocGenerator::linkForNode( const Node *node, const Node *relative ) +{ + // ### EVIL, relative should never be null + if (!relative) + relative = node; + + if (packageName(node).isEmpty()) { + // ### jasmin: Fixme + return QString(); + } + + QString result; + if (node->type() == Node::Fake) { + result = node->name(); + } else { + if (!node->isInnerNode()) { + result = linkForNode(node->parent(), relative) + "#" + refForNode(node); + } else { + result = node->name() + ".html"; + } + } + + QStringList nodePackage = packageName(node).split("."); + QStringList relativePackage = packageName(relative).split("."); + if (nodePackage == QStringList(QString()) || relativePackage == QStringList(QString())) { + qWarning("I'm in trouble [%s][%s]", qPrintable(node->name()), qPrintable(relative->name())); + return QString(); + } + + int i = nodePackage.count() - 1; + while (nodePackage.value(i) != relativePackage.value(i)) { + result.prepend(nodePackage.at(i) + "/"); + --i; + } + + ++i; + while (i < relativePackage.count()) { + result.prepend("%2E%2E/"); // javadoc 1.5.0_06 chokes on '../' + ++i; + } + + return result; +} + +QString JavadocGenerator::refForAtom(Atom *atom, const Node *node) +{ + return HtmlGenerator::refForAtom(atom, node); +} + +/* + Neutralize dumb functions called from HtmlGenerator. +*/ +void JavadocGenerator::generateDcf(const QString & /* fileBase */, const QString & /* startPage */, + const QString & /* title */, DcfSection & /* dcfRoot */) +{ +} + +void JavadocGenerator::generateIndex(const QString & /* fileBase */, const QString & /* url */, + const QString & /* title */) +{ +} + +void JavadocGenerator::generateIndent() +{ + for (int i = 0; i < currentDepth; ++i) + out() << " "; +} + +void JavadocGenerator::generateDoc(const Node *node, CodeMarker *marker) +{ + const Text &text = node->doc().body(); + if (!text.isEmpty()) { + out() << " doc=\"/**\n"; + Generator::generateStatus(node, marker); + generateText(text, node, marker); + if (node && node->type() == Node::Function) { + const FunctionNode *func = static_cast<const FunctionNode *>(node); + if (func->metaness() == FunctionNode::Signal) { + QStringList slotSignatures; + for (int i = func->parameters().count(); i >= 0; --i) + slotSignatures += javaSignature(func, SlotSignature, i); + + Text text; + + text << Atom(Atom::ListLeft, ATOM_LIST_TAG) + << Atom(Atom::ListTagLeft, ATOM_LIST_TAG) + << Atom(Atom::FormattingLeft, ATOM_FORMATTING_BOLD); + + if (slotSignatures.count() == 1) { + text << "Compatible Slot Signature:"; + } else { + text << "Compatible Slot Signatures:"; + } + + text << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) + << Atom(Atom::ListTagRight, ATOM_LIST_TAG); + + for (int i = 0; i < slotSignatures.count(); ++i) { + text << Atom(Atom::ListItemLeft, ATOM_LIST_TAG) + << Atom(Atom::C, marker->markedUpCode(slotSignatures.at(i), 0, "")) + << Atom(Atom::ListItemRight, ATOM_LIST_TAG); + } + text << Atom(Atom::ListRight, ATOM_LIST_TAG); + generateText(text, node, marker); + } + } + if (node) + generateAlsoList(node, marker); + out() << " */\""; + } +} + +void JavadocGenerator::generateEnumItemDoc(const Text &text, const Node *node, CodeMarker *marker) +{ + out() << " doc=\"/**\n"; + if (text.isEmpty()) { + out() << "Internal."; + } else { + generateText(text, node, marker); + } + out() << " */\""; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/javadocgenerator.h b/tools/qdoc3/javadocgenerator.h new file mode 100644 index 0000000..aac938e --- /dev/null +++ b/tools/qdoc3/javadocgenerator.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef JAVADOCGENERATOR_H +#define JAVADOCGENERATOR_H + +#include "htmlgenerator.h" + +QT_BEGIN_NAMESPACE + +class JavadocGenerator : public HtmlGenerator +{ +public: + JavadocGenerator(); + ~JavadocGenerator(); + + void initializeGenerator(const Config &config); + void terminateGenerator(); + QString format(); + bool canHandleFormat(const QString &format) { return format == "HTML" || format == "javadoc"; } + void generateTree(const Tree *tree, CodeMarker *marker); + QString typeString(const Node *node); + QString imageFileName(const Node *relative, const QString &fileBase); + +protected: + QString fileExtension(const Node *node); + void startText( const Node *relative, CodeMarker *marker ); + void endText( const Node *relative, CodeMarker *marker ); + int generateAtom( const Atom *atom, const Node *relative, CodeMarker *marker ); + void generateClassLikeNode(const InnerNode *inner, CodeMarker *marker); + void generateFakeNode( const FakeNode *fake, CodeMarker *marker ); + + void generateText( const Text& text, const Node *relative, CodeMarker *marker ); + void generateBody( const Node *node, CodeMarker *marker ); + void generateAlsoList( const Node *node, CodeMarker *marker ); + + QString refForNode( const Node *node ); + QString linkForNode( const Node *node, const Node *relative ); + QString refForAtom(Atom *atom, const Node *node); + +private: + void generateDcf(const QString &fileBase, const QString &startPage, + const QString &title, DcfSection &dcfRoot); + void generateIndex(const QString &fileBase, const QString &url, + const QString &title); + void generateIndent(); + void generateDoc(const Node *node, CodeMarker *marker); + void generateEnumItemDoc(const Text &text, const Node *node, CodeMarker *marker); + + QString buffer; + QIODevice *oldDevice; + int currentDepth; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/linguistgenerator.cpp b/tools/qdoc3/linguistgenerator.cpp new file mode 100644 index 0000000..1350654 --- /dev/null +++ b/tools/qdoc3/linguistgenerator.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + linguistgenerator.cpp +*/ + +#include "codemarker.h" +#include "pagegenerator.h" +#include "linguistgenerator.h" +#include "node.h" +#include "separator.h" +#include "tree.h" +#include <ctype.h> + +#include <qlist.h> +#include <qiterator.h> + +QT_BEGIN_NAMESPACE + +#define COMMAND_VERSION Doc::alias("version") + +LinguistGenerator::LinguistGenerator() + : PageGenerator() +{ +} + +LinguistGenerator::~LinguistGenerator() +{ +} + +void LinguistGenerator::initializeGenerator(const Config &config) +{ + Generator::initializeGenerator(config); +} + +void LinguistGenerator::terminateGenerator() +{ + PageGenerator::terminateGenerator(); +} + +QString LinguistGenerator::format() +{ + return "Linguist"; +} + +QString LinguistGenerator::fileExtension(const Node * /* node */) +{ + return "ts"; +} + +void LinguistGenerator::generateClassLikeNode(const InnerNode *inner, CodeMarker *marker) +{ + out().setCodec("utf-8"); + + QDomDocument document("TS"); + QDomElement documentElement = document.createElement("TS"); + documentElement.setAttribute("version", "1.1"); + + QList<QDomElement> contextElements = generateIndexSections(document, inner, marker); + foreach (const QDomElement &element, contextElements) + documentElement.appendChild(element); + + QDomProcessingInstruction process = document.createProcessingInstruction( + "xml", QString("version=\"1.0\" encoding=\"%1\"").arg("utf-8")); + document.appendChild(process); + document.appendChild(documentElement); + + out() << document; + out().flush(); +} + +void LinguistGenerator::generateFakeNode( const FakeNode *fake, CodeMarker *marker ) +{ + out().setCodec("utf-8"); + + QDomDocument document("TS"); + QDomElement documentElement = document.createElement("TS"); + documentElement.setAttribute("version", "1.1"); + + QList<QDomElement> contextElements = generateIndexSections(document, fake, marker); + foreach (const QDomElement &element, contextElements) + documentElement.appendChild(element); + + QDomProcessingInstruction process = document.createProcessingInstruction( + "xml", QString("version=\"1.0\" encoding=\"%1\"").arg("utf-8")); + document.appendChild(process); + document.appendChild(documentElement); + + out() << document; + out().flush(); +} + +QList<QDomElement> LinguistGenerator::generateIndexSections( + QDomDocument &document, const Node *node, CodeMarker *marker) +{ + QList<QDomElement> contexts; + + if (node->isInnerNode()) { + const InnerNode *inner = static_cast<const InnerNode *>(node); + + foreach (const Node *child, inner->childNodes()) { + // Recurse to generate a DOM element for this child node and all + // its children. + contexts += generateIndexSections(document, child, marker); + } +/* + foreach (const Node *child, inner->relatedNodes()) { + QDomElement childElement = generateIndexSections(document, child, marker); + element.appendChild(childElement); + } +*/ + } + + // Add documentation to this node if it exists. + if (!node->doc().isEmpty()) { + + QString nodeName = fullName(node); + QString signature; + + if (node->type() == Node::Function) { + QStringList pieces; + const FunctionNode *functionNode = static_cast<const FunctionNode*>(node); + foreach (const Parameter ¶meter, functionNode->parameters()) { + QString typeString = parameter.leftType() + parameter.rightType(); + if (typeString.split(" ").size() > 1) + pieces.append(typeString + parameter.name()); + else + pieces.append(typeString + " " + parameter.name()); + } + signature = "(" + pieces.join(", ") + ")"; + } + + QDomElement contextElement = document.createElement("context"); + QDomElement nameElement = document.createElement("name"); + nameElement.appendChild(document.createTextNode(nodeName + signature)); + contextElement.appendChild(nameElement); + + QDomElement messageElement = document.createElement("message"); + contextElement.appendChild(messageElement); + + QDomElement sourceElement = document.createElement("source"); + QString sourceText = simplified(node->doc().source()); + if (!signature.isEmpty() && signature != "()" && !sourceText.contains("\\fn")) + sourceText.prepend(QString("\\fn %1%2\n").arg(nodeName).arg(signature)); + sourceElement.appendChild(document.createTextNode(sourceText)); + messageElement.appendChild(sourceElement); + + QDomElement translationElement = document.createElement("translation"); + translationElement.setAttribute("type", "unfinished"); + messageElement.appendChild(translationElement); + + QDomElement locationElement = document.createElement("location"); + locationElement.setAttribute("filename", node->doc().location().filePath()); + locationElement.setAttribute("line", node->doc().location().lineNo()); + messageElement.appendChild(locationElement); + + contexts.append(contextElement); + } + + return contexts; +} + +QString LinguistGenerator::fullName(const Node *node) const +{ + if (!node) + return ""; + else if (node->parent() && !node->parent()->name().isEmpty()) + return fullName(node->parent()) + "::" + node->name(); + else + return node->name(); +} + +QString LinguistGenerator::simplified(const QString &text) const +{ + QStringList lines = text.split("\n"); + + while (lines.size() > 0 && lines.first().trimmed().isEmpty()) + lines.pop_front(); + + while (lines.size() > 0 && lines.last().trimmed().isEmpty()) + lines.pop_back(); + + int min = 0; + bool set = false; + foreach (const QString &line, lines) { + int j = 0; + while (j < line.length()) { + if (line[j] != ' ') + break; + ++j; + } + if (j < line.length()) { + if (!set) { + min = j; + set = true; + } else + min = qMin(min, j); + } + } + for (int i = 0; i < lines.size(); ++i) + lines[i] = lines[i].mid(min); + + return lines.join("\n"); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/linguistgenerator.h b/tools/qdoc3/linguistgenerator.h new file mode 100644 index 0000000..cae7e33 --- /dev/null +++ b/tools/qdoc3/linguistgenerator.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + LinguistGenerator.h +*/ + +#ifndef LINGUISTGENERATOR_H +#define LINGUISTGENERATOR_H + +#include <qmap.h> +#include <qregexp.h> +#include <qdom.h> + +#include "codemarker.h" +#include "config.h" +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +class LinguistGenerator : public PageGenerator +{ +public: + LinguistGenerator(); + ~LinguistGenerator(); + + virtual void initializeGenerator( const Config& config ); + virtual void terminateGenerator(); + virtual QString format(); + +protected: + virtual void generateClassLikeNode(const InnerNode *inner, + CodeMarker *marker); + virtual void generateFakeNode( const FakeNode *fake, CodeMarker *marker ); + virtual QString fileExtension(const Node *node); + + QList<QDomElement> generateIndexSections(QDomDocument &document, + const Node *node, CodeMarker *marker); + virtual QString fullName(const Node *node) const; + +private: + QString simplified(const QString &text) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/location.cpp b/tools/qdoc3/location.cpp new file mode 100644 index 0000000..62a18f0 --- /dev/null +++ b/tools/qdoc3/location.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDebug> +#include "config.h" +#include "location.h" + +#include <qregexp.h> +#include <qtranslator.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("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 + "]"; + 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 0 + if (columnNo() >= 1) + str += ":" + QString::number(columnNo()); +#endif + } + if (etc()) + str += QLatin1String(" (etc.)"); + return str; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/location.h b/tools/qdoc3/location.h new file mode 100644 index 0000000..d8f3106 --- /dev/null +++ b/tools/qdoc3/location.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/tools/qdoc3/loutgenerator.cpp b/tools/qdoc3/loutgenerator.cpp new file mode 100644 index 0000000..0d1f646 --- /dev/null +++ b/tools/qdoc3/loutgenerator.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + loutgenerator.cpp +*/ + +#include "loutgenerator.h" + +QT_BEGIN_NAMESPACE + +LoutGenerator::LoutGenerator() +{ +} + +LoutGenerator::~LoutGenerator() +{ +} + +QString LoutGenerator::format() +{ + return "lout"; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/loutgenerator.h b/tools/qdoc3/loutgenerator.h new file mode 100644 index 0000000..0d6f5f1 --- /dev/null +++ b/tools/qdoc3/loutgenerator.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + loutgenerator.h +*/ + +#ifndef LOUTGENERATOR_H +#define LOUTGENERATOR_H + +#include "bookgenerator.h" + +QT_BEGIN_NAMESPACE + +class LoutGenerator : public BookGenerator +{ +public: + LoutGenerator(); + ~LoutGenerator(); + + virtual QString format(); + +protected: + // ### +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/main.cpp b/tools/qdoc3/main.cpp new file mode 100644 index 0000000..3e6f832 --- /dev/null +++ b/tools/qdoc3/main.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + main.cpp +*/ + +#include <QtCore> +#include <stdlib.h> +#include "apigenerator.h" +#include "codemarker.h" +#include "codeparser.h" +#include "config.h" +#include "cppcodemarker.h" +#include "cppcodeparser.h" +#include "cpptoqsconverter.h" +#include "doc.h" +#include "htmlgenerator.h" +#include "jambiapiparser.h" +#include "javacodemarker.h" +#include "javadocgenerator.h" +#include "linguistgenerator.h" +#include "loutgenerator.h" +#include "mangenerator.h" +#include "plaincodemarker.h" +#include "polyarchiveextractor.h" +#include "polyuncompressor.h" +#include "qsakernelparser.h" +#include "qscodemarker.h" +#include "qscodeparser.h" +#include "sgmlgenerator.h" +#include "webxmlgenerator.h" +#include "tokenizer.h" +#include "tree.h" + +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 slow = false; +static QStringList defines; +static QHash<QString, Tree *> trees; + +//static int doxygen = 0; + +/*! + Find the Tree for language \a lang and return a pointer to it. + If there is no Tree for language \a lang in the Tree table, add + a new one. The Tree table is indexed by \a lang strings. + */ +static Tree* treeForLanguage(const QString &lang) +{ + Tree* tree = trees.value(lang); + if (tree == 0) { + tree = new Tree; + trees.insert( lang, tree ); + } + return tree; +} + +/*! + 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" + " -slow " + "Turn on features that slow down qdoc") ); +} + +/*! + Prints the qdoc version number to stdout. + */ +static void printVersion() +{ + Location::information(tr("qdoc version 4.4.1")); +} + +/*! + Processes the qdoc config file \a fileName. This is the + controller for all of qdoc. + */ +static void processQdocconfFile(const QString &fileName) +{ + QList<QTranslator *> translators; + + /* + 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_SLOW, QStringList(slow ? "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); + + /* + 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); + CppToQsConverter::initialize(config); + CodeMarker::initialize(config); + CodeParser::initialize(config); + Generator::initialize(config); + + /* + 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; + } + + //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(); + +#ifdef QDOC2DOX + // qdoc -> doxygen + if (doxygen == 2) { + qDebug() << "READING anchors.txt"; + DoxWriter::readAnchors(); + qDebug() << "READING title maps"; + DoxWriter::readTitles(); + qDebug() << "READING member multimaps"; + DoxWriter::readMembers(); + } +#endif + + /* + 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)); + + /* + There must be a code parser for the source code language, e.g. C++. + If there isn't one, give up. + */ + CodeParser *codeParser = CodeParser::parserForLanguage(lang); + if (codeParser == 0) + config.lastLocation().fatal(tr("Cannot parse programming language '%1'").arg(lang)); + + /* + By default, the only output format is HTML. + */ + QSet<QString> outputFormats = config.getStringSet(CONFIG_OUTPUTFORMATS); + Location outputFormatsLocation = config.lastLocation(); + + /* + There must be a code marker for the source code language, e.g. C++. + If there isn't one, give up. + */ + CodeMarker *marker = CodeMarker::markerForLanguage(lang); + if (!marker && !outputFormats.isEmpty()) + langLocation.fatal(tr("Cannot output documentation for programming language '%1'").arg(lang)); + + /* + Read some XML indexes. What are they??? + */ + QStringList indexFiles = config.getStringList(CONFIG_INDEXES); + tree->readIndexes(indexFiles); + + /* + Get all the header files: "*.ch *.h *.h++ *.hh *.hpp *.hxx" + Put them in a set. + */ + QSet<QString> excludedDirs; + QStringList excludedDirsList = config.getStringList(CONFIG_EXCLUDEDIRS); + foreach (const QString &excludeDir, excludedDirsList) + excludedDirs.insert(QDir::fromNativeSeparators(excludeDir)); + QSet<QString> headers = QSet<QString>::fromList( + config.getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, + codeParser->headerFileNameFilter(), + excludedDirs)); + + /* + Parse each header file in the set and add it to the big tree. + */ + QSet<QString>::ConstIterator h = headers.begin(); + while (h != headers.end()) { + codeParser->parseHeaderFile(config.location(), *h, tree); + ++h; + } + codeParser->doneParsingHeaderFiles(tree); + + /* + Get all the source text files: "*.cpp *.qdoc *.mm" + Put them in a set. + */ + QSet<QString> sources = QSet<QString>::fromList( + config.getAllFiles(CONFIG_SOURCES, CONFIG_SOURCEDIRS, + codeParser->sourceFileNameFilter(), + excludedDirs)); + + /* + Parse each source text file in the set and add it to the big tree. + */ + QSet<QString>::ConstIterator s = sources.begin(); + while (s != sources.end()) { + codeParser->parseSourceFile(config.location(), *s, tree); + ++s; + } + 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->resolveTargets(); + +#ifdef QDOC2DOX + // qdoc -> doxygen + if (doxygen == 1) { + DoxWriter::writeAnchors(); + DoxWriter::writeTitles(); + DoxWriter::writeMembers(); + } + + if (doxygen == 0) { +#endif + + /* + Now the tree has been built, and all the stuff that needed + resolving has been resolved. Now it is time to traverse + the big tree and generate the documentation output. + */ + 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, marker); + ++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(); + +#ifdef QDOC2DOX + } +#endif + + CodeParser::terminate(); + CodeMarker::terminate(); + CppToQsConverter::terminate(); + Doc::terminate(); + Tokenizer::terminate(); + Location::terminate(); + QDir::setCurrent(prevCurrentDir); + + foreach (QTranslator *translator, translators) + delete translator; + + delete tree; +} + +QT_END_NAMESPACE + +int main(int argc, char **argv) +{ + QT_USE_NAMESPACE + + QCoreApplication app(argc, argv); + QString cf = "qsauncompress \1 \2"; + PolyArchiveExtractor qsaExtractor(QStringList() << "qsa",cf); + cf = "tar -C \2 -xf \1"; + PolyArchiveExtractor tarExtractor(QStringList() << "tar",cf); + cf = "tar -C \2 -Zxf \1"; + PolyArchiveExtractor tazExtractor(QStringList() << "taz",cf); + cf = "tar -C \2 -jxf \1"; + PolyArchiveExtractor tbz2Extractor(QStringList() << "tbz" << "tbz2",cf); + cf = "tar -C \2 -zxf \1"; + PolyArchiveExtractor tgzExtractor(QStringList() << "tgz",cf); + cf = "unzip \1 -d \2"; + PolyArchiveExtractor zipExtractor(QStringList() << "zip",cf); + cf = "bunzip2 -c \1 > \2"; + PolyUncompressor bz2Uncompressor(QStringList() << "bz" << "bz2",cf); + cf = "gunzip -c \1 > \2"; + PolyUncompressor gzAndZUncompressor(QStringList() << "gz" << "z" << "Z",cf); + cf = "unzip -c \1 > \2"; + PolyUncompressor zipUncompressor(QStringList() << "zip",cf); + + /* + Create code parsers for the languages to be parsed, + and create a tree for C++. + */ + CppCodeParser cppParser; + Tree *cppTree = treeForLanguage(cppParser.language()); + + QsCodeParser qsParser(cppTree); + QsaKernelParser qsaKernelParser(cppTree); + JambiApiParser jambiParser(cppTree); + + /* + Create code markers for plain text, C++, Java, and qs. + */ + PlainCodeMarker plainMarker; + CppCodeMarker cppMarker; + JavaCodeMarker javaMarker; + QsCodeMarker qsMarker; + + ApiGenerator apiGenerator; + HtmlGenerator htmlGenerator; + JavadocGenerator javadocGenerator; + LinguistGenerator linguistGenerator; + LoutGenerator loutGenerator; + ManGenerator manGenerator; + SgmlGenerator smglGenerator; + WebXMLGenerator webxmlGenerator; + + 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 == "-slow") { + slow = true; + } + +#ifdef QDOC2DOX + else if (opt == "-doxygen1") { + // qdoc -> doxygen + // Don't use this; it isn't ready yet. + // Now it's a fossil. + qDebug() << "doxygen pass 1"; + doxygen = 1; + DoxWriter::setDoxPass(1); + } + else if (opt == "-doxygen2") { + // qdoc -> doxygen + // Don't use this; it isn't ready yet. + // Now it's a fossil. + qDebug() << "doxygen pass 2"; + doxygen = 2; + DoxWriter::setDoxPass(2); + } +#endif + + else { + qdocFiles.append(opt); + } + } + + if (qdocFiles.isEmpty()) { + printHelp(); + return EXIT_FAILURE; + } + + /* + Main loop. + */ + foreach (QString qf, qdocFiles) + processQdocconfFile(qf); + + qDeleteAll(trees); + return EXIT_SUCCESS; +} + diff --git a/tools/qdoc3/mangenerator.cpp b/tools/qdoc3/mangenerator.cpp new file mode 100644 index 0000000..619a680 --- /dev/null +++ b/tools/qdoc3/mangenerator.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + mangenerator.cpp +*/ + +#include <qdatetime.h> +#include <qregexp.h> + +#include "mangenerator.h" +#include "node.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +ManGenerator::ManGenerator() +{ + date = QDate::currentDate().toString( "d MMMM yyyy" ); +} + +ManGenerator::~ManGenerator() +{ +} + +QString ManGenerator::format() +{ + return "man"; +} + +int ManGenerator::generateAtom( const Atom *atom, const Node * /* relative */, + CodeMarker * /* marker */ ) +{ +#if 0 + switch ( atom->type() ) { + case Atom::AbstractBegin: + break; + case Atom::AbstractEnd: + break; + case Atom::Alias: + break; + case Atom::AliasArg: + break; + case Atom::BaseName: + break; + case Atom::BriefBegin: + break; + case Atom::BriefEnd: + break; + case Atom::C: + break; + case Atom::CaptionBegin: + break; + case Atom::CaptionEnd: + break; + case Atom::CitationBegin: + break; + case Atom::CitationEnd: + break; + case Atom::Code: + break; + case Atom::FootnoteBegin: + break; + case Atom::FootnoteEnd: + break; + case Atom::FormatBegin: + break; + case Atom::FormatEnd: + break; + case Atom::GeneratedList: + break; + case Atom::Image: + break; + case Atom::ImageText: + break; + case Atom::Link: + break; + case Atom::LinkNode: + break; + case Atom::ListBegin: + break; + case Atom::ListItemNumber: + break; + case Atom::ListItemBegin: + out() << ".IP " << atom->string() << ".\n"; + break; + case Atom::ListItemEnd: + break; + case Atom::ListEnd: + break; + case Atom::Nop: + break; + case Atom::ParaBegin: + out() << ".PP\n"; + break; + case Atom::ParaEnd: + out() << "\n"; + break; + case Atom::RawFormat: + break; + case Atom::RawString: + break; + case Atom::SectionBegin: + break; + case Atom::SectionEnd: + break; + case Atom::SectionHeadingBegin: + break; + case Atom::SectionHeadingEnd: + break; + case Atom::SidebarBegin: + break; + case Atom::SidebarEnd: + break; + case Atom::String: + out() << protectTextLine( atom->string() ); + break; + case Atom::TableBegin: + break; + case Atom::TableEnd: + break; + case Atom::TableOfContents: + break; + case Atom::Target: + break; + case Atom::UnknownCommand: + ; + } +#endif + unknownAtom( atom ); + return 0; +} + +void ManGenerator::generateClassLikeNode( const InnerNode *classe, + CodeMarker *marker ) +{ + generateHeader( classe->name() ); + out() << ".SH NAME\n" + << classe->name() << "\n" + << ".SH SYNOPSYS\n"; + generateBody( classe, marker ); + generateFooter(); +} + +void ManGenerator::generateFakeNode( const FakeNode *fake, CodeMarker *marker ) +{ + generateHeader( "foo" ); + generateBody( fake, marker ); + generateFooter(); +} + +QString ManGenerator::fileExtension(const Node * /* node */) +{ + return "3qt"; +} + +void ManGenerator::generateHeader( const QString& name ) +{ + out() << ".TH " << protectArg( name ) + << " " << protectArg( "3qt" ) + << " " << protectArg( date ) + << " " << protectArg( "Nokia Corporation and/or its subsidiary(-ies)" ) + << " " << protectArg( "Qt Toolkit" ) << "\n"; +} + +void ManGenerator::generateFooter() +{ +} + +QString ManGenerator::protectArg( const QString& str ) +{ + for ( int i = 0; i < (int) str.length(); i++ ) { + if ( str[i] == ' ' || str[i].isSpace() ) { + QString quoted = str; + quoted.replace( "\"", "\"\"" ); + return "\"" + quoted + "\""; + } + } + return str; +} + +QString ManGenerator::protectTextLine( const QString& str ) +{ + QString t = str; + if ( t.startsWith(".") || t.startsWith("'") ) + t.prepend( "\\&" ); + return t; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/mangenerator.h b/tools/qdoc3/mangenerator.h new file mode 100644 index 0000000..0136c5d --- /dev/null +++ b/tools/qdoc3/mangenerator.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + mangenerator.h +*/ + +#ifndef MANGENERATOR_H +#define MANGENERATOR_H + +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +class ManGenerator : public PageGenerator +{ +public: + ManGenerator(); + ~ManGenerator(); + + virtual QString format(); + +protected: + virtual int generateAtom( const Atom *atom, const Node *relative, + CodeMarker *marker ); + virtual void generateClassLikeNode(const InnerNode *node, CodeMarker *marker); + virtual void generateFakeNode( const FakeNode *fake, CodeMarker *marker ); + virtual QString fileExtension(const Node *node); + +private: + void generateHeader( const QString& name ); + void generateFooter(); + QString protectArg( const QString& str ); + QString protectTextLine( const QString& str ); + + QString date; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/node.cpp b/tools/qdoc3/node.cpp new file mode 100644 index 0000000..231149e --- /dev/null +++ b/tools/qdoc3/node.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + node.cpp +*/ + +#include "node.h" + +QT_BEGIN_NAMESPACE + +/*! + \class Node + \brief A node in a Tree. + */ + +/*! + */ +Node::~Node() +{ + if (par) + par->removeChild(this); + if (rel) + rel->removeRelated(this); +} + +/*! + */ +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; +} + +/*! + */ +Node::Node(Type type, InnerNode *parent, const QString& name) + : typ(type), + acc(Public), + sta(Commendable), + saf(UnspecifiedSafeness), + par(parent), + rel(0), + nam(name) +{ + if (par) + par->addChild(this); +} + +/*! + */ +QString Node::url() const +{ + return u; +} + +/*! + */ +void Node::setUrl(const QString &url) +{ + u = url; +} + +/*! + */ +void Node::setRelates(InnerNode *pseudoParent) +{ + if (rel) + rel->removeRelated(this); + rel = pseudoParent; + pseudoParent->related.append(this); +} + +/*! + */ +void Node::setLink(LinkType linkType, const QString &link, const QString &desc) +{ + QPair<QString,QString> linkPair; + linkPair.first = link; + linkPair.second = desc; + linkMap[linkType] = linkPair; +} + +/*! + */ +Node::Status Node::inheritedStatus() const +{ + Status parentStatus = Commendable; + if (par) + parentStatus = par->inheritedStatus(); + return (Status)qMin((int)sta, (int)parentStatus); +} + +/*! + */ +Node::ThreadSafeness Node::threadSafeness() const +{ + if (par && saf == par->inheritedThreadSafeness()) + return UnspecifiedSafeness; + return saf; +} + +/*! + */ +Node::ThreadSafeness Node::inheritedThreadSafeness() const +{ + if (par && saf == UnspecifiedSafeness) + return par->inheritedThreadSafeness(); + return saf; +} + +/*! + */ +QString Node::fileBase() const +{ + QString base = name(); + if (base.endsWith(".html")) + base.chop(5); + base.replace(QRegExp("[^A-Za-z0-9]+"), " "); + base = base.trimmed(); + base.replace(" ", "-"); + return base.toLower(); +} + +/*! + \class InnerNode + */ + +/*! + */ +InnerNode::~InnerNode() +{ + deleteChildren(); + removeFromRelated(); +} + +/*! + */ +Node *InnerNode::findNode(const QString& name) +{ + Node *node = childMap.value(name); + if (node) + return node; + return primaryFunctionMap.value(name); +} + +/*! + */ +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; + } + } +} + +/*! + */ +FunctionNode *InnerNode::findFunctionNode(const QString& name) +{ + return static_cast<FunctionNode *>(primaryFunctionMap.value(name)); +} + +/*! + */ +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; +} + +/*! + */ +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; + } + } + } +} + +/*! + */ +void InnerNode::makeUndocumentedChildrenInternal() +{ + foreach (Node *child, childNodes()) { + if (child->doc().isEmpty()) { + child->setAccess(Node::Private); + child->setStatus(Node::Internal); + } + } +} + +/*! + */ +void InnerNode::normalizeOverloads() +{ + QMap<QString, Node *>::Iterator p1 = primaryFunctionMap.begin(); + while (p1 != primaryFunctionMap.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p1; + if (secondaryFunctionMap.contains(primaryFunc->name()) && + (primaryFunc->status() != Commendable || + primaryFunc->access() == Private)) { + + NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; + NodeList::ConstIterator s = secs.begin(); + while (s != secs.end()) { + FunctionNode *secondaryFunc = (FunctionNode *) *s; + + // Any non-obsolete, non-compatibility, non-private functions + // (i.e, visible functions) are preferable to the primary + // function. + + if (secondaryFunc->status() == Commendable && + secondaryFunc->access() != Private) { + + *p1 = secondaryFunc; + int index = secondaryFunctionMap[primaryFunc->name()].indexOf(secondaryFunc); + secondaryFunctionMap[primaryFunc->name()].replace(index, primaryFunc); + break; + } + ++s; + } + } + ++p1; + } + + QMap<QString, Node *>::ConstIterator p = primaryFunctionMap.begin(); + while (p != primaryFunctionMap.end()) { + FunctionNode *primaryFunc = (FunctionNode *) *p; + if (primaryFunc->isOverload()) + primaryFunc->ove = false; + if (secondaryFunctionMap.contains(primaryFunc->name())) { + NodeList& secs = secondaryFunctionMap[primaryFunc->name()]; + NodeList::ConstIterator s = secs.begin(); + while (s != secs.end()) { + FunctionNode *secondaryFunc = (FunctionNode *) *s; + if (!secondaryFunc->isOverload()) + secondaryFunc->ove = true; + ++s; + } + } + ++p; + } + + NodeList::ConstIterator c = childNodes().begin(); + while (c != childNodes().end()) { + if ((*c)->isInnerNode()) + ((InnerNode *) *c)->normalizeOverloads(); + ++c; + } +} + +/*! + */ +void InnerNode::removeFromRelated() +{ + while (!related.isEmpty()) { + Node *p = static_cast<Node *>(related.takeFirst()); + + if (p != 0 && p->relates() == this) p->clearRelated(); + } +} + +/*! + */ +void InnerNode::deleteChildren() +{ + qDeleteAll(children); +} + +/*! + Returns true. + */ +bool InnerNode::isInnerNode() const +{ + return true; +} + +/*! + */ +const Node *InnerNode::findNode(const QString& name) const +{ + InnerNode *that = (InnerNode *) this; + return that->findNode(name); +} + +/*! + */ +const Node *InnerNode::findNode(const QString& name, Type type) const +{ + InnerNode *that = (InnerNode *) this; + return that->findNode(name, type); +} + +/*! + */ +const FunctionNode *InnerNode::findFunctionNode(const QString& name) const +{ + InnerNode *that = (InnerNode *) this; + return that->findFunctionNode(name); +} + +/*! + */ +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; +} + +/*! + */ +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; + } +} + +/*! + */ +int InnerNode::numOverloads(const QString& funcName) const +{ + if (primaryFunctionMap.contains(funcName)) { + return secondaryFunctionMap[funcName].count() + 1; + } + else { + return 0; + } +} + +/*! + */ +NodeList InnerNode::overloads(const QString &funcName) const +{ + NodeList result; + Node *primary = primaryFunctionMap.value(funcName); + if (primary) { + result << primary; + result += secondaryFunctionMap[funcName]; + } + return result; +} + +/*! + */ +InnerNode::InnerNode(Type type, InnerNode *parent, const QString& name) + : Node(type, parent, name) +{ +} + +/*! + */ +void InnerNode::addInclude(const QString& include) +{ + inc.append(include); +} + +/*! + */ +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; +} + +/*! + */ +void InnerNode::addChild(Node *child) +{ + children.append(child); + if (child->type() == Function) { + FunctionNode *func = (FunctionNode *) child; + if (!primaryFunctionMap.contains(func->name())) { + primaryFunctionMap.insert(func->name(), func); + } + else { + NodeList &secs = secondaryFunctionMap[func->name()]; + secs.append(func); + } + } + else { + if (child->type() == Enum) + enumChildren.append(child); + childMap.insert(child->name(), child); + } +} + +/*! + */ +void InnerNode::removeChild(Node *child) +{ + children.removeAll(child); + enumChildren.removeAll(child); + if (child->type() == Function) { + QMap<QString, Node *>::Iterator prim = + primaryFunctionMap.find(child->name()); + NodeList& secs = secondaryFunctionMap[child->name()]; + if (*prim == child) { + if (secs.isEmpty()) { + primaryFunctionMap.remove(child->name()); + } + else { + primaryFunctionMap.insert(child->name(), secs.takeFirst()); + } + } + else { + secs.removeAll(child); + } + QMap<QString, Node *>::Iterator ent = childMap.find( child->name() ); + if ( *ent == child ) + childMap.erase( ent ); + } + else { + QMap<QString, Node *>::Iterator ent = childMap.find(child->name()); + if (*ent == child) + childMap.erase(ent); + } +} + +/*! + Find the module (QtCore, QtGui, etc.) to which the class belongs. + We do this by obtaining the full path to the header file's location + and examine everything between "src/" and the filename. This is + semi-dirty because we are assuming a particular directory structure. + + This function is only really useful if the class's module has not + been defined in the header file with a QT_MODULE macro or with an + \inmodule command in the documentation. +*/ +QString Node::moduleName() const +{ + if (!mod.isEmpty()) + return mod; + + QString path = location().filePath(); + QString pattern = QString("src") + QDir::separator(); + int start = path.lastIndexOf(pattern); + + if (start == -1) + return ""; + + QString moduleDir = path.mid(start + pattern.size()); + int finish = moduleDir.indexOf(QDir::separator()); + + if (finish == -1) + return ""; + + QString moduleName = moduleDir.left(finish); + + if (moduleName == "corelib") + return "QtCore"; + else if (moduleName == "uitools") + return "QtUiTools"; + else if (moduleName == "gui") + return "QtGui"; + else if (moduleName == "network") + return "QtNetwork"; + else if (moduleName == "opengl") + return "QtOpenGL"; + else if (moduleName == "qt3support") + return "Qt3Support"; + else if (moduleName == "svg") + return "QtSvg"; + else if (moduleName == "sql") + return "QtSql"; + else if (moduleName == "qtestlib") + return "QtTest"; + else if (moduleDir.contains("webkit")) + return "QtWebKit"; + else if (moduleName == "xml") + return "QtXml"; + else + return ""; +} + +/*! + */ +void InnerNode::removeRelated(Node *pseudoChild) +{ + related.removeAll(pseudoChild); +} + +/*! + \class LeafNode + */ + +/*! + Returns false because this is an InnerNode. + */ +bool LeafNode::isInnerNode() const +{ + return false; +} + +/*! + */ +LeafNode::LeafNode(Type type, InnerNode *parent, const QString& name) + : Node(type, parent, name) +{ +} + +/*! + \class NamespaceNode + */ + +/*! + */ +NamespaceNode::NamespaceNode(InnerNode *parent, const QString& name) + : InnerNode(Namespace, parent, name) +{ +} + +/*! + \class ClassNode + */ + +/*! + */ +ClassNode::ClassNode(InnerNode *parent, const QString& name) + : InnerNode(Class, parent, name) +{ + hidden = false; +} + +/*! + */ +void ClassNode::addBaseClass(Access access, + ClassNode *node, + const QString &dataTypeWithTemplateArgs) +{ + bas.append(RelatedClass(access, node, dataTypeWithTemplateArgs)); + node->der.append(RelatedClass(access, this)); +} + +/*! + */ +void ClassNode::fixBaseClasses() +{ + int i; + + i = 0; + while (i < bas.size()) { + ClassNode *baseClass = bas.at(i).node; + if (baseClass->access() == Node::Private) { + bas.removeAt(i); + + const QList<RelatedClass> &basesBases = baseClass->baseClasses(); + for (int j = basesBases.size() - 1; j >= 0; --j) + bas.insert(i, basesBases.at(j)); + } + else { + ++i; + } + } + + i = 0; + while (i < der.size()) { + ClassNode *derivedClass = der.at(i).node; + if (derivedClass->access() == Node::Private) { + der.removeAt(i); + + const QList<RelatedClass> &dersDers = + derivedClass->derivedClasses(); + for (int j = dersDers.size() - 1; j >= 0; --j) + der.insert(i, dersDers.at(j)); + } + else { + ++i; + } + } +} + +/*! + \class FakeNode + */ + +/*! + */ +FakeNode::FakeNode(InnerNode *parent, const QString& name, SubType subType) + : InnerNode(Fake, parent, name), sub(subType) +{ +} + +/*! + */ +QString FakeNode::fullTitle() const +{ + if (sub == File) { + if (title().isEmpty()) + return name().mid(name().lastIndexOf('/') + 1) + " Example File"; + else + return title(); + } + else if (sub == HeaderFile) { + if (title().isEmpty()) + return name(); + else + return name() + " - " + title(); + } + else { + return title(); + } +} + +/*! + */ +QString FakeNode::subTitle() const +{ + if (!stle.isEmpty()) + return stle; + + if (sub == File) { + if (title().isEmpty() && name().contains("/")) + return name(); + } + return QString(); +} + +/*! + \class EnumNode + */ + +/*! + */ +EnumNode::EnumNode(InnerNode *parent, const QString& name) + : LeafNode(Enum, parent, name), ft(0) +{ +} + +/*! + */ +void EnumNode::addItem(const EnumItem& item) +{ + itms.append(item); + names.insert(item.name()); +} + +/*! + */ +Node::Access EnumNode::itemAccess(const QString &name) const +{ + if (doc().omitEnumItemNames().contains(name)) { + return Private; + } + else { + 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 + */ + +/*! + */ +Parameter::Parameter(const QString& leftType, + const QString& rightType, + const QString& name, + const QString& defaultValue) + : lef(leftType), rig(rightType), nam(name), def(defaultValue) +{ +} + +/*! + */ +Parameter::Parameter(const Parameter& p) + : lef(p.lef), rig(p.rig), nam(p.nam), def(p.def) +{ +} + +/*! + */ +Parameter& Parameter::operator=(const Parameter& p) +{ + lef = p.lef; + rig = p.rig; + nam = p.nam; + def = p.def; + return *this; +} + +/*! + \class FunctionNode + */ + +/*! + */ +FunctionNode::FunctionNode(InnerNode *parent, const QString& name) + : LeafNode(Function, parent, name), met(Plain), vir(NonVirtual), + con(false), sta(false), ove(false), rf(0), ap(0) +{ +} + +/*! + */ +void FunctionNode::setOverload(bool overlode) +{ + parent()->setOverload(this, overlode); + ove = overlode; +} + +/*! + */ +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; + } +} + +/*! + */ +void FunctionNode::setReimplementedFrom(FunctionNode *from) +{ + rf = from; + from->rb.append(this); +} + +/*! + */ +void FunctionNode::setAssociatedProperty(PropertyNode *property) +{ + ap = property; +} + +/*! + */ +int FunctionNode::overloadNumber() const +{ + return parent()->overloadNumber(this); +} + +/*! + */ +int FunctionNode::numOverloads() const +{ + return parent()->numOverloads(name()); +} + +/*! + */ +QStringList FunctionNode::parameterNames() const +{ + QStringList names; + QList<Parameter>::ConstIterator p = parameters().begin(); + while (p != parameters().end()) { + names << (*p).name(); + ++p; + } + return names; +} + +/*! + \class PropertyNode + */ + +/*! + */ +PropertyNode::PropertyNode(InnerNode *parent, const QString& name) + : LeafNode(Property, parent, name), + sto(Trool_Default), + des(Trool_Default), + overrides(0) +{ +} + +/*! + */ +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; + overrides = baseProperty; +} + +/*! + */ +QString PropertyNode::qualifiedDataType() const +{ + if (setters().isEmpty() && resetters().isEmpty()) { + if (dt.contains("*") || dt.contains("&")) { + // 'QWidget *' becomes 'QWidget *' const + return dt + " const"; + } + else { + /* + 'int' becomes 'const int' ('int const' is + correct C++, but looks wrong) + */ + return "const " + dt; + } + } + else { + return dt; + } +} + +/*! + */ +PropertyNode::Trool PropertyNode::toTrool(bool boolean) +{ + return boolean ? Trool_True : Trool_False; +} + +/*! + */ +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; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/node.h b/tools/qdoc3/node.h new file mode 100644 index 0000000..c34b82b --- /dev/null +++ b/tools/qdoc3/node.h @@ -0,0 +1,587 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 InnerNode; + +class Node +{ + public: + enum Type { + Namespace, + Class, + Fake, + Enum, + Typedef, + Function, + Property, + Variable, + Target + }; + + enum Access { Public, Protected, Private }; + + enum Status { + Compat, + Obsolete, + Deprecated, + Preliminary, + Commendable, + Main, + Internal + }; // don't reorder thisw enum + + enum ThreadSafeness { + UnspecifiedSafeness, + NonReentrant, + Reentrant, + ThreadSafe + }; + + enum LinkType { + StartLink, + NextLink, + PreviousLink, + ContentsLink, + IndexLink /*, + GlossaryLink, + CopyrightLink, + ChapterLink, + SectionLink, + SubsectionLink, + AppendixLink */ + }; + + virtual ~Node(); + + void setAccess(Access access) { acc = access; } + void setLocation(const Location& location) { loc = location; } + void setDoc(const Doc& doc, bool replace = false); + void setStatus(Status status) { sta = status; } + void setThreadSafeness(ThreadSafeness safeness) { saf = safeness; } + void setSince(const QString &since) { sinc = 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) { tpl = templateStuff; } + + virtual bool isInnerNode() const = 0; + Type type() const { return typ; } + InnerNode *parent() const { return par; } + InnerNode *relates() const { return rel; } + const QString& name() const { return nam; } + QMap<LinkType, QPair<QString,QString> > links() const { return linkMap; } + QString moduleName() const; + QString url() const; + + Access access() const { return acc; } + const Location& location() const { return loc; } + const Doc& doc() const { return d; } + Status status() const { return sta; } + Status inheritedStatus() const; + ThreadSafeness threadSafeness() const; + ThreadSafeness inheritedThreadSafeness() const; + QString since() const { return sinc; } + QString templateStuff() const { return tpl; } + + void clearRelated() { rel = 0; } + + QString fileBase() const; + + protected: + Node(Type type, InnerNode *parent, const QString& name); + + private: +#ifdef Q_WS_WIN + Type typ; + Access acc; + Status sta; + ThreadSafeness saf; +#else + Type typ : 4; + Access acc : 2; + Status sta : 3; + ThreadSafeness saf : 2; +#endif + InnerNode *par; + InnerNode *rel; + QString nam; + Location loc; + Doc d; + QMap<LinkType, QPair<QString, QString> > linkMap; + QString mod; + QString u; + QString sinc; + QString tpl; +}; + +class FunctionNode; +class EnumNode; + +typedef QList<Node *> NodeList; + +class InnerNode : public Node +{ + public: + ~InnerNode(); + + Node *findNode(const QString& name); + Node *findNode(const QString& name, Type type); + 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 deleteChildren(); + void removeFromRelated(); + + virtual bool isInnerNode() const; + const Node *findNode(const QString& name) 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 overloadNumber(const FunctionNode *func) const; + int numOverloads(const QString& funcName) const; + NodeList overloads(const QString &funcName) const; + const QStringList& includes() const { return inc; } + + protected: + InnerNode(Type type, InnerNode *parent, const QString& name); + + private: + friend class Node; + + static bool isSameSignature(const FunctionNode *f1, const FunctionNode *f2); + void addChild(Node *child); + void removeChild(Node *child); + void removeRelated(Node *pseudoChild); + + 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 bool isInnerNode() const; + + protected: + LeafNode(Type type, InnerNode *parent, const QString& name); +}; + +class NamespaceNode : public InnerNode +{ + public: + NamespaceNode(InnerNode *parent, const QString& name); +}; + +class ClassNode; + +struct RelatedClass +{ + RelatedClass() { } + RelatedClass(Node::Access access0, + ClassNode* node0, + const QString& dataTypeWithTemplateArgs0 = "") + : access(access0), + node(node0), + dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0) { } + + Node::Access access; + ClassNode* node; + QString dataTypeWithTemplateArgs; +}; + +class ClassNode : public InnerNode +{ + public: + ClassNode(InnerNode *parent, const QString& name); + + void addBaseClass(Access access, + ClassNode *node, + const QString &dataTypeWithTemplateArgs = ""); + void fixBaseClasses(); + + const QList<RelatedClass> &baseClasses() const { return bas; } + const QList<RelatedClass> &derivedClasses() const { return der; } + + bool hideFromMainList() const { return hidden; } + void setHideFromMainList(bool value) { hidden = value; } + + QString serviceName() const { return sname; } + void setServiceName(const QString& value) { sname = value; } + + private: + QList<RelatedClass> bas; + QList<RelatedClass> der; + bool hidden; + QString sname; +}; + +class FakeNode : public InnerNode +{ + public: + enum SubType { + Example, + HeaderFile, + File, + Group, + Module, + Page, + ExternalPage, + QmlClass + }; + + FakeNode(InnerNode *parent, const QString& name, SubType subType); + + void setTitle(const QString &title) { tle = title; } + void setSubTitle(const QString &subTitle) { stle = subTitle; } + void addGroupMember(Node *node) { gr.append(node); } + + SubType subType() const { return sub; } + QString title() const { return tle; } + QString fullTitle() const; + QString subTitle() const; + const NodeList &groupMembers() const { return gr; } + + private: + SubType sub; + QString tle; + QString stle; + NodeList gr; +}; + +class QmlNode : public FakeNode +{ + public: + QmlNode(InnerNode *parent, const QString& name, const ClassNode* cn) + : FakeNode(parent, name, QmlClass), cnode(cn) { } + + const ClassNode* classNode() const { return cnode; } + + private: + const ClassNode* cnode; +}; + +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); + + 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); + + 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; } + + 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); + + void setReturnType(const QString& returnType) { rt = returnType; } + void setMetaness(Metaness metaness) { met = metaness; } + void setVirtualness(Virtualness virtualness) { vir = virtualness; } + void setConst(bool conste) { con = conste; } + void setStatic(bool statique) { sta = statique; } + void setOverload(bool overlode); + 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; } + int overloadNumber() const; + int numOverloads() const; + const QList<Parameter>& parameters() const { return params; } + QStringList parameterNames() const; + const FunctionNode *reimplementedFrom() const { return rf; } + const QList<FunctionNode *> &reimplementedBy() const { return rb; } + const PropertyNode *associatedProperty() const { return ap; } + + private: + void setAssociatedProperty(PropertyNode *property); + + friend class InnerNode; + friend class PropertyNode; + + QString rt; +#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; + QList<Parameter> params; + const FunctionNode *rf; + const PropertyNode *ap; + QList<FunctionNode *> rb; +}; + +class PropertyNode : public LeafNode +{ + public: + enum FunctionRole { Getter, Setter, Resetter }; + enum { NumFunctionRoles = Resetter + 1 }; + + PropertyNode(InnerNode *parent, const QString& name); + + void setDataType(const QString& dataType) { dt = dataType; } + void addFunction(FunctionNode *function, FunctionRole role); + void setStored(bool stored) { sto = toTrool(stored); } + void setDesignable(bool designable) { des = toTrool(designable); } + void setOverriddenFrom(const PropertyNode *baseProperty); + + const QString &dataType() const { return dt; } + 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); } + bool isStored() const { return fromTrool(sto, storedDefault()); } + bool isDesignable() const { return fromTrool(des, designableDefault()); } + const PropertyNode *overriddenFrom() const { return overrides; } + + private: + enum Trool { Trool_True, Trool_False, Trool_Default }; + + static Trool toTrool(bool boolean); + static bool fromTrool(Trool troolean, bool defaultValue); + + bool storedDefault() const { return true; } + bool designableDefault() const { return !setters().isEmpty(); } + + QString dt; + NodeList funcs[NumFunctionRoles]; + Trool sto; + Trool des; + 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 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); + + 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) +{ +} + +class TargetNode : public LeafNode +{ + public: + TargetNode(InnerNode *parent, const QString& name); + + virtual bool isInnerNode() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/openedlist.cpp b/tools/qdoc3/openedlist.cpp new file mode 100644 index 0000000..04d9871 --- /dev/null +++ b/tools/qdoc3/openedlist.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/tools/qdoc3/openedlist.h b/tools/qdoc3/openedlist.h new file mode 100644 index 0000000..0506e79 --- /dev/null +++ b/tools/qdoc3/openedlist.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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/tools/qdoc3/pagegenerator.cpp b/tools/qdoc3/pagegenerator.cpp new file mode 100644 index 0000000..39e286e --- /dev/null +++ b/tools/qdoc3/pagegenerator.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + pagegenerator.cpp +*/ + +#include <QtCore> +#include <qfile.h> +#include <qfileinfo.h> + +#include "pagegenerator.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +/*! + Nothing to do in the constructor. + */ +PageGenerator::PageGenerator() +{ + // nothing. +} + +/*! + The destructor + */ +PageGenerator::~PageGenerator() +{ + while (!outStreamStack.isEmpty()) + endSubPage(); +} + +/*! + This function is recursive. + */ +void PageGenerator::generateTree(const Tree *tree, CodeMarker *marker) +{ + generateInnerNode(tree->root(), marker); +} + +QString PageGenerator::fileBase(const Node *node) +{ + if (node->relates()) + node = node->relates(); + else if (!node->isInnerNode()) + node = node->parent(); + + QString base = node->doc().baseName(); + if (!base.isEmpty()) + return base; + + const Node *p = node; + + forever { + base.prepend(p->name()); + const Node *pp = p->parent(); + if (!pp || pp->name().isEmpty() || pp->type() == Node::Fake) + break; + base.prepend(QLatin1Char('-')); + p = pp; + } + + if (node->type() == Node::Fake) { +#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(" ", "-"); + // 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; +} + +QString PageGenerator::fileName(const Node *node) +{ + if (!node->url().isEmpty()) + return node->url(); + + QString name = fileBase(node); + name += QLatin1Char('.'); + name += fileExtension(node); + return name; +} + +QString PageGenerator::outFileName() +{ + return QFileInfo(static_cast<QFile *>(out().device())->fileName()).fileName(); +} + +void PageGenerator::beginSubPage(const Location& location, + const QString& fileName) +{ + QFile *outFile = new QFile(outputDir() + "/" + fileName); + if (!outFile->open(QFile::WriteOnly)) + location.fatal(tr("Cannot open output file '%1'") + .arg(outFile->fileName())); + QTextStream *out = new QTextStream(outFile); + out->setCodec("ISO-8859-1"); + outStreamStack.push(out); +} + +void PageGenerator::endSubPage() +{ + outStreamStack.top()->flush(); + delete outStreamStack.top()->device(); + delete outStreamStack.pop(); +} + +QTextStream &PageGenerator::out() +{ + return *outStreamStack.top(); +} + +/*! + Recursive writing of html files from the root \a node. + */ +void PageGenerator::generateInnerNode(const InnerNode *node, + CodeMarker *marker) +{ + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == FakeNode::ExternalPage) + return; + } + + if (node->parent() != 0) { + beginSubPage(node->location(), fileName(node)); + if (node->type() == Node::Namespace || node->type() == Node::Class) { + generateClassLikeNode(node, marker); + } + else if (node->type() == Node::Fake) { + const FakeNode* fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == FakeNode::QmlClass) { + qDebug() << "FILENAME:" << fileName(node); + } + 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, marker); + ++c; + } +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/pagegenerator.h b/tools/qdoc3/pagegenerator.h new file mode 100644 index 0000000..002d390 --- /dev/null +++ b/tools/qdoc3/pagegenerator.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 ClassNode; +class InnerNode; +class NamespaceNode; + +class PageGenerator : public Generator +{ + public: + PageGenerator(); + ~PageGenerator(); + + virtual void generateTree(const Tree *tree, CodeMarker *marker); + + protected: + virtual QString fileBase(const Node *node); + virtual QString fileExtension(const Node *node) = 0; + QString fileName(const Node *node); + QString outFileName(); + void beginSubPage(const Location& location, const QString& fileName); + void endSubPage(); + virtual void generateInnerNode(const InnerNode *node, CodeMarker *marker); + QTextStream& out(); + + private: + QStack<QTextStream *> outStreamStack; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/plaincodemarker.cpp b/tools/qdoc3/plaincodemarker.cpp new file mode 100644 index 0000000..5ed4bbe --- /dev/null +++ b/tools/qdoc3/plaincodemarker.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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; +} + +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 QString& /* dirPath */ ) +{ + 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>(); +} + +const Node *PlainCodeMarker::resolveTarget( const QString& /* target */, + const Tree * /* tree */, + const Node * /* relative */ ) +{ + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/plaincodemarker.h b/tools/qdoc3/plaincodemarker.h new file mode 100644 index 0000000..24a1dfe --- /dev/null +++ b/tools/qdoc3/plaincodemarker.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 ); + QString plainName( const Node *node ); + QString plainFullName( const Node *node, const Node *relative ); + QString markedUpCode( const QString& code, const Node *relative, const QString& dirPath ); + 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); + const Node *resolveTarget(const QString &target, const Tree *tree, const Node *relative); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/polyarchiveextractor.cpp b/tools/qdoc3/polyarchiveextractor.cpp new file mode 100644 index 0000000..b7fdfef --- /dev/null +++ b/tools/qdoc3/polyarchiveextractor.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + polyarchiveextractor.cpp +*/ + +#include "command.h" +#include "polyarchiveextractor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class PolyArchiveExtractor + + \brief The PolyArchiveExtractor class is a class for unpacking + archive files. + + This subclass of ArchiveExtractor contains a parameterized + command for doing the archive extraction. + + It has an extractArchive() function you call to do the + actual archive extraction. + */ + +/*! + The constructor takes the list of filename \a extensions, + which it passes to the base class, and the \a commandFormat, + which it stores locally. The \a commandFormat is a command + template string. + */ +PolyArchiveExtractor::PolyArchiveExtractor( const QStringList& extensions, + const QString& commandFormat ) + : ArchiveExtractor( extensions ), cmd( commandFormat ) +{ +} + +/*! + The destructor doesn't have to do anything. + */ +PolyArchiveExtractor::~PolyArchiveExtractor() +{ +} + +/*! + Call this function to do the actual archive extraction. It calls + the executeCommand() function to do the work. That's all it does. + */ +void PolyArchiveExtractor::extractArchive( const Location& location, + const QString& filePath, + const QString& outputDir ) +{ + executeCommand( location, cmd, QStringList() << filePath << outputDir ); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/polyarchiveextractor.h b/tools/qdoc3/polyarchiveextractor.h new file mode 100644 index 0000000..9c637da --- /dev/null +++ b/tools/qdoc3/polyarchiveextractor.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + polyarchiveextractor.h +*/ + +#ifndef POLYARCHIVEEXTRACTOR_H +#define POLYARCHIVEEXTRACTOR_H + +#include "archiveextractor.h" + +QT_BEGIN_NAMESPACE + +class PolyArchiveExtractor : public ArchiveExtractor +{ + public: + PolyArchiveExtractor(const QStringList& extensions, + const QString& commandFormat); + ~PolyArchiveExtractor(); + + virtual void extractArchive(const Location& location, + const QString& filePath, + const QString& outputDir); + + private: + QString cmd; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/polyuncompressor.cpp b/tools/qdoc3/polyuncompressor.cpp new file mode 100644 index 0000000..c08ede6 --- /dev/null +++ b/tools/qdoc3/polyuncompressor.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "command.h" +#include "polyuncompressor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class PolyUncompressor + + \brief The PolyUncompressor class is a class for uncompressing + compressed files. + + This subclass of Uncompressor contains a parameterized + command for doing the uncompression + + It has an uncompressFile() function you call to do the + actual uncompression. + */ + +/*! + The constructor takes the list of filename \a extensions, + which it passes to the base class, and the \a commandFormat, + which it stores locally. The \a commandFormat is a command + template string. + */ +PolyUncompressor::PolyUncompressor( const QStringList& extensions, + const QString& commandFormat ) + : Uncompressor( extensions ), cmd( commandFormat ) +{ +} + +/*! + The destructor doesn't have to do anything. + */ +PolyUncompressor::~PolyUncompressor() +{ +} + +/*! + From \a filePath, derive a file path for the uncompressed + file and return it. If it can't figure out what the file + path should be, it just concatenates ".out" to the + \a filePath and returns that. + */ +QString PolyUncompressor::uncompressedFilePath( const QString& filePath ) +{ + QStringList::ConstIterator e = fileExtensions().begin(); + while ( e != fileExtensions().end() ) { + QString dotExt = "." + *e; + if ( filePath.endsWith(dotExt) ) + return filePath.left( filePath.length() - dotExt.length() ); + ++e; + } + return filePath + ".out"; // doesn't really matter +} + +/*! + Call this function to do the actual uncompressing. It calls + the executeCommand() function to do the work. That's all it does. + */ +void PolyUncompressor::uncompressFile( const Location& location, + const QString& filePath, + const QString& outputFilePath ) +{ + executeCommand( location, cmd, + QStringList() << filePath << outputFilePath ); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/polyuncompressor.h b/tools/qdoc3/polyuncompressor.h new file mode 100644 index 0000000..162f672 --- /dev/null +++ b/tools/qdoc3/polyuncompressor.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + polyuncompressor.h +*/ + +#ifndef POLYUNCOMPRESSOR_H +#define POLYUNCOMPRESSOR_H + +#include "uncompressor.h" + +QT_BEGIN_NAMESPACE + +class PolyUncompressor : public Uncompressor +{ + public: + PolyUncompressor(const QStringList& extensions, + const QString& commandFormat); + ~PolyUncompressor(); + + virtual QString uncompressedFilePath(const QString& filePath); + virtual void uncompressFile(const Location& location, + const QString& filePath, + const QString& outputFilePath); + + private: + QString cmd; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/qdoc3.pro b/tools/qdoc3/qdoc3.pro new file mode 100644 index 0000000..3268585 --- /dev/null +++ b/tools/qdoc3/qdoc3.pro @@ -0,0 +1,108 @@ +DEFINES += QDOC2_COMPAT +#DEFINES += QT_NO_CAST_TO_ASCII +#DEFINES += QT_NO_CAST_FROM_ASCII + +QT = core xml +CONFIG += console +build_all:!build_pass { + CONFIG -= build_all + CONFIG += release +} +mac:CONFIG -= app_bundle +HEADERS += apigenerator.h \ + archiveextractor.h \ + atom.h \ + bookgenerator.h \ + ccodeparser.h \ + codechunk.h \ + codemarker.h \ + codeparser.h \ + command.h \ + config.h \ + cppcodemarker.h \ + cppcodeparser.h \ + cpptoqsconverter.h \ + dcfsection.h \ + doc.h \ + editdistance.h \ + generator.h \ + helpprojectwriter.h \ + htmlgenerator.h \ + jambiapiparser.h \ + javacodemarker.h \ + javadocgenerator.h \ + linguistgenerator.h \ + location.h \ + loutgenerator.h \ + mangenerator.h \ + node.h \ + openedlist.h \ + pagegenerator.h \ + plaincodemarker.h \ + polyarchiveextractor.h \ + polyuncompressor.h \ + qsakernelparser.h \ + qscodemarker.h \ + qscodeparser.h \ + quoter.h \ + separator.h \ + sgmlgenerator.h \ + text.h \ + tokenizer.h \ + tr.h \ + tree.h \ + uncompressor.h \ + webxmlgenerator.h +SOURCES += apigenerator.cpp \ + archiveextractor.cpp \ + atom.cpp \ + bookgenerator.cpp \ + ccodeparser.cpp \ + codechunk.cpp \ + codemarker.cpp \ + codeparser.cpp \ + command.cpp \ + config.cpp \ + cppcodemarker.cpp \ + cppcodeparser.cpp \ + cpptoqsconverter.cpp \ + dcfsection.cpp \ + doc.cpp \ + editdistance.cpp \ + generator.cpp \ + helpprojectwriter.cpp \ + htmlgenerator.cpp \ + jambiapiparser.cpp \ + javacodemarker.cpp \ + javadocgenerator.cpp \ + linguistgenerator.cpp \ + location.cpp \ + loutgenerator.cpp \ + mangenerator.cpp \ + main.cpp \ + node.cpp \ + openedlist.cpp \ + pagegenerator.cpp \ + plaincodemarker.cpp \ + polyarchiveextractor.cpp \ + polyuncompressor.cpp \ + qsakernelparser.cpp \ + qscodemarker.cpp \ + qscodeparser.cpp \ + quoter.cpp \ + separator.cpp \ + sgmlgenerator.cpp \ + text.cpp \ + tokenizer.cpp \ + tree.cpp \ + uncompressor.cpp \ + webxmlgenerator.cpp \ + yyindent.cpp + + +win32 { + QT_WINCONFIG = release + CONFIG(debug, debug|release) { + QT_WINCONFIG = debug + } +} diff --git a/tools/qdoc3/qsakernelparser.cpp b/tools/qdoc3/qsakernelparser.cpp new file mode 100644 index 0000000..b856957 --- /dev/null +++ b/tools/qdoc3/qsakernelparser.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qfile.h> + +#include "qsakernelparser.h" +#include "tokenizer.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +QsaKernelParser::QsaKernelParser( Tree *cppTree ) + : cppTre( cppTree ) +{ +} + +QsaKernelParser::~QsaKernelParser() +{ +} + +QString QsaKernelParser::language() +{ + return "QSA Kernel C++"; +} + +QString QsaKernelParser::sourceFileNameFilter() +{ + return "*.cpp"; +} + +void QsaKernelParser::parseSourceFile( const Location& location, + const QString& filePath, + Tree * /* tree */ ) +{ + FILE *in = fopen( QFile::encodeName(filePath), "r" ); + if ( in == 0 ) { + location.error( tr("Cannot open QSA kernel file '%1'").arg(filePath) ); + return; + } + + Location fileLocation( filePath ); + Tokenizer fileTokenizer( fileLocation, in ); + tokenizer = &fileTokenizer; + readToken(); + + QString ident; + QString className; + int delimDepth = 0; + + while ( tok != Tok_Eoi ) { + if ( tok == Tok_Ident ) { + ident = tokenizer->lexeme(); + readToken(); + if ( tok == Tok_Gulbrandsen && tokenizer->braceDepth() == 0 && + tokenizer->parenDepth() == 0 ) { + className = ident; + } else if ( ident.startsWith("add") && ident.endsWith("Member") && + tok == Tok_LeftParen ) { + bool isProperty = ident.endsWith( "VariableMember" ); + bool isStatic = ident.startsWith( "addStatic" ); + bool isWritable = !isStatic; + + readToken(); + if ( tok == Tok_String ) { + QString member = tokenizer->lexeme(); + member = member.mid( 1, member.length() - 2 ); + + readToken(); + if ( tok == Tok_Comma ) + readToken(); + if ( tok == Tok_Ident && tokenizer->lexeme() == "QSMember" ) + readToken(); + if ( tok == Tok_LeftParen ) { + delimDepth++; + readToken(); + } + + while ( tok != Tok_Eoi && tok != Tok_RightParen && + tok != Tok_Semicolon ) { + if ( tok == Tok_Ident ) { + ident = tokenizer->lexeme(); + if ( ident == "Custom" ) { + isProperty = true; + } else if ( ident == "AttributeNonWritable" ) { + isWritable = false; + } else if ( ident == "AttributeStatic" ) { + isStatic = true; + } + } + readToken(); + } + + ClassNode *classe = + (ClassNode *) cppTre->findNode( QStringList(className), + Node::Class ); + if ( classe == 0 ) { + classe = new ClassNode( cppTre->root(), className ); + classe->setLocation( tokenizer->location() ); + } + + if ( isProperty ) { + PropertyNode *property = new PropertyNode(classe, member); + property->setLocation( tokenizer->location() ); + property->setDataType( "Object" ); +#if 0 + property->setGetter( member ); + if ( isWritable ) { + QString setter = member; + setter[0] = setter[0].toUpper(); + setter.prepend( "set" ); + property->setSetter( setter ); + } +#endif + } else { + FunctionNode *func = new FunctionNode( classe, member ); + func->setLocation( tokenizer->location() ); + func->setAccess( FunctionNode::Public ); + func->setMetaness( FunctionNode::Slot ); + if ( member == "toLocaleString" || + member == "toString" ) { + func->setReturnType( "QString" ); + } else if ( member == "valueOf" ) { + func->setReturnType( "Object" ); + } else { + func->setReturnType( "Object" ); + func->addParameter( Parameter("...") ); + } + func->setStatic( false ); // ### + } + } + } + } else { + readToken(); + } + } + fclose( in ); +} + +void QsaKernelParser::doneParsingSourceFiles( Tree * /* tree */ ) +{ +} + +void QsaKernelParser::readToken() +{ + tok = tokenizer->getToken(); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/qsakernelparser.h b/tools/qdoc3/qsakernelparser.h new file mode 100644 index 0000000..59273cd --- /dev/null +++ b/tools/qdoc3/qsakernelparser.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + qsakernelparser.h +*/ + +#ifndef QSAKERNELPARSER_H +#define QSAKERNELPARSER_H + +#include "codeparser.h" + +QT_BEGIN_NAMESPACE + +class Tokenizer; + +class QsaKernelParser : public CodeParser +{ +public: + QsaKernelParser( Tree *cppTree ); + ~QsaKernelParser(); + + virtual QString language(); + virtual QString sourceFileNameFilter(); + virtual void parseSourceFile( const Location& location, + const QString& filePath, Tree *tree ); + virtual void doneParsingSourceFiles( Tree *tree ); + +private: + void readToken(); + + Tree *cppTre; + Tokenizer *tokenizer; + int tok; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/qscodemarker.cpp b/tools/qdoc3/qscodemarker.cpp new file mode 100644 index 0000000..ed89b60 --- /dev/null +++ b/tools/qdoc3/qscodemarker.cpp @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + qscodemarker.cpp +*/ + +#include "node.h" +#include "qscodemarker.h" + +QT_BEGIN_NAMESPACE + +QsCodeMarker::QsCodeMarker() +{ +} + +QsCodeMarker::~QsCodeMarker() +{ +} + +bool QsCodeMarker::recognizeCode( const QString& /* code */ ) +{ + return true; +} + +bool QsCodeMarker::recognizeExtension( const QString& ext ) +{ + return ext == "js" || ext == "qs"; +} + +bool QsCodeMarker::recognizeLanguage( const QString& lang ) +{ + return lang == "JavaScript" || lang == "Qt Script"; +} + +QString QsCodeMarker::plainName( const Node *node ) +{ + QString name = node->name(); + if ( node->type() == Node::Function ) + name += "()"; + return name; +} + +QString QsCodeMarker::plainFullName( const Node *node, const Node * /* relative */ ) +{ + QString fullName; + for ( ;; ) { + fullName.prepend( plainName(node) ); + if ( node->parent()->name().isEmpty() ) + break; + node = node->parent(); + fullName.prepend("."); + } + return fullName; +} + +QString QsCodeMarker::markedUpCode( const QString& code, + const Node * /* relative */, + const QString& /* dirPath */ ) +{ + return protect( code ); +} + +QString QsCodeMarker::markedUpSynopsis( const Node *node, + const Node * /* relative */, + SynopsisStyle style ) +{ + QString synopsis; + QStringList extras; + 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::Enum ) + name.prepend( taggedNode(node->parent()) + "." ); + + switch ( node->type() ) { + case Node::Class: + synopsis = "class " + name; + break; + case Node::Function: + { + const FunctionNode *func = (const FunctionNode *) node; + + synopsis = name; + + if ( style == SeparateList ) { + synopsis += "()"; + } else { + synopsis += " ("; + if ( !func->parameters().isEmpty() ) { + synopsis += " "; + int numOptional = 0; + QList<Parameter>::ConstIterator p = func->parameters().begin(); + while ( p != func->parameters().end() ) { + if ( !(*p).defaultValue().isEmpty() ) { + if ( p == func->parameters().begin() ) { + synopsis += "[ "; + } else { + synopsis += " [ , "; + } + numOptional++; + } else { + if ( p != func->parameters().begin() ) + synopsis += ", "; + } + if ( !(*p).name().isEmpty() ) + synopsis += "<@param>" + protect( (*p).name() ) + + "</@param> : "; + synopsis += protect( (*p).leftType() ); + ++p; + } + for ( int i = 0; i < numOptional; i++ ) + synopsis += " ]"; + synopsis += " "; + } + synopsis += ")"; + } + + if ( style != SeparateList && !func->returnType().isEmpty() ) + synopsis += " : " + protect( func->returnType() ); + + if ( style == Detailed && func->metaness() == FunctionNode::Signal ) + extras << "[signal]"; + } + break; + case Node::Property: + { + const PropertyNode *property = (const PropertyNode *) node; + + synopsis = name; + if ( style != SeparateList ) + synopsis += " : " + property->dataType(); + if ( style == Detailed && property->setters().isEmpty() ) + extras << "[read only]"; + } + break; + case Node::Enum: + { + /* + The letters A to F and X (upper- and lower-case) can + appear in a hexadecimal constant (e.g. 0x3F). + */ + QRegExp letterRegExp( "[G-WYZg-wyz_]" ); + const EnumNode *enume = (const EnumNode *) node; + + synopsis = name; + if ( style == Summary && !enume->items().isEmpty() ) { + synopsis += " : "; + QString comma; + QList<EnumItem>::ConstIterator it = enume->items().begin(); + while ( it != enume->items().end() ) { + if ( enume->itemAccess((*it).name()) == Node::Public ) { + synopsis += comma; + synopsis += (*it).name(); + if ( (*it).value().indexOf(letterRegExp) != -1 ) + synopsis += " = " + (*it).value(); + comma = ", "; + } + ++it; + } + } + } + break; + case Node::Namespace: + case Node::Typedef: + default: + synopsis = name; + } + + if ( style == Summary ) { + if ( node->status() == Node::Preliminary ) { + extras << "(preliminary)"; + } else if ( node->status() == Node::Deprecated ) { + extras << "(deprecated)"; + } else if ( node->status() == Node::Obsolete ) { + extras << "(obsolete)"; + } + } + + QString extra; + if ( !extras.isEmpty() ) + extra = "<@extra>" + extras.join(" ") + "</@extra>"; + return synopsis + extra; +} + +QString QsCodeMarker::markedUpName( const Node *node ) +{ + QString name = linkTag( node, taggedNode(node) ); + if ( node->type() == Node::Function ) + name += "()"; + return name; +} + +QString QsCodeMarker::markedUpFullName( const Node *node, + const Node * /* relative */ ) +{ + QString fullName; + for ( ;; ) { + fullName.prepend( markedUpName(node) ); + if ( node->parent()->name().isEmpty() ) + break; + node = node->parent(); + fullName.prepend( "<@op>.</@op>" ); + } + return fullName; +} + +QString QsCodeMarker::markedUpEnumValue(const QString & /* enumValue */, + const Node * /* relative */) +{ + return QString(); +} + +QString QsCodeMarker::markedUpIncludes( const QStringList& /* includes */ ) +{ + return QString(); +} + +QString QsCodeMarker::functionBeginRegExp( const QString& funcName ) +{ + return "^function[ \t].*\\b" + QRegExp::escape( funcName ); +} + +QString QsCodeMarker::functionEndRegExp( const QString& /* funcName */ ) +{ + return "^}"; +} + +QList<Section> QsCodeMarker::sections( const InnerNode *inner, SynopsisStyle style, Status status ) +{ + QList<Section> sections; + + if (inner->type() != Node::Class) + return sections; + + const ClassNode *classe = static_cast<const ClassNode *>(inner); + + if ( style == Summary ) { + FastSection enums(classe, "Enums", "enum", "enums"); + FastSection functions(classe, "Functions", "function", "functions"); + FastSection readOnlyProperties(classe, "Read-Only Properties", "property", "properties"); + FastSection signalz(classe, "Signals", "signal", "signals"); + FastSection writableProperties(classe, "Writable Properties", "property", "properties"); + + 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::Public ) { + if ( (*c)->type() == Node::Enum ) { + insert( enums, *c, style, status ); + } else if ( (*c)->type() == Node::Function ) { + const FunctionNode *func = (const FunctionNode *) *c; + if ( func->metaness() == FunctionNode::Signal ) { + insert( signalz, *c, style, status ); + } else { + insert( functions, *c, style, status ); + } + } else if ( (*c)->type() == Node::Property ) { + const PropertyNode *property = + (const PropertyNode *) *c; + if ( property->setters().isEmpty() ) { + insert( readOnlyProperties, *c, style, status ); + } else { + insert( writableProperties, *c, style, status ); + } + } + } + ++c; + } + + QList<RelatedClass>::ConstIterator r = ancestorClass->baseClasses().begin(); + while ( r != ancestorClass->baseClasses().end() ) { + stack.prepend( (*r).node ); + ++r; + } + } + append( sections, enums ); + append( sections, writableProperties ); + append( sections, readOnlyProperties ); + append( sections, functions ); + append( sections, signalz ); + } else if ( style == Detailed ) { + FastSection enums( classe, "Enum Documentation" ); + FastSection functionsAndSignals( classe, "Function and Signal Documentation" ); + FastSection properties( classe, "Property Documentation" ); + + NodeList::ConstIterator c = classe->childNodes().begin(); + while ( c != classe->childNodes().end() ) { + if ( (*c)->access() == Node::Public ) { + if ( (*c)->type() == Node::Enum ) { + insert( enums, *c, style, status ); + } else if ( (*c)->type() == Node::Function ) { + insert( functionsAndSignals, *c, style, status ); + } else if ( (*c)->type() == Node::Property ) { + insert( properties, *c, style, status ); + } + } + ++c; + } + append( sections, enums ); + append( sections, properties ); + append( sections, functionsAndSignals ); + } else { // ( style == SeparateList ) + FastSection all( classe ); + + 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::Public ) + 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 ); + } + return sections; +} + +const Node *QsCodeMarker::resolveTarget( const QString& /* target */, + const Tree * /* tree */, + const Node * /* relative */ ) +{ + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/qscodemarker.h b/tools/qdoc3/qscodemarker.h new file mode 100644 index 0000000..a283e88 --- /dev/null +++ b/tools/qdoc3/qscodemarker.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + qscodemarker.h +*/ + +#ifndef QSCODEMARKER_H +#define QSCODEMARKER_H + +#include "codemarker.h" + +QT_BEGIN_NAMESPACE + +class QsCodeMarker : public CodeMarker +{ +public: + QsCodeMarker(); + ~QsCodeMarker(); + + bool recognizeCode( const QString& code ); + bool recognizeExtension( const QString& ext ); + bool recognizeLanguage( const QString& lang ); + QString plainName(const Node *node); + QString plainFullName(const Node *node, const Node *relative); + QString markedUpCode( const QString& code, const Node *relative, + const QString& dirPath ); + 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 ); + QList<Section> sections(const InnerNode *innerNode, SynopsisStyle style, Status status); + QString functionBeginRegExp( const QString& funcName ); + QString functionEndRegExp( const QString& funcName ); + const Node *resolveTarget( const QString& target, const Tree *tree, const Node *relative ); +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/qscodeparser.cpp b/tools/qdoc3/qscodeparser.cpp new file mode 100644 index 0000000..1a8902e --- /dev/null +++ b/tools/qdoc3/qscodeparser.cpp @@ -0,0 +1,944 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + qscodeparser.cpp +*/ + +#include <qfile.h> +#include <qregexp.h> + +#include "config.h" +#include "qscodeparser.h" +#include "text.h" +#include "tokenizer.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +#define CONFIG_QUICK "quick" +#define CONFIG_REPLACES "replaces" + +#define COMMAND_BRIEF Doc::alias( "brief") +#define COMMAND_CODE Doc::alias( "code") +#define COMMAND_ENDCODE Doc::alias( "endcode") +#define COMMAND_ENDQUICKCODE Doc::alias( "endquickcode") +#define COMMAND_FILE Doc::alias( "file") +#define COMMAND_GROUP Doc::alias( "group") +#define COMMAND_MODULE Doc::alias( "module") +#define COMMAND_PAGE Doc::alias( "page") +#define COMMAND_QUICKCLASS Doc::alias( "quickclass") +#define COMMAND_QUICKCODE Doc::alias( "quickcode") +#define COMMAND_QUICKENUM Doc::alias( "quickenum") +#define COMMAND_QUICKFN Doc::alias( "quickfn") +#define COMMAND_QUICKIFY Doc::alias( "quickify") +#define COMMAND_QUICKPROPERTY Doc::alias( "quickproperty") +#define COMMAND_PROTECTED Doc::alias( "protected") +#define COMMAND_REPLACE Doc::alias( "replace") + +static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*"; + +QsCodeParser::QsCodeParser(Tree *cppTree) + : cppTre(cppTree), qsTre(0), replaceRegExp("/(.+)/([^/]*)/") +{ +} + +QsCodeParser::~QsCodeParser() +{ +} + +void QsCodeParser::initializeParser(const Config& config) +{ + CppCodeParser::initializeParser(config); + + nodeTypeMap.insert(COMMAND_QUICKCLASS, Node::Class); + nodeTypeMap.insert(COMMAND_QUICKENUM, Node::Enum); + nodeTypeMap.insert(COMMAND_QUICKPROPERTY, Node::Property); + nodeTypeMap.insert(COMMAND_QUICKFN, Node::Function); + + QString quickDotReplaces = CONFIG_QUICK + Config::dot + CONFIG_REPLACES; + QStringList replaces = config.getStringList(quickDotReplaces); + QStringList::ConstIterator r = replaces.begin(); + while (r != replaces.end()) { + if (replaceRegExp.exactMatch(*r)) { + QRegExp before(replaceRegExp.cap(1)); + before.setMinimal(true); + QString after = replaceRegExp.cap(2); + + if (before.isValid()) { + replaceBefores << before; + replaceAfters << after; + } + else { + config.lastLocation().warning( + tr("Invalid regular expression '%1'") + .arg(before.pattern())); + } + } + else { + config.lastLocation().warning(tr("Bad syntax in '%1'") + .arg(quickDotReplaces)); + } + ++r; + } +} + +void QsCodeParser::terminateParser() +{ + nodeTypeMap.clear(); + classesWithNoQuickDoc.clear(); + replaceBefores.clear(); + replaceAfters.clear(); + CppCodeParser::terminateParser(); +} + +QString QsCodeParser::language() +{ + return "Qt Script"; +} + +QString QsCodeParser::headerFileNameFilter() +{ + return "*"; +} + +QString QsCodeParser::sourceFileNameFilter() +{ + return "*.qs *.qsd"; +} + +void QsCodeParser::parseHeaderFile(const Location& location, + const QString& filePath, + Tree *tree) +{ + qsTre = tree; + + FILE *in = fopen(QFile::encodeName(filePath), "r"); + if (in == 0) { + location.error(tr("Cannot open Qt Script class list '%1'") + .arg(filePath)); + return; + } + + Location fileLocation(filePath); + Tokenizer fileTokenizer(fileLocation, in); + int tok = fileTokenizer.getToken(); + while (tok != Tok_Eoi) { + if (tok == Tok_Ident) { + ClassNode *quickClass = new ClassNode(qsTre->root(), + fileTokenizer.lexeme()); + quickClass->setLocation(fileTokenizer.location()); + } + else { + fileTokenizer.location().error(tr("Unexpected token '%1' in Qt" + " Script class list") + .arg(fileTokenizer.lexeme())); + break; + } + tok = fileTokenizer.getToken(); + } + fclose(in); +} + +void QsCodeParser::parseSourceFile(const Location& location, + const QString& filePath, + Tree *tree) +{ + qsTre = tree; + CppCodeParser::parseSourceFile(location, filePath, tree); +} + +void QsCodeParser::doneParsingHeaderFiles(Tree *tree) +{ + NodeList::ConstIterator c = tree->root()->childNodes().begin(); + while (c != tree->root()->childNodes().end()) { + if ((*c)->type() == Node::Class) + quickifyClass((ClassNode *) *c); + ++c; + } + cppTre->root()->deleteChildren(); // save memory + tree->resolveInheritance(); + tree->resolveProperties(); +} + +void QsCodeParser::doneParsingSourceFiles(Tree *tree) +{ + tree->root()->normalizeOverloads(); + + NodeList::ConstIterator c = tree->root()->childNodes().begin(); + while (c != tree->root()->childNodes().end()) { + if ((*c)->type() == Node::Class) { + QMap<QString, Node *>::ConstIterator cwnqd = + classesWithNoQuickDoc.find((*c)->name()); + if (cwnqd != classesWithNoQuickDoc.end()) { + (*cwnqd)->location().warning(tr("No '\\%1' documentation for" + " class '%2'") + .arg(COMMAND_QUICKCLASS) + .arg(cwnqd.key())); + (*cwnqd)->setDoc(Doc(), true); + } + } + ++c; + } + + // ### check which enum types are used +} + +FunctionNode *QsCodeParser::findFunctionNode(const QString& synopsis, + Tree *tree) +{ + QStringList parentPath; + FunctionNode *clone; + FunctionNode *func = 0; + + if (makeFunctionNode(synopsis, &parentPath, &clone)) { + func = tree->findFunctionNode(parentPath, clone); + delete clone; + } + return func; +} + +QSet<QString> QsCodeParser::topicCommands() +{ + return QSet<QString>() << COMMAND_FILE << COMMAND_GROUP << COMMAND_MODULE + << COMMAND_PAGE << COMMAND_QUICKCLASS + << COMMAND_QUICKENUM << COMMAND_QUICKFN + << COMMAND_QUICKPROPERTY; +} + +Node *QsCodeParser::processTopicCommand(const Doc& doc, + const QString& command, + const QString& arg) +{ + if (command == COMMAND_QUICKFN) { + QStringList parentPath; + FunctionNode *quickFunc = 0; + FunctionNode *clone; + + if (makeFunctionNode(arg, &parentPath, &clone)) { + FunctionNode *kernelFunc = findKernelFunction(parentPath, + clone->name()); + if (kernelFunc != 0) + kernelFunc->setAccess(Node::Private); + + quickFunc = qsTre->findFunctionNode(parentPath, clone); + if (quickFunc == 0 && kernelFunc != 0) { + quickFunc = new FunctionNode(kernelFunc->parent(), + kernelFunc->name()); + quickFunc->setLocation(kernelFunc->location()); + quickFunc->setReturnType(clone->returnType()); + quickFunc->setParameters(clone->parameters()); + } + + if (quickFunc == 0) { + doc.location().warning(tr("Cannot find '%1' specified with '\\%2'") + .arg(arg).arg(command)); + } + else { + quickFunc->setAccess(Node::Public); + QStringList qtParams = quickFunc->parameterNames(); + quickFunc->borrowParameterNames(clone); + QStringList quickParams = quickFunc->parameterNames(); + setQuickDoc(quickFunc, doc, qtParams, quickParams); + } + delete clone; + } + else { + doc.location().warning(tr("Cannot find '%1' specified with '\\%2'") + .arg(arg).arg(command)); + } + return 0; + } + else if (nodeTypeMap.contains(command)) { + QStringList subArgs = arg.split(" "); + QString dataType; + + if (subArgs.count() == 3 && subArgs[1] == ":") { + dataType = subArgs[2]; + } + else if (subArgs.count() != 1) { + doc.location().warning(tr("Invalid syntax in '\\%1'") + .arg(command)); + } + + QStringList path = subArgs[0].split("."); + Node *quickNode = qsTre->findNode(path, nodeTypeMap[command]); + if (quickNode == 0) { + doc.location().warning(tr("Cannot find '%1' specified with '\\%2'") + .arg(arg).arg(command)); + } + else { + setQuickDoc(quickNode, doc); + if (quickNode->type() == Node::Class) { + classesWithNoQuickDoc.remove(quickNode->name()); + if (doc.briefText().isEmpty()) + doc.location().warning(tr("Missing '\\%1' for class '%2'") + .arg(COMMAND_BRIEF) + .arg(quickNode->name())); + } + else if (quickNode->type() == Node::Property) { + PropertyNode *quickProperty = (PropertyNode *) quickNode; + if (quickProperty->dataType() == "Object") { + if (dataType.isEmpty()) { + doc.location().warning(tr("Missing data type in '\\%1'" + " (assuming 'Object')") + .arg(command)); + } + else { + quickProperty->setDataType(dataType); + } + } + else if (dataType != quickProperty->dataType()) { + doc.location().warning(tr("Ignored contradictory data type" + " in '\\%1'") + .arg(command)); + } + } + } + return 0; + } + else { + return CppCodeParser::processTopicCommand(doc, command, arg); + } +} + +QSet<QString> QsCodeParser::otherMetaCommands() +{ + return commonMetaCommands() << COMMAND_ENDQUICKCODE << COMMAND_QUICKCODE + << COMMAND_QUICKIFY << COMMAND_REPLACE; +} + +void QsCodeParser::processOtherMetaCommand(const Doc& doc, + const QString& command, + const QString& arg, + Node *node) +{ + if (command == COMMAND_PROTECTED) { + doc.location().warning(tr("Cannot use '\\%1' in %2") + .arg(COMMAND_PROTECTED).arg(language())); + } + else { + CppCodeParser::processOtherMetaCommand(doc,command,arg,node); + } +} + +ClassNode *QsCodeParser::tryClass(const QString& className) +{ + return (ClassNode*) cppTre->findNode(QStringList(className),Node::Class); +} + +FunctionNode *QsCodeParser::findKernelFunction(const QStringList& parentPath, + const QString& name) +{ + FunctionNode clone(0, name); + clone.setReturnType("Object"); + clone.addParameter(Parameter("...")); + return qsTre->findFunctionNode(parentPath, &clone); +} + +void QsCodeParser::extractRegExp(const QRegExp& regExp, + QString& source, + const Doc& doc) +{ + QRegExp blankLineRegExp( + "[ \t]*(?:\n(?:[ \t]*\n)+[ \t]*|[ \n\t]*\\\\code|" + "\\\\endcode[ \n\t]*)"); + QStringList paras = source.trimmed().split(blankLineRegExp); + paras = paras.filter(regExp); + if (paras.count() == 0) { + doc.location().warning(tr("Cannot find regular expression '%1'") + .arg(regExp.pattern())); + } + else if (paras.count() > 1) { + doc.location().warning(tr("Regular rexpression '%1' matches multiple" + "times").arg(regExp.pattern())); + } + else { + source = paras.first() + "\n\n"; + } +} + +void QsCodeParser::extractTarget(const QString& target, + QString& source, + const Doc& doc) +{ + QRegExp targetRegExp( + "(\\\\target\\s+(\\S+)[^\n]*\n" + "(?:(?!\\s*\\\\code)[^\n]+\n|\\s*\\\\code.*\\\\endcode\\s*\n)*)" + "(?:\\s*\n|[^\n]*$)"); + targetRegExp.setMinimal(true); + + int pos = 0; + while ((pos = source.indexOf(targetRegExp, pos)) != -1) { + if (targetRegExp.cap(2) == target) { + source = targetRegExp.cap(1) + "\n\n"; + return; + } + pos += targetRegExp.matchedLength(); + } + doc.location().warning(tr("Cannot find target '%1'").arg(target)); +} + +void QsCodeParser::renameParameters(QString& source, + const Doc& /* doc */, + const QStringList& qtParams, + const QStringList& quickParams) +{ + QRegExp paramRegExp("(\\\\a\\s*\\{?\\s*)([A-Za-z0-9_]+)"); + + int pos = 0; + while ((pos = paramRegExp.indexIn(source, pos)) != -1) { + pos += paramRegExp.cap(1).length(); + QString before = paramRegExp.cap(2); + int index = qtParams.indexOf(before); + if (index != -1) { + QString after = quickParams[index]; + source.replace(pos, before.size(), after); + } + } +} + +void QsCodeParser::applyReplacementList(QString& source, const Doc& doc) +{ + QStringList args = doc.metaCommandArgs(COMMAND_REPLACE); + QStringList::ConstIterator a = args.begin(); + while (a != args.end()) { + if (replaceRegExp.exactMatch(*a)) { + QRegExp before(replaceRegExp.cap(1)); + before.setMinimal(true); + QString after = replaceRegExp.cap(2); + + if (before.isValid()) { + int oldLen = source.size(); + source.replace(before, after); + + // this condition is sufficient but not necessary + if (oldLen == source.size() && !source.contains(after)) + doc.location().warning( + tr("Regular expression '%1' did not match anything") + .arg(before.pattern())); + } + else { + doc.location().warning( + tr("Invalid regular expression '%1'") + .arg(before.pattern())); + } + } + else { + doc.location().warning(tr("Bad syntax in '\\%1'") + .arg(COMMAND_REPLACE)); + } + ++a; + } + + QRegExp codeRegExp("\\\\" + COMMAND_CODE + "(.*)\\\\" + COMMAND_ENDCODE); + codeRegExp.setMinimal(true); + + QRegExp quickcodeRegExp( + "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" + COMMAND_ENDQUICKCODE); + quickcodeRegExp.setMinimal(true); + + int quickcodePos = doc.source().indexOf(quickcodeRegExp); + if (quickcodePos != -1) { + int codePos = source.indexOf(codeRegExp); + if (codePos == -1) { + doc.location().warning( + tr("Cannot find any '\\%1' snippet corresponding to '\\%2'") + .arg(COMMAND_CODE).arg(COMMAND_QUICKCODE)); + } + else { + source.replace(codeRegExp.pos(1), codeRegExp.cap(1).length(), + quickcodeRegExp.cap(1)); + codePos = codeRegExp.pos(1) + quickcodeRegExp.cap(1).length(); + + if (doc.source().indexOf(quickcodeRegExp, quickcodePos + 1) != -1) { + doc.location().warning( + tr("Cannot use '\\%1' twice in a row") + .arg(COMMAND_QUICKCODE)); + } + else if (source.indexOf(codeRegExp, codePos + 1) != -1) { + doc.location().warning(tr("Ambiguous '\\%1'") + .arg(COMMAND_QUICKCODE)); + } + } + } +} + +void QsCodeParser::quickifyClass(ClassNode *quickClass) +{ + QString qtClassName = quickClass->name(); + QString bare = quickClass->name(); + if (bare != "Qt" && bare != "Object") { + if (bare.startsWith("Q")) { + bare = bare.mid(1); + } + else { + qtClassName.prepend("Q"); + classesWithNoQ.insert(bare); + } + } + + ClassNode *qtClass = 0; + ClassNode *wrapperClass = 0; + + if ((wrapperClass = tryClass("Quick" + bare)) != 0 || + (wrapperClass = tryClass("QS" + bare + "Class")) != 0) { + qtClass = tryClass(qtClassName); + if (qtClass == 0) { + qtClass = wrapperClass; + wrapperClass = 0; + } + } + else if ((wrapperClass = tryClass("Quick" + bare + "Ptr")) != 0) { + QRegExp ptrToQtType("(Q[A-Za-z0-9_]+)\\s*\\*"); + FunctionNode *ctor = + wrapperClass->findFunctionNode(wrapperClass->name()); + if (ctor != 0 && !ctor->parameters().isEmpty() && + ptrToQtType.exactMatch(ctor->parameters().first().leftType())) + qtClassName = ptrToQtType.cap(1); + qtClass = tryClass(qtClassName); + } + else { + wrapperClass = tryClass("Q" + bare + "Ptr"); + if (wrapperClass == 0) + wrapperClass = tryClass("Quick" + bare + "Interface"); + qtClass = tryClass(qtClassName); + } + + if (qtClass == 0) { + if (wrapperClass == 0) { + quickClass->location().warning(tr("Cannot find Qt class '%1'") + .arg(qtClassName)); + } + else { + quickClass->location().warning(tr("Cannot find Qt class '%1'" + " wrapped by '%2'") + .arg(qtClassName) + .arg(wrapperClass->name())); + } + return; + } + + QList<RelatedClass>::ConstIterator r = qtClass->baseClasses().begin(); + while (r != qtClass->baseClasses().end()) { + ClassNode *quickBaseClass = cpp2qs.findClassNode(qsTre, + (*r).node->name()); + if (quickBaseClass) + quickClass->addBaseClass((*r).access, quickBaseClass); + ++r; + } + if (quickClass->baseClasses().isEmpty() && quickClass->name() != "Object") + quickClass->addBaseClass(Node::Public, + cpp2qs.findClassNode(qsTre,"Object")); + + QSet<QString> funcBlackList; + QSet<QString> propertyBlackList; + + NodeList children; + if (wrapperClass != 0) { + children = wrapperClass->childNodes(); + + funcBlackList.insert(wrapperClass->name()); + funcBlackList.insert("~" + wrapperClass->name()); + } + children += qtClass->childNodes(); + + for (int pass = 0; pass < 2; pass++) { + NodeList::ConstIterator c = children.begin(); + while (c != children.end()) { + if ((*c)->access() != Node::Private && + (*c)->status() == Node::Commendable) { + if (pass == 0) { + if ((*c)->type() == Node::Enum) { + EnumNode *enume = (EnumNode *) *c; + quickifyEnum(quickClass, enume); + } + else if ((*c)->type() == Node::Property) { + if (!propertyBlackList.contains((*c)->name())) { + PropertyNode *property = (PropertyNode *) *c; + quickifyProperty(quickClass, qtClass, property); + if (!property->getters().isEmpty()) + funcBlackList.insert(property->getters().first()->name()); + if (!property->setters().isEmpty()) + funcBlackList.insert(property->setters().first()->name()); + if (!property->resetters().isEmpty()) + funcBlackList.insert(property->resetters().first()->name()); + propertyBlackList.insert(property->name()); + } + } + } + else if ((*c)->type() == Node::Function) { + FunctionNode *func = (FunctionNode *) *c; + quickifyFunction(quickClass, qtClass, func, + funcBlackList.contains((*c)->name()) && + func->parameters().count() < 2); + } + } + ++c; + } + } + setQtDoc(quickClass, qtClass->doc()); + classesWithNoQuickDoc.insert(quickClass->name(), quickClass); +} + +void QsCodeParser::quickifyEnum(ClassNode *quickClass, EnumNode *enume) +{ + EnumNode *quickEnum = new EnumNode(quickClass, enume->name()); + quickEnum->setLocation(enume->location()); +#if 0 // ### not yet + quickEnum->setAccess(Node::Protected); +#endif + + QList<EnumItem>::ConstIterator it = enume->items().begin(); + while (it != enume->items().end()) { + QString name = (*it).name(); + QString value = (*it).value(); + quickEnum->addItem(EnumItem(name, value)); + ++it; + } + setQtDoc(quickEnum, enume->doc()); +} + +void QsCodeParser::quickifyFunction(ClassNode *quickClass, ClassNode *qtClass, + FunctionNode *func, bool onBlackList) +{ + if (func->metaness() == FunctionNode::Dtor) + return; + + FunctionNode *kernelFunc = findKernelFunction( + QStringList() << quickClass->name(), func->name()); + + QString quickName = func->name(); + if (func->metaness() == FunctionNode::Ctor) + quickName = quickClass->name(); + FunctionNode *quickFunc = new FunctionNode(quickClass, quickName); + quickFunc->setLocation(func->location()); + + if (onBlackList) { + quickFunc->setAccess(Node::Protected); + } + else { + if (kernelFunc != 0 && func->numOverloads() == 1 && + (func->parameters().count() == 0 || + func->parameters().last().defaultValue().isEmpty())) { + kernelFunc->setAccess(Node::Private); + } + else { + if (func->metaness() == FunctionNode::Plain) + quickFunc->setAccess(Node::Protected); + } + } + + quickFunc->setReturnType(cpp2qs.convertedDataType(qsTre, + func->returnType())); + if (func->metaness() != FunctionNode::Slot) + quickFunc->setMetaness(func->metaness()); + quickFunc->setVirtualness(FunctionNode::ImpureVirtual); + quickFunc->setOverload(func->isOverload()); + + QList<Parameter>::ConstIterator q = func->parameters().begin(); + while (q != func->parameters().end()) { + QString dataType = cpp2qs.convertedDataType(qsTre, (*q).leftType(), + (*q).rightType()); + if (dataType.isEmpty()) { + dataType = "UNKNOWN"; + quickFunc->setAccess(Node::Private); + } + Parameter param(dataType, "", (*q).name(), + (*q).defaultValue().isEmpty() ? "" : "undefined"); + quickFunc->addParameter(param); + ++q; + } + + if (func->doc().isEmpty()) { + if (func->parent() != (InnerNode *) qtClass) { + func = qtClass->findFunctionNode(func); + if (func != 0) + setQtDoc(quickFunc, func->doc()); + } + } + else { + setQtDoc(quickFunc, func->doc()); + } +} + +void QsCodeParser::quickifyProperty(ClassNode *quickClass, + ClassNode * /* qtClass */, + PropertyNode *property) +{ + PropertyNode *quickProperty = new PropertyNode(quickClass, + property->name()); + quickProperty->setLocation(property->location()); + quickProperty->setDataType(cpp2qs.convertedDataType(qsTre, + property->dataType())); +#if 0 + quickProperty->setGetter(property->getter()); + quickProperty->setSetter(property->setter()); + quickProperty->setResetter(property->resetter()); +#endif + quickProperty->setStored(property->isStored()); + quickProperty->setDesignable(property->isDesignable()); + + setQtDoc(quickProperty, property->doc()); +} + +QString QsCodeParser::quickifiedDoc(const QString& source) +{ + QString result; + int i = 0; + + while (i < (int) source.length()) { + if (leftWordBoundary(source, i)) { + if (source[i] == 'Q') { + if (source[i + 1] == 'C' && source.mid(i, 8) == "QCString") { + i += 2; + } + else { + int end = i + 1; + while (isWord(source[end])) + ++end; + if (!classesWithNoQ.contains( + source.mid(i + 1, end - (i + 1)))) + result += "Q"; + i++; + } + } + else if (source[i] == 'T' && source.mid(i, 4) == "TRUE" && + rightWordBoundary(source, i + 4)) { + result += "\\c{true}"; + i += 4; + } + else if (source[i] == 'F' && source.mid(i, 5) == "FALSE" && + rightWordBoundary(source, i + 5)) { + result += "\\c{false}"; + i += 5; + } + else if (source[i] == 'c' && source.mid(i, 6) == "const ") { + i += 6; + } + else { + result += source[i++]; + } + } + else if ((source[i] == ':' && source[i + 1] == ':') || + (source[i] == '-' && source[i + 1] == '>')) { + result += '.'; + i += 2; + } + else if (source[i] == '\\') { + // ### make independent of the command name + if (source.mid(i, 5) == "\\code") { + do { + result += source[i++]; + } while (source[i - 1] != '\n'); + + int begin = i; + int end = source.indexOf("\\endcode", i); + if (end != -1) { + QString code = source.mid(begin, end - begin); + result += cpp2qs.convertedCode(qsTre, code, + classesWithNoQ); + i = end; + } + } + else { + result += source[i++]; + } + } + else { + result += source[i++]; + } + } + + QList<QRegExp>::ConstIterator b = replaceBefores.begin(); + QStringList::ConstIterator a = replaceAfters.begin(); + while (a != replaceAfters.end()) { + result.replace(*b, *a); + ++b; + ++a; + } + return result; +} + +void QsCodeParser::setQtDoc(Node *quickNode, const Doc& doc) +{ + if (!doc.isEmpty()) { + Doc quickDoc(doc.location(), doc.location(), + quickifiedDoc(doc.source()), + CppCodeParser::topicCommands() + + CppCodeParser::otherMetaCommands()); + quickNode->setDoc(quickDoc, true); + } +} + +void QsCodeParser::setQuickDoc(Node *quickNode, + const Doc& doc, + const QStringList& qtParams, + const QStringList& quickParams) +{ + QRegExp quickifyCommand("\\\\" + COMMAND_QUICKIFY + "([^\n]*)(?:\n|$)"); + + if (quickNode->type() == Node::Function) { + FunctionNode *quickFunc = (FunctionNode *) quickNode; + quickFunc->setOverload(false); + } + + if (doc.metaCommandsUsed().contains(COMMAND_QUICKIFY)) { + QString source = doc.source(); + int pos = source.indexOf(quickifyCommand); + if (pos != -1) { + QString quickifiedSource = quickNode->doc().source(); + if (!qtParams.isEmpty() && qtParams != quickParams) + renameParameters(quickifiedSource, doc, qtParams, + quickParams); + applyReplacementList(quickifiedSource, doc); + + do { + QString extract = quickifiedSource; + QString arg = quickifyCommand.cap(1).simplified(); + if (!arg.isEmpty()) { + if (arg.startsWith("/") && arg.endsWith("/") && + arg.length() > 2) { + QString pattern = arg.mid(1, arg.length() - 2); + extractRegExp(QRegExp(pattern), extract, doc); + } + else { + extractTarget(arg, extract, doc); + } + } + source.replace(pos, quickifyCommand.matchedLength(), extract); + pos += extract.length(); + } while ((pos = source.indexOf(quickifyCommand, pos)) != -1); + + QRegExp quickcodeRegExp( + "\\\\" + COMMAND_QUICKCODE + "(.*)\\\\" + + COMMAND_ENDQUICKCODE); + quickcodeRegExp.setMinimal(true); + source.replace(quickcodeRegExp, ""); + } + + Doc quickDoc(doc.location(), + doc.location(), + source, + (CppCodeParser::topicCommands() + topicCommands() + + CppCodeParser::otherMetaCommands()) << COMMAND_REPLACE); + quickNode->setDoc(quickDoc, true); + processOtherMetaCommands(quickDoc, quickNode); + } + else { + quickNode->setDoc(doc, true); + processOtherMetaCommands(doc, quickNode); + } +} + +bool QsCodeParser::makeFunctionNode(const QString& synopsis, + QStringList *parentPathPtr, + FunctionNode **funcPtr) +{ + QRegExp funcRegExp( + "\\s*([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\s*\\((" + + balancedParens + + ")\\)(?:\\s*:\\s*([A-Za-z0-9_]+))?\\s*"); + QRegExp paramRegExp( + "\\s*(\\[)?\\s*(?:([A-Za-z0-9_]+)\\s*:\\s*)?" + "([A-Za-z0-9_]+|\\.\\.\\.)\\s*(\\[)?[\\s\\]]*"); + + if (!funcRegExp.exactMatch(synopsis)) + return false; + + ClassNode *classe = (ClassNode*) + qsTre->findNode(QStringList(funcRegExp.cap(1)),Node::Class); + if (classe == 0) + return false; + + FunctionNode *clone = new FunctionNode(0, funcRegExp.cap(2)); + bool optional = false; + + QString paramStr = funcRegExp.cap(3); + QStringList params = paramStr.split(","); + QStringList::ConstIterator p = params.begin(); + while (p != params.end()) { + if (paramRegExp.exactMatch(*p)) { + if (!paramRegExp.cap(1).isEmpty()) + optional = true; + clone->addParameter(Parameter(paramRegExp.cap(3), + "", + paramRegExp.cap(2), + optional ? "undefined" : "")); + if (!paramRegExp.cap(4).isEmpty()) + optional = true; + } + else { + delete clone; + return false; + } + ++p; + } + QString returnType = funcRegExp.cap(4); + if (!returnType.isEmpty()) + clone->setReturnType(returnType); + if (parentPathPtr != 0) + *parentPathPtr = QStringList() << classe->name(); + if (funcPtr != 0) { + *funcPtr = clone; + } + else { + delete clone; + } + return true; +} + +bool QsCodeParser::isWord(QChar ch) +{ + return ch.isLetterOrNumber() || ch == QChar('_'); +} + +bool QsCodeParser::leftWordBoundary(const QString& str, int pos) +{ + return !isWord(str[pos - 1]) && isWord(str[pos]); +} + +bool QsCodeParser::rightWordBoundary(const QString& str, int pos) +{ + return isWord(str[pos - 1]) && !isWord(str[pos]); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/qscodeparser.h b/tools/qdoc3/qscodeparser.h new file mode 100644 index 0000000..bd599a8 --- /dev/null +++ b/tools/qdoc3/qscodeparser.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + qscodeparser.h +*/ + +#ifndef QSCODEPARSER_H +#define QSCODEPARSER_H + +#include "cppcodeparser.h" +#include "cpptoqsconverter.h" + +QT_BEGIN_NAMESPACE + +class QsCodeParser : public CppCodeParser +{ + public: + QsCodeParser(Tree *cppTree); + ~QsCodeParser(); + + virtual void initializeParser(const Config& config); + virtual void terminateParser(); + virtual QString language(); + virtual QString headerFileNameFilter(); + virtual QString 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); + + FunctionNode *findFunctionNode(const QString& synopsis, Tree *tree); + + protected: + virtual QSet<QString> topicCommands(); + virtual Node *processTopicCommand(const Doc& doc, const QString& command, + const QString& arg); + virtual QSet<QString> otherMetaCommands(); + virtual void processOtherMetaCommand(const Doc& doc, + const QString& command, + const QString& arg, Node *node); + + private: + ClassNode *tryClass(const QString& className); + FunctionNode *findKernelFunction(const QStringList& parentPath, + const QString& name); + void extractRegExp(const QRegExp& regExp, QString& source, + const Doc& doc); + void extractTarget(const QString& target, QString& source, + const Doc& doc); + void renameParameters(QString& source, const Doc& doc, + const QStringList& qtNames, + const QStringList& quickNames); + void applyReplacementList(QString& source, const Doc& doc); + void quickifyClass(ClassNode *quickClass); + void quickifyEnum(ClassNode *quickClass, EnumNode *enume); + void quickifyFunction(ClassNode *quickClass, ClassNode *qtClass, + FunctionNode *func, bool onBlackList); + void quickifyProperty(ClassNode *quickClass, ClassNode *qtClass, + PropertyNode *property); + QString quickifiedDoc(const QString& source); + void setQtDoc(Node *quickNode, const Doc& doc); + void setQuickDoc(Node *quickNode, const Doc& doc, + const QStringList& qtParams = QStringList(), + const QStringList& quickParams = QStringList()); + bool makeFunctionNode(const QString& synopsis, QStringList *parentPathPtr, + FunctionNode **funcPtr); + + static bool isWord(QChar ch); + static bool leftWordBoundary(const QString& str, int pos); + static bool rightWordBoundary(const QString& str, int pos); + + QMap<QString,Node::Type> nodeTypeMap; + QMap<QString,Node*> classesWithNoQuickDoc; + QList<QRegExp> replaceBefores; + QStringList replaceAfters; + QSet<QString> classesWithNoQ; + Tree* cppTre; + Tree* qsTre; + QRegExp replaceRegExp; + CppToQsConverter cpp2qs; + + static int tabSize; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/quoter.cpp b/tools/qdoc3/quoter.cpp new file mode 100644 index 0000000..40ca15a --- /dev/null +++ b/tools/qdoc3/quoter.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 +static QStringList 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 files: + #! [<id>] + * .xq, .xml, .html 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; + + while (!plainLines.isEmpty()) { + if (match(docLocation, delimiter, plainLines.first())) { + getLine(); + break; + } + getLine(); + } + while (!plainLines.isEmpty()) { + QString line = plainLines.first(); + if (match(docLocation, delimiter, line)) { + QString lastLine = getLine(); + int dIndex = lastLine.indexOf(delimiter); + if (dIndex > 0) { + QString leading = lastLine.left(dIndex); + dIndex = leading.indexOf(comment); + if (dIndex != -1) + leading = leading.left(dIndex); + if (!leading.trimmed().isEmpty()) + t += leading; + } + return t; + } + // Remove special macros to support Qt namespacing. + if (line.startsWith("QT_BEGIN_NAMESPACE")) { + getLine(); + } else if (line.startsWith("QT_END_NAMESPACE")) { + getLine(); + t += QLatin1Char('\n'); + } else if (!line.startsWith(comment)) { + // Ordinary code + t += getLine(); + } else { + // Normal comments + if (line.contains(QLatin1Char('\n'))) + t += QLatin1Char('\n'); + getLine(); + } + } + 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(); + // Remove special macros to support Qt namespacing. + if (line.startsWith("QT_BEGIN_NAMESPACE")) { + getLine(); + } else if (line.startsWith("QT_END_NAMESPACE")) { + getLine(); + t += QLatin1Char('\n'); + } else if (!line.startsWith(comment)) + // Ordinary code + t += getLine(); + else { + // Normal comments + if (line.contains(QLatin1Char('\n'))) + t += QLatin1Char('\n'); + getLine(); + } + } + } 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() +{ + if ( plainLines.isEmpty() ) + return QString(); + + plainLines.removeFirst(); + + QString t = markedLines.takeFirst(); + 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, "//!"); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/quoter.h b/tools/qdoc3/quoter.h new file mode 100644 index 0000000..7906754 --- /dev/null +++ b/tools/qdoc3/quoter.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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); + +private: + QString getLine(); + void failedAtEnd( const Location& docLocation, const QString& command ); + bool match( const Location& docLocation, const QString& pattern, + const QString& line ); + QString commentForCode() const; + + bool silent; + bool validRegExp; + QStringList plainLines; + QStringList markedLines; + Location codeLocation; + QHash<QString,QString> commentHash; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/separator.cpp b/tools/qdoc3/separator.cpp new file mode 100644 index 0000000..0d730e1 --- /dev/null +++ b/tools/qdoc3/separator.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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" ); + } else { + if ( index == 0 ) { + return tr( ", ", "first separator when N > 2" ); + } else if ( index < count - 2 ) { + return tr( ", ", "general separator when N > 2" ); + } else { + return tr( ", and ", "last separator when N > 2" ); + } + } +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/separator.h b/tools/qdoc3/separator.h new file mode 100644 index 0000000..f29868c --- /dev/null +++ b/tools/qdoc3/separator.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + separator.h +*/ + +#ifndef SEPARATOR_H +#define SEPARATOR_H + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +QString separator( int index, int count ); + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/sgmlgenerator.cpp b/tools/qdoc3/sgmlgenerator.cpp new file mode 100644 index 0000000..575bb53 --- /dev/null +++ b/tools/qdoc3/sgmlgenerator.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + sgmlgenerator.cpp +*/ + +#include "sgmlgenerator.h" + +QT_BEGIN_NAMESPACE + +SgmlGenerator::SgmlGenerator() +{ +} + +SgmlGenerator::~SgmlGenerator() +{ +} + +QString SgmlGenerator::format() +{ + return "SGML"; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/sgmlgenerator.h b/tools/qdoc3/sgmlgenerator.h new file mode 100644 index 0000000..e4ffa80 --- /dev/null +++ b/tools/qdoc3/sgmlgenerator.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + sgmlgenerator.h +*/ + +#ifndef SGMLGENERATOR_H +#define SGMLGENERATOR_H + +#include "bookgenerator.h" + +QT_BEGIN_NAMESPACE + +class SgmlGenerator : public BookGenerator +{ +public: + SgmlGenerator(); + ~SgmlGenerator(); + + virtual QString format(); + +protected: + // ### +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/test/arthurtext.qdocconf b/tools/qdoc3/test/arthurtext.qdocconf new file mode 100644 index 0000000..473152d --- /dev/null +++ b/tools/qdoc3/test/arthurtext.qdocconf @@ -0,0 +1,6 @@ +include(qt.qdocconf) +headerdirs = +sourcedirs = +sources = $QTDIR/doc/src/qt4-arthur.qdoc +outputdir = $QTDIR/demos/arthur/html +HTML.{postheader,address} = ""
\ No newline at end of file diff --git a/tools/qdoc3/test/assistant.qdocconf b/tools/qdoc3/test/assistant.qdocconf new file mode 100644 index 0000000..7bb6bbf --- /dev/null +++ b/tools/qdoc3/test/assistant.qdocconf @@ -0,0 +1,45 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qt Assistant +description = Qt Assistant Manual +url = http://doc.trolltech.com/4.5 + +indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index + +qhp.projects = Assistant + +qhp.Assistant.file = assistant.qhp +qhp.Assistant.namespace = com.trolltech.assistant.450 +qhp.Assistant.virtualFolder = qdoc +qhp.Assistant.indexTitle = Qt Assistant Manual +qhp.Assistant.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png +qhp.Assistant.filterAttributes = qt 4.5.0 tools assistant +qhp.Assistant.customFilters.Assistant.name = Qt Assistant Manual +qhp.Assistant.customFilters.Assistant.filterAttributes = qt tools assistant +qhp.Assistant.subprojects = manual examples +qhp.Assistant.subprojects.manual.title = Manual +qhp.Assistant.subprojects.manual.indexTitle = Qt Assistant Manual +qhp.Assistant.subprojects.manual.selectors = fake:page +qhp.Assistant.subprojects.examples.title = Examples +qhp.Assistant.subprojects.examples.indexTitle = Qt Examples +qhp.Assistant.subprojects.examples.selectors = fake:example +qhp.Assistant.subprojects.examples.sortPages = true + +language = Cpp + +sources = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc + +sources.fileextensions = "*.cpp *.qdoc" + +exampledirs = $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src + +imagedirs = $QT_SOURCE_TREE/doc/src/images +outputdir = $QT_BUILD_TREE/doc-build/html-assistant +base = file:$QT_BUILD_TREE/doc-build/html-assistant diff --git a/tools/qdoc3/test/carbide-eclipse-integration.qdocconf b/tools/qdoc3/test/carbide-eclipse-integration.qdocconf new file mode 100644 index 0000000..683048d --- /dev/null +++ b/tools/qdoc3/test/carbide-eclipse-integration.qdocconf @@ -0,0 +1,12 @@ +include(eclipse-integration.qdocconf) + +defines += NO_QT_ECLIPSE_INSTALLATION_PAGE +macro.theEclipseIntegration = Carbide.c++ +macro.TheEclipseIntegration = Carbide.c++ + +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"40%\" align="left">Copyright © 2009 <a href=\"http://doc.trolltech.com/trolltech.html\">Nokia Corporation</a></td>\n" \ + "<td width=\"30%\" align=\"center\"><a href=\"http://doc.trolltech.com\">Trademarks</a></td>\n" \ + "<td width=\"40%\" align=\"right\"><div align=\"right\">Carbide.c++</div></td>\n" \ + "</tr></table></div></address>" diff --git a/tools/qdoc3/test/classic.css b/tools/qdoc3/test/classic.css new file mode 100644 index 0000000..0d0b664 --- /dev/null +++ b/tools/qdoc3/test/classic.css @@ -0,0 +1,131 @@ +h3.fn,span.fn +{ + margin-left: 1cm; + text-indent: -1cm; +} + +a:link +{ + color: #004faf; + text-decoration: none +} + +a:visited +{ + color: #672967; + text-decoration: none +} + +a.obsolete +{ + color: #661100; + text-decoration: none +} + +a.compat +{ + color: #661100; + text-decoration: none +} + +a.obsolete:visited +{ + color: #995500; + text-decoration: none +} + +a.compat:visited +{ + color: #995500; + text-decoration: none +} + +td.postheader +{ + font-family: sans-serif +} + +tr.address +{ + font-family: sans-serif +} + +body +{ + background: #ffffff; + color: black +} + +table tr.odd { + background: #f0f0f0; + color: black; +} + +table tr.even { + background: #e4e4e4; + color: black; +} + +table.annotated th { + padding: 3px; + text-align: left +} + +table.annotated td { + padding: 3px; +} + +table tr pre +{ + padding-top: none; + padding-bottom: none; + padding-left: none; + padding-right: none; + border: none; + background: none +} + +tr.qt-style +{ + background: #96E066; + color: black +} + +body pre +{ + padding: 0.2em; + border: #e7e7e7 1px solid; + background: #f1f1f1; + color: black +} + +span.preprocessor, span.preprocessor a +{ + color: darkblue; +} + +span.comment +{ + color: darkred; + font-style: italic +} + +span.string,span.char +{ + color: darkgreen; +} + +.title +{ + text-align: center +} + +.subtitle +{ + font-size: 0.8em +} + +.small-subtitle +{ + font-size: 0.65em +} diff --git a/tools/qdoc3/test/compat.qdocconf b/tools/qdoc3/test/compat.qdocconf new file mode 100644 index 0000000..5745ed9 --- /dev/null +++ b/tools/qdoc3/test/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/tools/qdoc3/test/designer.qdocconf b/tools/qdoc3/test/designer.qdocconf new file mode 100644 index 0000000..26636cd --- /dev/null +++ b/tools/qdoc3/test/designer.qdocconf @@ -0,0 +1,51 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qt Designer +description = Qt Designer Manual +url = http://doc.trolltech.com/4.5 + +indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index + +qhp.projects = Designer + +qhp.Designer.file = designer.qhp +qhp.Designer.namespace = com.trolltech.designer.450 +qhp.Designer.virtualFolder = qdoc +qhp.Designer.indexTitle = Qt Designer Manual +qhp.Designer.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png +qhp.Designer.filterAttributes = qt 4.5.0 tools designer +qhp.Designer.customFilters.Designer.name = Qt Designer Manual +qhp.Designer.customFilters.Designer.filterAttributes = qt tools designer +qhp.Designer.subprojects = manual examples +qhp.Designer.subprojects.manual.title = Manual +qhp.Designer.subprojects.manual.indexTitle = Qt Designer Manual +qhp.Designer.subprojects.manual.selectors = fake:page +qhp.Designer.subprojects.examples.title = Examples +qhp.Designer.subprojects.examples.indexTitle = Qt Examples +qhp.Designer.subprojects.examples.selectors = fake:example +qhp.Designer.subprojects.examples.sortPages = true + +language = Cpp + +sources = $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc + +sources.fileextensions = "*.cpp *.qdoc" + +exampledirs = $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src + +imagedirs = $QT_SOURCE_TREE/doc/src/images +outputdir = $QT_BUILD_TREE/doc-build/html-designer +base = file:$QT_BUILD_TREE/doc-build/html-designer diff --git a/tools/qdoc3/test/eclipse-integration.qdocconf b/tools/qdoc3/test/eclipse-integration.qdocconf new file mode 100644 index 0000000..1fdaa1b --- /dev/null +++ b/tools/qdoc3/test/eclipse-integration.qdocconf @@ -0,0 +1,13 @@ +include(qt.qdocconf) + +headerdirs = +sourcedirs = +sourcedirs = $QTDIR/../qteclipsetools/main/doc +imagedirs = $QTDIR/../qteclipsetools/main/doc +outputdir = $QTDIR/../qteclipsetools/main/doc/html + +project = Qt Eclipse Integration +description = "Qt Eclipse Integration" +url = http://doc.trolltech.com/eclipse-integration-4.4 + +HTML.{postheader,address} = "" diff --git a/tools/qdoc3/test/jambi.qdocconf b/tools/qdoc3/test/jambi.qdocconf new file mode 100644 index 0000000..3644b69 --- /dev/null +++ b/tools/qdoc3/test/jambi.qdocconf @@ -0,0 +1,47 @@ +include(compat.qdocconf) +include(macros.qdocconf) + +project = Qt Jambi +description = Qt Jambi Reference Documentation +url = http://doc.trolltech.com/qtjambi + +version = 4.4.0_01 + +language = Java + +sources = $JAMBI/doc/japi/qdoc.japi +outputformats = javadoc HTML +outputdir = $JAMBI/doc/html/com/trolltech/qt + +imagedirs = $QTDIR/doc/src/images \ + $QTDIR/examples \ + $JAMBI/doc/src/images \ + ../doc/src/images + +extraimages.javadoc = qt-logo \ + qt-logo.png + +HTML.stylesheets = classic.css + +HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \ + "<tr>\n" \ + "<td align=\"left\" valign=\"top\" width=\"32\">" \ + "<img src=\"images/qt-logo.png\" align=\"left\" width=\"32\" height=\"32\" border=\"0\" />" \ + "</td>\n" \ + "<td width=\"1\"> </td>" \ + "<td class=\"postheader\" valign=\"center\">" \ + "<a href=\"qtjambi-index.html\">" \ + "<font color=\"#004faf\">Home</font></a> ·" \ + " <a href=\"qtjambi-overviews.html\">" \ + "<font color=\"#004faf\">Overviews</font></a> ·" \ + " <a href=\"qtjambi-examples.html\">" \ + "<font color=\"#004faf\">Examples</font></a> " \ + "</td>\n" \ + "</tr></table>" + +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"30%\">Copyright © \$THISYEAR\$ <a href=\"trolltech.html\">Trolltech</a></td>\n" \ + "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \ + "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt Jambi \\version</div></td>\n" \ + "</tr></table></div></address>" diff --git a/tools/qdoc3/test/linguist.qdocconf b/tools/qdoc3/test/linguist.qdocconf new file mode 100644 index 0000000..5e889a8 --- /dev/null +++ b/tools/qdoc3/test/linguist.qdocconf @@ -0,0 +1,47 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qt Linguist +description = Qt Linguist Manual +url = http://doc.trolltech.com/4.5 + +indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index + +qhp.projects = Linguist + +qhp.Linguist.file = linguist.qhp +qhp.Linguist.namespace = com.trolltech.linguist.450 +qhp.Linguist.virtualFolder = qdoc +qhp.Linguist.indexTitle = Qt Linguist Manual +qhp.Linguist.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png +qhp.Linguist.filterAttributes = qt 4.5.0 tools linguist +qhp.Linguist.customFilters.Linguist.name = Qt Linguist Manual +qhp.Linguist.customFilters.Linguist.filterAttributes = qt tools linguist +qhp.Linguist.subprojects = manual examples +qhp.Linguist.subprojects.manual.title = Manual +qhp.Linguist.subprojects.manual.indexTitle = Qt Linguist Manual +qhp.Linguist.subprojects.manual.selectors = fake:page +qhp.Linguist.subprojects.examples.title = Examples +qhp.Linguist.subprojects.examples.indexTitle = Qt Examples +qhp.Linguist.subprojects.examples.selectors = fake:example +qhp.Linguist.subprojects.examples.sortPages = true + +language = Cpp + +sources = $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc + +sources.fileextensions = "*.cpp *.qdoc" + +exampledirs = $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src + +imagedirs = $QT_SOURCE_TREE/doc/src/images +outputdir = $QT_BUILD_TREE/doc-build/html-linguist +base = file:$QT_BUILD_TREE/doc-build/html-linguist diff --git a/tools/qdoc3/test/macros.qdocconf b/tools/qdoc3/test/macros.qdocconf new file mode 100644 index 0000000..d14f80a --- /dev/null +++ b/tools/qdoc3/test/macros.qdocconf @@ -0,0 +1,27 @@ +macro.Aring.HTML = "Å" +macro.aring.HTML = "å" +macro.Auml.HTML = "Ä" +macro.author = "\\bold{Author:}" +macro.br.HTML = "<br />" +macro.BR.HTML = "<br />" +macro.aacute.HTML = "á" +macro.eacute.HTML = "é" +macro.iacute.HTML = "í" +macro.gui = "\\bold" +macro.hr.HTML = "<hr />" +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.param = "\\e" +macro.raisedaster.HTML = "<sup>*</sup>" +macro.rarrow.HTML = "→" +macro.reg.HTML = "<sup>®</sup>" +macro.return = "Returns" +macro.starslash = "\\c{*/}" +macro.uuml.HTML = "ü" +macro.mdash.HTML = "—" diff --git a/tools/qdoc3/test/qmake.qdocconf b/tools/qdoc3/test/qmake.qdocconf new file mode 100644 index 0000000..c357cfb --- /dev/null +++ b/tools/qdoc3/test/qmake.qdocconf @@ -0,0 +1,40 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = QMake +description = QMake Manual +url = http://doc.trolltech.com/4.5 + +indexes = $QT_BUILD_TREE/doc-build/html-qt/qt.index + +qhp.projects = qmake + +qhp.qmake.file = qmake.qhp +qhp.qmake.namespace = com.trolltech.qmake.450 +qhp.qmake.virtualFolder = qdoc +qhp.qmake.indexTitle = QMake Manual +qhp.qmake.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png +qhp.qmake.filterAttributes = qt 4.5.0 tools qmake +qhp.qmake.customFilters.qmake.name = qmake Manual +qhp.qmake.customFilters.qmake.filterAttributes = qt tools qmake +qhp.qmake.subprojects = manual +qhp.qmake.subprojects.manual.title = Manual +qhp.qmake.subprojects.manual.indexTitle = qmake Manual +qhp.qmake.subprojects.manual.selectors = fake:page + +language = Cpp + +sources = $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc + +sources.fileextensions = "*.cpp *.qdoc" + +exampledirs = $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/doc/src + +imagedirs = $QT_SOURCE_TREE/doc/src/images +outputdir = $QT_BUILD_TREE/doc-build/html-qmake +base = file:$QT_BUILD_TREE/doc-build/html-qmake diff --git a/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf b/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf new file mode 100644 index 0000000..0389386 --- /dev/null +++ b/tools/qdoc3/test/qt-api-only-with-xcode.qdocconf @@ -0,0 +1,29 @@ +include(qt-build-docs-with-xcode.qdocconf) + +# Ensures that the generated index contains a URL that can be used by the +# tools documentation (assistant.qdocconf, designer.qdocconf, linguist.qdocconf, +# qmake.qdocconf). + +url = ./ + +# Ensures that the documentation for the tools is not included in the generated +# .qhp file. + +qhp.Qt.excluded = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc \ + $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc \ + $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc \ + $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc + +outputdir = $QT_BUILD_TREE/doc-build/html-qt +base = file:$QT_BUILD_TREE/doc-build/html-qt diff --git a/tools/qdoc3/test/qt-api-only.qdocconf b/tools/qdoc3/test/qt-api-only.qdocconf new file mode 100644 index 0000000..2e91ba2 --- /dev/null +++ b/tools/qdoc3/test/qt-api-only.qdocconf @@ -0,0 +1,30 @@ +include(qt-build-docs.qdocconf) + +# Ensures that the generated index contains a URL that can be used by the +# tools documentation (assistant.qdocconf, designer.qdocconf, linguist.qdocconf, +# qmake.qdocconf). + +url = ./ + +# Ensures that the documentation for the tools is not included in the generated +# .qhp file. + +qhp.Qt.excluded = $QT_SOURCE_TREE/doc/src/assistant-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/simpletextviewer.qdoc \ + $QT_SOURCE_TREE/doc/src/designer-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/calculatorform.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/customwidgetplugin.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/taskmenuextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/containerextension.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockbuilder.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/worldtimeclockplugin.qdoc \ + $QT_SOURCE_TREE/doc/src/linguist-manual.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/hellotr.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/arrowpad.qdoc \ + $QT_SOURCE_TREE/doc/src/examples/trollprint.qdoc \ + $QT_SOURCE_TREE/doc/src/qmake-manual.qdoc + +outputdir = $QT_BUILD_TREE/doc-build/html-qt +tagfile = $QT_BUILD_TREE/doc-build/html-qt/qt.tags +base = file:$QT_BUILD_TREE/doc-build/html-qt diff --git a/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf b/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf new file mode 100644 index 0000000..e4be476 --- /dev/null +++ b/tools/qdoc3/test/qt-build-docs-with-xcode.qdocconf @@ -0,0 +1,3 @@ +include( qt-build-docs.qdocconf ) + +HTML.generatemacrefs = "true" diff --git a/tools/qdoc3/test/qt-build-docs.qdocconf b/tools/qdoc3/test/qt-build-docs.qdocconf new file mode 100644 index 0000000..a085768 --- /dev/null +++ b/tools/qdoc3/test/qt-build-docs.qdocconf @@ -0,0 +1,109 @@ +include(compat.qdocconf) +include(macros.qdocconf) +include(qt-cpp-ignore.qdocconf) +include(qt-html-templates.qdocconf) +include(qt-defines.qdocconf) + +project = Qt +description = Qt Reference Documentation +url = http://doc.trolltech.com/4.5 + +edition.Console.modules = QtCore QtDBus QtNetwork QtScript QtSql QtXml \ + QtXmlPatterns QtTest +edition.Desktop.modules = QtCore QtDBus QtGui QtNetwork QtOpenGL QtScript 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.450 +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/trolltech-logo \ + images/taskmenuextension-example.png \ + images/coloreditorfactoryimage.png \ + images/dynamiclayouts-example.png \ + images/stylesheet-coffee-plastique.png + +qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.5.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.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 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 = $QT_SOURCE_TREE/src \ + $QT_SOURCE_TREE/extensions/activeqt \ + $QT_SOURCE_TREE/tools/assistant/lib \ + $QT_SOURCE_TREE/tools/assistant/compat/lib \ + $QT_SOURCE_TREE/tools/designer/src/uitools \ + $QT_SOURCE_TREE/tools/designer/src/lib/extension \ + $QT_SOURCE_TREE/tools/designer/src/lib/sdk \ + $QT_SOURCE_TREE/tools/designer/src/lib/uilib \ + $QT_SOURCE_TREE/tools/qtestlib/src \ + $QT_SOURCE_TREE/tools/qdbus/src +sourcedirs = $QT_SOURCE_TREE/src \ + $QT_SOURCE_TREE/doc/src \ + $QT_SOURCE_TREE/extensions/activeqt \ + $QT_SOURCE_TREE/tools/assistant/lib \ + $QT_SOURCE_TREE/tools/assistant/compat/lib \ + $QT_SOURCE_TREE/tools/designer/src/uitools \ + $QT_SOURCE_TREE/tools/designer/src/lib/extension \ + $QT_SOURCE_TREE/tools/designer/src/lib/sdk \ + $QT_SOURCE_TREE/tools/designer/src/lib/uilib \ + $QT_SOURCE_TREE/tools/qtestlib/src \ + $QT_SOURCE_TREE/tools/qdbus + +excludedirs = $QT_SOURCE_TREE/src/3rdparty/clucene \ + $QT_SOURCE_TREE/src/3rdparty/des \ + $QT_SOURCE_TREE/src/3rdparty/freetype \ + $QT_SOURCE_TREE/src/3rdparty/harfbuzz \ + $QT_SOURCE_TREE/src/3rdparty/kdebase \ + $QT_SOURCE_TREE/src/3rdparty/libjpeg \ + $QT_SOURCE_TREE/src/3rdparty/libmng \ + $QT_SOURCE_TREE/src/3rdparty/libpng \ + $QT_SOURCE_TREE/src/3rdparty/libtiff \ + $QT_SOURCE_TREE/src/3rdparty/md4 \ + $QT_SOURCE_TREE/src/3rdparty/md5 \ + $QT_SOURCE_TREE/src/3rdparty/patches \ + $QT_SOURCE_TREE/src/3rdparty/sha1 \ + $QT_SOURCE_TREE/src/3rdparty/sqlite \ + $QT_SOURCE_TREE/src/3rdparty/webkit/JavaScriptCore \ + $QT_SOURCE_TREE/src/3rdparty/webkit/WebCore \ + $QT_SOURCE_TREE/src/3rdparty/wintab \ + $QT_SOURCE_TREE/src/3rdparty/zlib \ + $QT_SOURCE_TREE/doc/src/snippets + +sources.fileextensions = "*.cpp *.qdoc *.mm" +examples.fileextensions = "*.cpp *.h *.js *.xq *.svg *.xml *.ui *.qhp *.qhcp" + +exampledirs = $QT_SOURCE_TREE/doc/src \ + $QT_SOURCE_TREE/examples \ + $QT_SOURCE_TREE/examples/tutorials \ + $QT_SOURCE_TREE \ + $QT_SOURCE_TREE/qmake/examples +imagedirs = $QT_SOURCE_TREE/doc/src/images \ + $QT_SOURCE_TREE/examples +outputdir = $QT_BUILD_TREE/doc/html +tagfile = $QT_BUILD_TREE/doc/html/qt.tags +base = file:$QT_BUILD_TREE/doc/html diff --git a/tools/qdoc3/test/qt-cpp-ignore.qdocconf b/tools/qdoc3/test/qt-cpp-ignore.qdocconf new file mode 100644 index 0000000..107c692 --- /dev/null +++ b/tools/qdoc3/test/qt-cpp-ignore.qdocconf @@ -0,0 +1,87 @@ +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_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_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_NETWORK_EXPORT \ + Q_OPENGL_EXPORT \ + Q_OUTOFLINE_TEMPLATE \ + Q_SQL_EXPORT \ + Q_SVG_EXPORT \ + Q_SCRIPT_EXPORT \ + Q_SCRIPTTOOLS_EXPORT \ + Q_TESTLIB_EXPORT \ + Q_TYPENAME \ + Q_XML_EXPORT \ + Q_XMLSTREAM_EXPORT \ + Q_XMLPATTERNS_EXPORT \ + QDBUS_EXPORT \ + QT_BEGIN_NAMESPACE \ + QT_BEGIN_INCLUDE_NAMESPACE \ + QT_END_NAMESPACE \ + QT_END_INCLUDE_NAMESPACE \ + PHONON_EXPORT +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 diff --git a/tools/qdoc3/test/qt-defines.qdocconf b/tools/qdoc3/test/qt-defines.qdocconf new file mode 100644 index 0000000..b22727f --- /dev/null +++ b/tools/qdoc3/test/qt-defines.qdocconf @@ -0,0 +1,26 @@ +defines = Q_QDOC \ + QT_.*_SUPPORT \ + QT_.*_LIB \ + QT_COMPAT \ + QT_KEYPAD_NAVIGATION \ + QT3_SUPPORT \ + Q_WS_.* \ + Q_OS_.* \ + Q_BYTE_ORDER \ + QT_DEPRECATED \ + Q_NO_USING_KEYWORD \ + __cplusplus + +versionsym = QT_VERSION_STR + +codeindent = 1 + +# Files not referenced in any qdoc file (last four needed by qtdemo) +# See also qhp.Qt.extraFiles +extraimages.HTML = qt-logo \ + trolltech-logo \ + taskmenuextension-example.png \ + coloreditorfactoryimage.png \ + dynamiclayouts-example.png \ + stylesheet-coffee-plastique.png +
\ No newline at end of file diff --git a/tools/qdoc3/test/qt-for-jambi.qdocconf b/tools/qdoc3/test/qt-for-jambi.qdocconf new file mode 100644 index 0000000..2285e0e --- /dev/null +++ b/tools/qdoc3/test/qt-for-jambi.qdocconf @@ -0,0 +1,12 @@ +include(qt.qdocconf) + +# Comment out for full run: +# sourcedirs = $QTDIR/doc/src $QTDIR/src/corelib/tools + +sourcedirs += $JAMBI/doc/src +exampledirs += $JAMBI/com/trolltech/examples + +macro.QJ = "Qt Jambi" +macro.QC = "Qt/C++" + +outputformats = diff --git a/tools/qdoc3/test/qt-html-templates.qdocconf b/tools/qdoc3/test/qt-html-templates.qdocconf new file mode 100644 index 0000000..f09192a --- /dev/null +++ b/tools/qdoc3/test/qt-html-templates.qdocconf @@ -0,0 +1,32 @@ +HTML.stylesheets = classic.css +HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \ + "<tr>\n" \ + "<td align=\"left\" valign=\"top\" width=\"32\">" \ + "<a href=\"http://qtsoftware.com/products/qt\"><img src=\"images/qt-logo.png\" align=\"left\" border=\"0\" /></a>" \ + "</td>\n" \ + "<td width=\"1\"> </td>" \ + "<td class=\"postheader\" valign=\"center\">" \ + "<a href=\"index.html\">" \ + "<font color=\"#004faf\">Home</font></a> ·" \ + " <a href=\"namespaces.html\">" \ + "<font color=\"#004faf\">All Namespaces</font></a> ·" \ + " <a href=\"classes.html\">" \ + "<font color=\"#004faf\">All Classes</font></a> ·" \ + " <a href=\"mainclasses.html\">" \ + "<font color=\"#004faf\">Main Classes</font></a> ·" \ + " <a href=\"groups.html\">" \ + "<font color=\"#004faf\">Grouped Classes</font></a> ·" \ + " <a href=\"modules.html\">" \ + "<font color=\"#004faf\">Modules</font></a> ·" \ + " <a href=\"functions.html\">" \ + "<font color=\"#004faf\">Functions</font></a>" \ + "</td>\n" \ + "<td align=\"right\" valign=\"top\" width=\"230\"></td></tr></table>" + +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"30%\" align=\"left\">Copyright © %THISYEAR% Nokia Corporation " \ + "and/or its subsidiary(-ies)</td>\n" \ + "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \ + "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt \\version</div></td>\n" \ + "</tr></table></div></address>" diff --git a/tools/qdoc3/test/qt-inc.qdocconf b/tools/qdoc3/test/qt-inc.qdocconf new file mode 100644 index 0000000..97893dc --- /dev/null +++ b/tools/qdoc3/test/qt-inc.qdocconf @@ -0,0 +1,146 @@ +include(compat.qdocconf) +include(macros.qdocconf) + +project = Qt +description = Qt Reference Documentation +url = http://doc.trolltech.com/4.5 + +edition.Console = QtCore QtNetwork QtSql QtXml QtScript QtTest +edition.Desktop = QtCore QtGui QtNetwork QtOpenGL QtSql QtSvg QtXml QtScript \ + QtDesigner QtAssistant Qt3Support QAxContainer \ + QAxServer QtUiTools QtTest QtDBus +edition.DesktopLight = QtCore QtGui Qt3SupportLight QtTest + +language = Cpp + +norecursion = true +sources.fileextensions = "*.cpp *.qdoc" +sourcedirs = $QDOC_CURRENT_DIR +headerdirs = $QDOC_CURRENT_DIR +exampledirs = $QTDIR/doc/src \ + $QTDIR/examples \ + $QTDIR \ + $QTDIR/qmake/examples +imagedirs = $QTDIR/doc/src/images \ + $QTDIR/examples +outputdir = $QTDIR/doc/html +indexdir = $QTDIR/doc/indexes +indexes = $QDOC_INPUT_INDEXES +outputindex = $QDOC_OUTPUT_INDEX +base = file:$QTDIR/doc/html +versionsym = QT_VERSION_STR +defines = Q_QDOC \ + QT_.*_SUPPORT \ + QT_.*_LIB \ + QT_COMPAT \ + QT_KEYPAD_NAVIGATION \ + QT3_SUPPORT \ + Q_WS_.* \ + Q_OS_.* \ + Q_BYTE_ORDER \ + __cplusplus + +codeindent = 1 +extraimages.HTML = qt-logo \ + trolltech-logo + +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_SQL \ + QM_EXPORT_TABLE \ + QM_EXPORT_WORKSPACE \ + QM_EXPORT_XML \ + QT_ASCII_CAST_WARN \ + QT_BEGIN_HEADER \ + QT_DESIGNER_STATIC \ + QT_END_HEADER \ + QT_WIDGET_PLUGIN_EXPORT \ + Q_COMPAT_EXPORT \ + Q_CORE_EXPORT \ + Q_EXPLICIT \ + Q_EXPORT \ + Q_EXPORT_CODECS_CN \ + Q_EXPORT_CODECS_JP \ + Q_EXPORT_CODECS_KR \ + Q_EXPORT_PLUGIN \ + Q_GFX_INLINE \ + 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 \ + Q_INLINE_TEMPLATE \ + Q_NETWORK_EXPORT \ + Q_OPENGL_EXPORT \ + Q_OUTOFLINE_TEMPLATE \ + Q_SQL_EXPORT \ + Q_SVG_EXPORT \ + Q_SCRIPT_EXPORT \ + Q_TESTLIB_EXPORT \ + Q_TYPENAME \ + Q_XML_EXPORT \ + QDBUS_EXPORT +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 \ + Q_DUMMY_COMPARISON_OPERATOR \ + Q_ENUMS \ + Q_FLAGS \ + Q_INTERFACES \ + __attribute__ + +HTML.stylesheets = $QTDIR/util/qdoc3/test/classic.css +HTML.postheader = "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n" \ + "<tr>\n" \ + "<td align=\"left\" valign=\"top\" width=\"32\">" \ + "<a href=\"http://qtsoftware.com/products/qt\"><img src=\"images/qt-logo.png\" align=\"left\" width=\"32\" height=\"32\" border=\"0\" /></a>" \ + "</td>\n" \ + "<td width=\"1\"> </td>" \ + "<td class=\"postheader\" valign=\"center\">" \ + "<a href=\"index.html\">" \ + "<font color=\"#004faf\">Home</font></a> ·" \ + " <a href=\"classes.html\">" \ + "<font color=\"#004faf\">All Classes</font></a> ·" \ + " <a href=\"mainclasses.html\">" \ + "<font color=\"#004faf\">Main Classes</font></a> ·" \ + " <a href=\"groups.html\">" \ + "<font color=\"#004faf\">Grouped Classes</font></a> ·" \ + " <a href=\"modules.html\">" \ + "<font color=\"#004faf\">Modules</font></a> ·" \ + " <a href=\"functions.html\">" \ + "<font color=\"#004faf\">Functions</font></a>" \ + "</td>\n" \ + "<td align=\"right\" valign=\"top\" width=\"230\"><a href=\"http://qtsoftware.com\"><img src=\"images/trolltech-logo.png\" align=\"right\" width=\"203\" height=\"32\" border=\"0\" /></a></td></tr></table>" + +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"30%\">Copyright © %THISYEAR% <a href=\"trolltech.html\">Trolltech</a></td>\n" \ + "<td width=\"40%\" align=\"center\"><a href=\"trademarks.html\">Trademarks</a></td>\n" \ + "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt \\version</div></td>\n" \ + "</tr></table></div></address>" diff --git a/tools/qdoc3/test/qt-linguist.qdocconf b/tools/qdoc3/test/qt-linguist.qdocconf new file mode 100644 index 0000000..7564226 --- /dev/null +++ b/tools/qdoc3/test/qt-linguist.qdocconf @@ -0,0 +1,4 @@ +include(qt.qdocconf) + +outputdir = $QTDIR/doc/linguist +outputformats = Linguist diff --git a/tools/qdoc3/test/qt-webxml.qdocconf b/tools/qdoc3/test/qt-webxml.qdocconf new file mode 100644 index 0000000..3ad0457 --- /dev/null +++ b/tools/qdoc3/test/qt-webxml.qdocconf @@ -0,0 +1,11 @@ +include(qt.qdocconf) + +quotinginformation = true +imagedirs = $QTDIR/doc/src/images \ + $QTDIR/examples + +outputdir = $QTDIR/doc/webxml +outputformats = WebXML + +generateindex = true +url = . diff --git a/tools/qdoc3/test/qt-with-extensions.qdocconf b/tools/qdoc3/test/qt-with-extensions.qdocconf new file mode 100644 index 0000000..45188c7 --- /dev/null +++ b/tools/qdoc3/test/qt-with-extensions.qdocconf @@ -0,0 +1,8 @@ +include( qt.qdocconf ) + +project = Qt with Extensions +headerdirs += $QUICK/src/extensions +headers += qsa/date.h +sourcedirs += $QUICK/src/extensions +spurious = ".*" +outputformats = "" diff --git a/tools/qdoc3/test/qt-with-xcode.qdocconf b/tools/qdoc3/test/qt-with-xcode.qdocconf new file mode 100644 index 0000000..932f6d9 --- /dev/null +++ b/tools/qdoc3/test/qt-with-xcode.qdocconf @@ -0,0 +1,3 @@ +include( qt.qdocconf ) + +HTML.generatemacrefs = "true" diff --git a/tools/qdoc3/test/qt.qdocconf b/tools/qdoc3/test/qt.qdocconf new file mode 100644 index 0000000..298311e --- /dev/null +++ b/tools/qdoc3/test/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://doc.trolltech.com/4.5 + +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.450 +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/trolltech-logo \ + images/taskmenuextension-example.png \ + images/coloreditorfactoryimage.png \ + images/dynamiclayouts-example.png \ + images/stylesheet-coffee-plastique.png + +qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.5.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.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 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 +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/tools/qdoc3/test/standalone-eclipse-integration.qdocconf b/tools/qdoc3/test/standalone-eclipse-integration.qdocconf new file mode 100644 index 0000000..c3c4291 --- /dev/null +++ b/tools/qdoc3/test/standalone-eclipse-integration.qdocconf @@ -0,0 +1,11 @@ +include(eclipse-integration.qdocconf) + +macro.theEclipseIntegration = the Qt Eclipse Integration +macro.TheEclipseIntegration = The Qt Eclipse Integration + +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"40%\" align="left">Copyright © 2009 <a href=\"http://doc.trolltech.com\">Nokia Corporation</a></td>\n" \ + "<td width=\"30%\" align=\"center\"><a href=\"http://doc.trolltech.com/trademarks.html\">Trademarks</a></td>\n" \ + "<td width=\"40%\" align=\"right\"><div align=\"right\">Qt Eclipse Integration 1.4.3</div></td>\n" \ + "</tr></table></div></address>" diff --git a/tools/qdoc3/text.cpp b/tools/qdoc3/text.cpp new file mode 100644 index 0000000..0b0a8d7 --- /dev/null +++ b/tools/qdoc3/text.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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 ( first == 0 ) { + first = new Atom( atom.type(), atom.string() ); + last = first; + } else { + last = new Atom( last, atom.type(), atom.string() ); + } + 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 ) + str += atom->string(); + atom = atom->next(); + } + return str; +} + +Text Text::subText( Atom::Type left, Atom::Type right, const Atom *from ) const +{ + const Atom *begin = from ? from : firstAtom(); + const Atom *end; + + while ( begin != 0 && begin->type() != left ) + begin = begin->next(); + if ( begin != 0 ) + begin = begin->next(); + + end = begin; + while ( end != 0 && end->type() != right ) + end = end->next(); + if ( end == 0 ) + begin = 0; + 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/tools/qdoc3/text.h b/tools/qdoc3/text.h new file mode 100644 index 0000000..82506df --- /dev/null +++ b/tools/qdoc3/text.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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) const; + void dump() const; + + 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: + void clear(); + + 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/tools/qdoc3/tokenizer.cpp b/tools/qdoc3/tokenizer.cpp new file mode 100644 index 0000000..24b1b5a --- /dev/null +++ b/tools/qdoc3/tokenizer.cpp @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "tokenizer.h" + +#include <qdebug.h> +#include <qfile.h> +#include <qhash.h> +#include <qregexp.h> +#include <qstring.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_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; + +/* + 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, FILE *in) +{ + init(); + QFile file; + file.open(in, QIODevice::ReadOnly); + yyIn = file.readAll(); + file.close(); + 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); + + 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.replace(*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); + 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.right(1)[0] == QChar(')')) + return isTrue(t.mid(1, t.length() - 2)); + + if (definedX->exactMatch(t)) + return defines->exactMatch(definedX->cap(1)); + else + return !falsehoods->exactMatch(t); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/tokenizer.h b/tools/qdoc3/tokenizer.h new file mode 100644 index 0000000..745c427 --- /dev/null +++ b/tools/qdoc3/tokenizer.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + tokenizer.h +*/ + +#ifndef TOKENIZER_H +#define TOKENIZER_H + +#include <qstack.h> +#include <qstring.h> + +#include <stdio.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_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, FILE *in); + + ~Tokenizer(); + + int getToken(); + void setParsingFnOrMacro(bool macro) { parsingMacro = macro; } + bool parsingFnOrMacro() const { return parsingMacro; } + + const Location &location() const { return yyTokLoc; } + QString previousLexeme() const { return QString(yyPrevLex); } + QString lexeme() const { return QString(yyLex); } + 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 explicitely 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/tools/qdoc3/tr.h b/tools/qdoc3/tr.h new file mode 100644 index 0000000..0966e45 --- /dev/null +++ b/tools/qdoc3/tr.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + tr.h +*/ + +#ifndef TR_H +#define TR_H + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +inline QString tr( const char *sourceText, const char * /* comment */ = 0 ) +{ + return QString( QLatin1String(sourceText) ); +} + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/tree.cpp b/tools/qdoc3/tree.cpp new file mode 100644 index 0000000..0fbd438 --- /dev/null +++ b/tools/qdoc3/tree.cpp @@ -0,0 +1,2012 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + tree.cpp +*/ + +#include <QtCore> +#include <QDomDocument> + +#include "atom.h" +#include "doc.h" +#include "htmlgenerator.h" +#include "location.h" +#include "node.h" +#include "text.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +struct InheritanceBound +{ + Node::Access access; + QStringList basePath; + QString dataTypeWithTemplateArgs; + InnerNode *parent; + + InheritanceBound() + : access(Node::Public) { } + InheritanceBound(Node::Access access0, + const QStringList& basePath0, + const QString &dataTypeWithTemplateArgs0, + InnerNode *parent) + : access(access0), basePath(basePath0), + dataTypeWithTemplateArgs(dataTypeWithTemplateArgs0), + parent(parent) { } +}; + +struct Target +{ + Node *node; + Atom *atom; + int priority; +}; + +typedef QMap<PropertyNode::FunctionRole, QString> RoleMap; +typedef QMap<PropertyNode *, RoleMap> PropertyMap; +typedef QMultiMap<QString, Node *> GroupMap; +typedef QMultiHash<QString, FakeNode *> FakeNodeHash; +typedef QMultiHash<QString, Target> TargetHash; + +class TreePrivate +{ +public: + QMap<ClassNode *, QList<InheritanceBound> > unresolvedInheritanceMap; + PropertyMap unresolvedPropertyMap; + GroupMap groupMap; + QMultiMap<QString, QString> publicGroupMap; + FakeNodeHash fakeNodesByTitle; + TargetHash targetHash; + QList<QPair<ClassNode*,QString> > basesList; + QList<QPair<FunctionNode*,QString> > relatedList; +}; + +/*! + \class Tree + */ + +/*! + The default constructor is the only constructor. + */ +Tree::Tree() + : roo(0, "") +{ + priv = new TreePrivate; +} + +/*! + The destructor deletes the internal, private tree. + */ +Tree::~Tree() +{ + delete priv; +} + +/*! + */ +Node *Tree::findNode(const QStringList &path, Node *relative, int findFlags) +{ + return const_cast<Node*>(const_cast<const Tree*>(this)->findNode(path, + relative, + findFlags)); +} + +/*! + */ +const Node *Tree::findNode(const QStringList &path, + const Node *relative, + int findFlags) const +{ + if (!relative) + relative = root(); + + do { + const Node *node = relative; + int i; + + for (i = 0; i < path.size(); ++i) { + if (node == 0 || !node->isInnerNode()) + break; + + const Node *next = + static_cast<const InnerNode*>(node)->findNode(path.at(i)); + if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) + next = static_cast<const InnerNode*>(node)->findEnumNodeForValue(path.at(i)); + + if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node)); + foreach (const Node *baseClass, baseClasses) { + next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i)); + if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) + next = static_cast<const InnerNode *>(baseClass) + ->findEnumNodeForValue(path.at(i)); + if (next) + break; + } + } + node = next; + } + if (node && i == path.size() + && (!(findFlags & NonFunction) || node->type() != Node::Function + || ((FunctionNode *)node)->metaness() == FunctionNode::MacroWithoutParams)) + return node; + relative = relative->parent(); + } while (relative); + + return 0; +} + +/*! + */ +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)); +} + +/*! + */ +const Node *Tree::findNode(const QStringList &path, + Node::Type type, + const Node *relative, + int findFlags) const +{ + const Node *node = findNode(path, relative, findFlags); + if (node != 0 && node->type() == type) + return node; + return 0; +} + +/*! + */ +FunctionNode *Tree::findFunctionNode(const QStringList& path, + Node *relative, + int findFlags) +{ + return const_cast<FunctionNode *>( + const_cast<const Tree *>(this)->findFunctionNode(path, relative, findFlags)); +} + +/*! + */ +const FunctionNode *Tree::findFunctionNode(const QStringList &path, + const Node *relative, + int findFlags) const +{ + if (!relative) + relative = root(); + do { + const Node *node = relative; + int i; + + for (i = 0; i < path.size(); ++i) { + if (node == 0 || !node->isInnerNode()) + break; + + const Node *next; + if (i == path.size() - 1) + next = ((InnerNode *) node)->findFunctionNode(path.at(i)); + else + next = ((InnerNode *) node)->findNode(path.at(i)); + + if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast<const ClassNode *>(node)); + foreach (const Node *baseClass, baseClasses) { + if (i == path.size() - 1) + next = static_cast<const InnerNode *>(baseClass)-> + findFunctionNode(path.at(i)); + else + next = static_cast<const InnerNode *>(baseClass)->findNode(path.at(i)); + + if (next) + break; + } + } + + node = next; + } + if (node && i == path.size() && node->type() == Node::Function) { + // CppCodeParser::processOtherMetaCommand ensures that reimplemented + // functions are private. + const FunctionNode *func = static_cast<const FunctionNode*>(node); + + while (func->access() == Node::Private) { + const FunctionNode *from = func->reimplementedFrom(); + if (from != 0) { + if (from->access() != Node::Private) + return from; + else + func = from; + } else + break; + } + return func; + } + relative = relative->parent(); + } while (relative); + + return 0; +} + +/*! + */ +FunctionNode *Tree::findFunctionNode(const QStringList &parentPath, + const FunctionNode *clone, + Node *relative, + int findFlags) +{ + return const_cast<FunctionNode *>( + const_cast<const Tree *>(this)->findFunctionNode(parentPath, + clone, + relative, + findFlags)); +} + +/*! + */ +const FunctionNode *Tree::findFunctionNode(const QStringList &parentPath, + const FunctionNode *clone, + const Node *relative, + int findFlags) const +{ + const Node *parent = findNode(parentPath, relative, findFlags); + if (parent == 0 || !parent->isInnerNode()) { + return 0; + } else { + return ((InnerNode *)parent)->findFunctionNode(clone); + } +} + +static const int NumSuffixes = 3; +static const char * const suffixes[NumSuffixes] = { "", "s", "es" }; + +/*! + */ +const FakeNode *Tree::findFakeNodeByTitle(const QString &title) const +{ + for (int pass = 0; pass < NumSuffixes; ++pass) { + FakeNodeHash::const_iterator i = + priv->fakeNodesByTitle.find(Doc::canonicalTitle(title + suffixes[pass])); + if (i != priv->fakeNodesByTitle.constEnd()) { + FakeNodeHash::const_iterator j = i; + ++j; + if (j != priv->fakeNodesByTitle.constEnd() && j.key() == i.key()) { + QList<Location> internalLocations; + while (j != priv->fakeNodesByTitle.constEnd()) { + if (j.key() == i.key() && j.value()->url().isEmpty()) + internalLocations.append(j.value()->doc().location()); + ++j; + } + if (internalLocations.size() > 0) { + i.value()->doc().location().warning( + tr("Page '%1' defined in more than one location:").arg(title)); + foreach (const Location &location, internalLocations) + location.warning(tr("(defined here)")); + } + } + return i.value(); + } + } + return 0; +} + +/*! + */ +const Node* +Tree::findUnambiguousTarget(const QString &target, Atom *&atom) const +{ + Target bestTarget = {0, 0, INT_MAX}; + int numBestTargets = 0; + + for (int pass = 0; pass < NumSuffixes; ++pass) { + TargetHash::const_iterator i = + priv->targetHash.find(Doc::canonicalTitle(target + suffixes[pass])); + if (i != priv->targetHash.constEnd()) { + TargetHash::const_iterator j = i; + do { + const Target &candidate = j.value(); + if (candidate.priority < bestTarget.priority) { + bestTarget = candidate; + numBestTargets = 1; + } else if (candidate.priority == bestTarget.priority) { + ++numBestTargets; + } + ++j; + } while (j != priv->targetHash.constEnd() && j.key() == i.key()); + + if (numBestTargets == 1) { + atom = bestTarget.atom; + return bestTarget.node; + } + } + } + return 0; +} + +/*! + */ +Atom *Tree::findTarget(const QString &target, const Node *node) const +{ + for (int pass = 0; pass < NumSuffixes; ++pass) { + QString key = Doc::canonicalTitle(target + suffixes[pass]); + TargetHash::const_iterator i = priv->targetHash.find(key); + + if (i != priv->targetHash.constEnd()) { + do { + if (i.value().node == node) + return i.value().atom; + ++i; + } while (i != priv->targetHash.constEnd() && i.key() == key); + } + } + return 0; +} + +/*! + */ +void Tree::addBaseClass(ClassNode *subclass, Node::Access access, + const QStringList &basePath, + const QString &dataTypeWithTemplateArgs, + InnerNode *parent) +{ + priv->unresolvedInheritanceMap[subclass].append( + InheritanceBound(access, + basePath, + dataTypeWithTemplateArgs, + parent) + ); +} + + +/*! + */ +void Tree::addPropertyFunction(PropertyNode *property, + const QString &funcName, + PropertyNode::FunctionRole funcRole) +{ + priv->unresolvedPropertyMap[property].insert(funcRole, funcName); +} + +/*! + */ +void Tree::addToGroup(Node *node, const QString &group) +{ + priv->groupMap.insert(group, node); +} + +/*! + */ +QMultiMap<QString, Node *> Tree::groups() const +{ + return priv->groupMap; +} + +/*! + */ +void Tree::addToPublicGroup(Node *node, const QString &group) +{ + priv->publicGroupMap.insert(node->name(), group); + addToGroup(node, group); +} + +/*! + */ +QMultiMap<QString, QString> Tree::publicGroups() const +{ + return priv->publicGroupMap; +} + +/*! + */ +void Tree::resolveInheritance(NamespaceNode *rootNode) +{ + if (!rootNode) + rootNode = root(); + + for (int pass = 0; pass < 2; pass++) { + NodeList::ConstIterator c = rootNode->childNodes().begin(); + while (c != rootNode->childNodes().end()) { + if ((*c)->type() == Node::Class) + resolveInheritance(pass, (ClassNode *) *c); + else if ((*c)->type() == Node::Namespace) { + NamespaceNode *ns = static_cast<NamespaceNode*>(*c); + resolveInheritance(ns); + } + ++c; + } + if (rootNode == root()) + priv->unresolvedInheritanceMap.clear(); + } +} + +/*! + */ +void Tree::resolveProperties() +{ + PropertyMap::ConstIterator propEntry; + + propEntry = priv->unresolvedPropertyMap.begin(); + while (propEntry != priv->unresolvedPropertyMap.end()) { + PropertyNode *property = propEntry.key(); + InnerNode *parent = property->parent(); + QString getterName = (*propEntry)[PropertyNode::Getter]; + QString setterName = (*propEntry)[PropertyNode::Setter]; + QString resetterName = (*propEntry)[PropertyNode::Resetter]; + + 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); + } + } + } + ++c; + } + ++propEntry; + } + + propEntry = priv->unresolvedPropertyMap.begin(); + while (propEntry != priv->unresolvedPropertyMap.end()) { + PropertyNode *property = propEntry.key(); + // redo it to set the property functions + if (property->overriddenFrom()) + property->setOverriddenFrom(property->overriddenFrom()); + ++propEntry; + } + + priv->unresolvedPropertyMap.clear(); +} + +/*! + */ +void Tree::resolveInheritance(int pass, ClassNode *classe) +{ + if (pass == 0) { + QList<InheritanceBound> bounds = priv->unresolvedInheritanceMap[classe]; + QList<InheritanceBound>::ConstIterator b = bounds.begin(); + while (b != bounds.end()) { + ClassNode *baseClass = (ClassNode*)findNode((*b).basePath, + Node::Class); + if (!baseClass && (*b).parent) + baseClass = (ClassNode*)findNode((*b).basePath, + Node::Class, + (*b).parent); + if (baseClass) + classe->addBaseClass((*b).access, + baseClass, + (*b).dataTypeWithTemplateArgs); + ++b; + } + } + else { + NodeList::ConstIterator c = classe->childNodes().begin(); + while (c != classe->childNodes().end()) { + if ((*c)->type() == Node::Function) { + FunctionNode *func = (FunctionNode *) *c; + FunctionNode *from = findVirtualFunctionInBaseClasses(classe, func); + if (from != 0) { + if (func->virtualness() == FunctionNode::NonVirtual) + func->setVirtualness(FunctionNode::ImpureVirtual); + func->setReimplementedFrom(from); + } + } + else if ((*c)->type() == Node::Property) { + fixPropertyUsingBaseClasses(classe, static_cast<PropertyNode *>(*c)); + } + ++c; + } + } +} + +/*! + */ +void Tree::resolveGroups() +{ + GroupMap::const_iterator i; + QString prevGroup; + for (i = priv->groupMap.constBegin(); i != priv->groupMap.constEnd(); ++i) { + if (i.value()->access() == Node::Private) + continue; + + FakeNode *fake = + static_cast<FakeNode*>(findNode(QStringList(i.key()),Node::Fake)); + if (fake && fake->subType() == FakeNode::Group) { + fake->addGroupMember(i.value()); + } + else { + if (prevGroup != i.key()) + i.value()->doc().location().warning(tr("No such group '%1'").arg(i.key())); + } + + prevGroup = i.key(); + } + + //priv->groupMap.clear(); +} + +/*! + */ +void Tree::resolveTargets() +{ + // need recursion + + foreach (Node *child, roo.childNodes()) { + if (child->type() == Node::Fake) { + FakeNode *node = static_cast<FakeNode *>(child); + priv->fakeNodesByTitle.insert(Doc::canonicalTitle(node->title()), node); + } + + if (child->doc().hasTableOfContents()) { + const QList<Atom *> &toc = child->doc().tableOfContents(); + Target target; + target.node = child; + target.priority = 3; + + for (int i = 0; i < toc.size(); ++i) { + target.atom = toc.at(i); + QString title = Text::sectionHeading(target.atom).toString(); + if (!title.isEmpty()) + priv->targetHash.insert(Doc::canonicalTitle(title), target); + } + } + if (child->doc().hasKeywords()) { + const QList<Atom *> &keywords = child->doc().keywords(); + Target target; + target.node = child; + target.priority = 1; + + for (int i = 0; i < keywords.size(); ++i) { + target.atom = keywords.at(i); + priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + if (child->doc().hasTargets()) { + const QList<Atom *> &toc = child->doc().targets(); + Target target; + target.node = child; + target.priority = 2; + + for (int i = 0; i < toc.size(); ++i) { + target.atom = toc.at(i); + priv->targetHash.insert(Doc::canonicalTitle(target.atom->string()), target); + } + } + } +} + +/*! + */ +void Tree::fixInheritance(NamespaceNode *rootNode) +{ + if (!rootNode) + rootNode = root(); + + NodeList::ConstIterator c = rootNode->childNodes().begin(); + while (c != rootNode->childNodes().end()) { + if ((*c)->type() == Node::Class) + static_cast<ClassNode *>(*c)->fixBaseClasses(); + else if ((*c)->type() == Node::Namespace) { + NamespaceNode *ns = static_cast<NamespaceNode*>(*c); + fixInheritance(ns); + } + ++c; + } +} + +/*! + */ +FunctionNode *Tree::findVirtualFunctionInBaseClasses(ClassNode *classe, + FunctionNode *clone) +{ + QList<RelatedClass>::ConstIterator r = classe->baseClasses().begin(); + while (r != classe->baseClasses().end()) { + FunctionNode *func; + if (((func = findVirtualFunctionInBaseClasses((*r).node, clone)) != 0 || + (func = (*r).node->findFunctionNode(clone)) != 0)) { + if (func->virtualness() != FunctionNode::NonVirtual) + return func; + } + ++r; + } + return 0; +} + +/*! + */ +void Tree::fixPropertyUsingBaseClasses(ClassNode *classe, + PropertyNode *property) +{ + QList<RelatedClass>::const_iterator r = classe->baseClasses().begin(); + while (r != classe->baseClasses().end()) { + PropertyNode *baseProperty = + static_cast<PropertyNode *>(r->node->findNode(property->name(), + Node::Property)); + if (baseProperty) { + fixPropertyUsingBaseClasses(r->node, baseProperty); + property->setOverriddenFrom(baseProperty); + } + else { + fixPropertyUsingBaseClasses(r->node, property); + } + ++r; + } +} + +/*! + */ +NodeList Tree::allBaseClasses(const ClassNode *classe) const +{ + NodeList result; + foreach (const RelatedClass &r, classe->baseClasses()) { + result += r.node; + result += allBaseClasses(r.node); + } + return result; +} + +/*! + */ +void Tree::readIndexes(const QStringList &indexFiles) +{ + foreach (const QString &indexFile, indexFiles) + readIndexFile(indexFile); +} + +/*! + Read the QDomDocument at \a path and get the index from it. + */ +void Tree::readIndexFile(const QString &path) +{ + QFile file(path); + if (file.open(QFile::ReadOnly)) { + QDomDocument document; + document.setContent(&file); + file.close(); + + QDomElement indexElement = document.documentElement(); + QString indexUrl = indexElement.attribute("url", ""); + priv->basesList.clear(); + priv->relatedList.clear(); + + // Scan all elements in the XML file, constructing a map that contains + // base classes for each class found. + + QDomElement child = indexElement.firstChildElement(); + while (!child.isNull()) { + readIndexSection(child, root(), indexUrl); + child = child.nextSiblingElement(); + } + + // Now that all the base classes have been found for this index, + // arrange them into an inheritance hierarchy. + + resolveIndex(); + } +} + +/*! + */ +void Tree::readIndexSection(const QDomElement &element, + InnerNode *parent, + const QString &indexUrl) +{ + QString name = element.attribute("name"); + QString href = element.attribute("href"); + + Node *section; + Location location; + + if (element.nodeName() == "namespace") { + section = new NamespaceNode(parent, name); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + "/" + name.toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(name.toLower() + ".html"); + + } + else if (element.nodeName() == "class") { + section = new ClassNode(parent, name); + priv->basesList.append(QPair<ClassNode*,QString>( + static_cast<ClassNode*>(section), element.attribute("bases"))); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + "/" + name.toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(name.toLower() + ".html"); + + } + else if (element.nodeName() == "page") { + FakeNode::SubType subtype; + if (element.attribute("subtype") == "example") + subtype = FakeNode::Example; + else if (element.attribute("subtype") == "header") + subtype = FakeNode::HeaderFile; + else if (element.attribute("subtype") == "file") + subtype = FakeNode::File; + else if (element.attribute("subtype") == "group") + subtype = FakeNode::Group; + else if (element.attribute("subtype") == "module") + subtype = FakeNode::Module; + else if (element.attribute("subtype") == "page") + subtype = FakeNode::Page; + else if (element.attribute("subtype") == "externalpage") + subtype = FakeNode::ExternalPage; + else + return; + + FakeNode *fakeNode = new FakeNode(parent, name, subtype); + fakeNode->setTitle(element.attribute("title")); + + if (element.hasAttribute("location")) + name = element.attribute("location", ""); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + "/" + name); + else if (!indexUrl.isNull()) + location = Location(name); + + section = fakeNode; + + } + else if (element.nodeName() == "enum") { + EnumNode *enumNode = new EnumNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + "/" + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + QDomElement child = element.firstChildElement("value"); + while (!child.isNull()) { + EnumItem item(child.attribute("name"), child.attribute("value")); + enumNode->addItem(item); + child = child.nextSiblingElement("value"); + } + + section = enumNode; + + } else if (element.nodeName() == "typedef") { + section = new TypedefNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + "/" + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "property") { + section = new PropertyNode(parent, name); + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + "/" + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } else if (element.nodeName() == "function") { + FunctionNode::Virtualness virt; + if (element.attribute("virtual") == "non") + virt = FunctionNode::NonVirtual; + else if (element.attribute("virtual") == "impure") + virt = FunctionNode::ImpureVirtual; + else if (element.attribute("virtual") == "pure") + virt = FunctionNode::PureVirtual; + else + return; + + FunctionNode::Metaness meta; + if (element.attribute("meta") == "plain") + meta = FunctionNode::Plain; + else if (element.attribute("meta") == "signal") + meta = FunctionNode::Signal; + else if (element.attribute("meta") == "slot") + meta = FunctionNode::Slot; + else if (element.attribute("meta") == "constructor") + meta = FunctionNode::Ctor; + else if (element.attribute("meta") == "destructor") + meta = FunctionNode::Dtor; + else if (element.attribute("meta") == "macro") + meta = FunctionNode::MacroWithParams; + else if (element.attribute("meta") == "macrowithparams") + meta = FunctionNode::MacroWithParams; + else if (element.attribute("meta") == "macrowithoutparams") + meta = FunctionNode::MacroWithoutParams; + else + return; + + FunctionNode *functionNode = new FunctionNode(parent, name); + functionNode->setReturnType(element.attribute("return")); + functionNode->setVirtualness(virt); + functionNode->setMetaness(meta); + functionNode->setConst(element.attribute("const") == "true"); + functionNode->setStatic(element.attribute("static") == "true"); + functionNode->setOverload(element.attribute("overload") == "true"); + + if (element.hasAttribute("relates") + && element.attribute("relates") != parent->name()) { + priv->relatedList.append( + QPair<FunctionNode*,QString>(functionNode, + element.attribute("relates"))); + } + + QDomElement child = element.firstChildElement("parameter"); + while (!child.isNull()) { + // Do not use the default value for the parameter; it is not + // required, and has been known to cause problems. + Parameter parameter(child.attribute("left"), + child.attribute("right"), + child.attribute("name"), + ""); // child.attribute("default") + functionNode->addParameter(parameter); + child = child.nextSiblingElement("parameter"); + } + + section = functionNode; + + if (!indexUrl.isEmpty()) + location = + Location(indexUrl + "/" + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "variable") { + section = new VariableNode(parent, name); + + if (!indexUrl.isEmpty()) + location = Location(indexUrl + "/" + parent->name().toLower() + ".html"); + else if (!indexUrl.isNull()) + location = Location(parent->name().toLower() + ".html"); + + } + else if (element.nodeName() == "keyword") { + Target target; + target.node = parent; + target.priority = 1; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else if (element.nodeName() == "target") { + Target target; + target.node = parent; + target.priority = 2; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else if (element.nodeName() == "contents") { + Target target; + target.node = parent; + target.priority = 3; + target.atom = new Atom(Atom::Target, name); + priv->targetHash.insert(name, target); + return; + + } + else + return; + + QString access = element.attribute("access"); + if (access == "public") + section->setAccess(Node::Public); + else if (access == "protected") + section->setAccess(Node::Protected); + else if (access == "private") + section->setAccess(Node::Private); + else + section->setAccess(Node::Public); + + if (element.nodeName() != "page") { + QString threadSafety = element.attribute("threadsafety"); + if (threadSafety == "non-reentrant") + section->setThreadSafeness(Node::NonReentrant); + else if (threadSafety == "reentrant") + section->setThreadSafeness(Node::Reentrant); + else if (threadSafety == "thread safe") + section->setThreadSafeness(Node::ThreadSafe); + else + section->setThreadSafeness(Node::UnspecifiedSafeness); + } + else + section->setThreadSafeness(Node::UnspecifiedSafeness); + + QString status = element.attribute("status"); + if (status == "compat") + section->setStatus(Node::Compat); + else if (status == "obsolete") + section->setStatus(Node::Obsolete); + else if (status == "deprecated") + section->setStatus(Node::Deprecated); + else if (status == "preliminary") + section->setStatus(Node::Preliminary); + else if (status == "commendable") + section->setStatus(Node::Commendable); + else if (status == "internal") + section->setStatus(Node::Internal); + else if (status == "main") + section->setStatus(Node::Main); + else + section->setStatus(Node::Commendable); + + section->setModuleName(element.attribute("module")); + if (!indexUrl.isEmpty()) { + if (indexUrl.startsWith(".")) + section->setUrl(href); + else + section->setUrl(indexUrl + "/" + href); + } + + // Create some content for the node. + QSet<QString> emptySet; + + Doc doc(location, location, " ", emptySet); // placeholder + section->setDoc(doc); + + if (section->isInnerNode()) { + InnerNode *inner = static_cast<InnerNode*>(section); + if (inner) { + QDomElement child = element.firstChildElement(); + + while (!child.isNull()) { + if (element.nodeName() == "class") + readIndexSection(child, inner, indexUrl); + else if (element.nodeName() == "page") + readIndexSection(child, inner, indexUrl); + else if (element.nodeName() == "namespace" && !name.isEmpty()) + // The root node in the index is a namespace with an empty name. + readIndexSection(child, inner, indexUrl); + else + readIndexSection(child, parent, indexUrl); + + child = child.nextSiblingElement(); + } + } + } +} + +/*! + */ +QString Tree::readIndexText(const QDomElement &element) +{ + QString text; + QDomNode child = element.firstChild(); + while (!child.isNull()) { + if (child.isText()) + text += child.toText().nodeValue(); + child = child.nextSibling(); + } + return text; +} + +/*! + */ +void Tree::resolveIndex() +{ + QPair<ClassNode*,QString> pair; + + foreach (pair, priv->basesList) { + foreach (const QString &base, pair.second.split(",")) { + Node *baseClass = root()->findNode(base, Node::Class); + if (baseClass) { + pair.first->addBaseClass(Node::Public, + static_cast<ClassNode*>(baseClass)); + } + } + } + + QPair<FunctionNode*,QString> relatedPair; + + foreach (relatedPair, priv->relatedList) { + Node *classNode = root()->findNode(relatedPair.second, Node::Class); + if (classNode) + relatedPair.first->setRelates(static_cast<ClassNode*>(classNode)); + } +} + +/*! + Generate the index section with the given \a writer for the \a node + specified, returning true if an element was written; otherwise returns + false. + */ +bool Tree::generateIndexSection(QXmlStreamWriter &writer, + const Node *node, + bool generateInternalNodes) const +{ + if (!node->url().isEmpty()) + return false; + + QString nodeName; + switch (node->type()) { + case Node::Namespace: + nodeName = "namespace"; + break; + case Node::Class: + nodeName = "class"; + break; + case Node::Fake: + nodeName = "page"; + break; + case Node::Enum: + nodeName = "enum"; + break; + case Node::Typedef: + nodeName = "typedef"; + break; + case Node::Property: + nodeName = "property"; + break; + case Node::Function: + nodeName = "function"; + break; + case Node::Variable: + nodeName = "variable"; + break; + case Node::Target: + nodeName = "target"; + break; + default: + return false; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + // Do not include private non-internal nodes in the index. + // (Internal public and protected nodes are marked as private + // by qdoc. We can check their internal status to determine + // whether they were really private to begin with.) + if (node->status() == Node::Internal && generateInternalNodes) + access = "internal"; + else + return false; + break; + default: + return false; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + return false; + + writer.writeStartElement(nodeName); + + QXmlStreamAttributes attributes; + writer.writeAttribute("access", access); + + if (node->type() != Node::Fake) { + QString threadSafety; + switch (node->threadSafeness()) { + case Node::NonReentrant: + threadSafety = "non-reentrant"; + break; + case Node::Reentrant: + threadSafety = "reentrant"; + break; + case Node::ThreadSafe: + threadSafety = "thread safe"; + break; + case Node::UnspecifiedSafeness: + default: + threadSafety = "unspecified"; + break; + } + writer.writeAttribute("threadsafety", threadSafety); + } + + QString status; + switch (node->status()) { + case Node::Compat: + status = "compat"; + break; + case Node::Obsolete: + status = "obsolete"; + break; + case Node::Deprecated: + status = "deprecated"; + break; + case Node::Preliminary: + status = "preliminary"; + break; + case Node::Commendable: + status = "commendable"; + break; + case Node::Internal: + status = "internal"; + break; + case Node::Main: + default: + status = "main"; + break; + } + writer.writeAttribute("status", status); + + writer.writeAttribute("name", objName); + QString fullName = fullDocumentName(node); + if (fullName != objName) + writer.writeAttribute("fullname", fullName); + writer.writeAttribute("href", fullDocumentLocation(node)); + if (node->type() != Node::Fake) + 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 FakeNode::Example: + writer.writeAttribute("subtype", "example"); + break; + case FakeNode::HeaderFile: + writer.writeAttribute("subtype", "header"); + break; + case FakeNode::File: + writer.writeAttribute("subtype", "file"); + break; + case FakeNode::Group: + writer.writeAttribute("subtype", "group"); + break; + case FakeNode::Module: + writer.writeAttribute("subtype", "module"); + break; + case FakeNode::Page: + writer.writeAttribute("subtype", "page"); + break; + case FakeNode::ExternalPage: + writer.writeAttribute("subtype", "externalpage"); + 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::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() == FakeNode::ExternalPage) + external = true; + } + + foreach (const Atom *target, inner->doc().targets()) { + QString targetName = target->string(); + if (!external) + targetName = Doc::canonicalTitle(targetName); + + writer.writeStartElement("target"); + writer.writeAttribute("name", targetName); + writer.writeEndElement(); // target + } + } + if (inner->doc().hasKeywords()) { + foreach (const Atom *keyword, inner->doc().keywords()) { + writer.writeStartElement("keyword"); + writer.writeAttribute("name", + Doc::canonicalTitle(keyword->string())); + writer.writeEndElement(); // keyword + } + } + if (inner->doc().hasTableOfContents()) { + for (int i = 0; i < inner->doc().tableOfContents().size(); ++i) { + Atom *item = inner->doc().tableOfContents()[i]; + int level = inner->doc().tableOfContentsLevels()[i]; + + QString title = Text::sectionHeading(item).toString(); + writer.writeStartElement("contents"); + writer.writeAttribute("name", Doc::canonicalTitle(title)); + writer.writeAttribute("title", title); + writer.writeAttribute("level", QString::number(level)); + writer.writeEndElement(); // contents + } + } + + } + else if (node->type() == Node::Function) { + + const FunctionNode *functionNode = static_cast<const FunctionNode*>(node); + // Write a signature attribute for convenience. + QStringList signatureList; + QStringList resolvedParameters; + + foreach (const Parameter ¶meter, functionNode->parameters()) { + QString leftType = parameter.leftType(); + const Node *leftNode = + const_cast<Tree*>(this)->findNode(parameter.leftType().split("::"), + Node::Typedef, 0, SearchBaseClasses|NonFunction); + if (!leftNode) { + leftNode = const_cast<Tree *>(this)->findNode( + parameter.leftType().split("::"), Node::Typedef, + node->parent(), SearchBaseClasses|NonFunction); + } + if (leftNode) { + if (leftNode->type() == Node::Typedef) { + const TypedefNode *typedefNode = + static_cast<const TypedefNode *>(leftNode); + if (typedefNode->associatedEnum()) { + leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">"; + } + } + else + leftType = fullDocumentName(leftNode); + } + resolvedParameters.append(leftType); + signatureList.append(leftType + " " + parameter.name()); + } + + QString signature = functionNode->name()+"("+signatureList.join(", ")+")"; + if (functionNode->isConst()) + signature += " const"; + writer.writeAttribute("signature", signature); + + for (int i = 0; i < functionNode->parameters().size(); ++i) { + Parameter parameter = functionNode->parameters()[i]; + writer.writeStartElement("parameter"); + writer.writeAttribute("left", resolvedParameters[i]); + writer.writeAttribute("right", parameter.rightType()); + writer.writeAttribute("name", parameter.name()); + writer.writeAttribute("default", parameter.defaultValue()); + writer.writeEndElement(); // parameter + } + + } + else if (node->type() == Node::Enum) { + + const EnumNode *enumNode = static_cast<const EnumNode*>(node); + if (enumNode->flagsType()) { + writer.writeAttribute("typedef", + fullDocumentName(enumNode->flagsType())); + } + foreach (const EnumItem &item, enumNode->items()) { + writer.writeStartElement("value"); + writer.writeAttribute("name", item.name()); + writer.writeAttribute("value", item.value()); + writer.writeEndElement(); // value + } + + } + else if (node->type() == Node::Typedef) { + + const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node); + if (typedefNode->associatedEnum()) { + writer.writeAttribute("enum", + fullDocumentName(typedefNode->associatedEnum())); + } + } + + return true; +} + +/*! + */ +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); + + // Recurse to write an element for this child node and all its children. + foreach (const Node *child, inner->childNodes()) + generateIndexSections(writer, child, generateInternalNodes); + +/* + foreach (const Node *child, inner->relatedNodes()) { + QDomElement childElement = generateIndexSections(document, child); + element.appendChild(childElement); + } +*/ + } + writer.writeEndElement(); + } +} + +/*! + Outputs an index file. + */ +void Tree::generateIndex(const QString &fileName, + const QString &url, + const QString &title, + bool generateInternalNodes) const +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeDTD("<!DOCTYPE QDOCINDEX>"); + + writer.writeStartElement("INDEX"); + writer.writeAttribute("url", url); + writer.writeAttribute("title", title); + writer.writeAttribute("version", version()); + + generateIndexSections(writer, root(), generateInternalNodes); + + writer.writeEndElement(); // INDEX + writer.writeEndElement(); // QDOCINDEX + writer.writeEndDocument(); + file.close(); +} + +/*! + Generate the tag file section with the given \a writer for the \a node + specified, returning true if an element was written; otherwise returns + false. + */ +void Tree::generateTagFileCompounds(QXmlStreamWriter &writer, + const InnerNode *inner) const +{ + foreach (const Node *node, inner->childNodes()) { + + if (!node->url().isEmpty()) + continue; + + QString kind; + switch (node->type()) { + case Node::Namespace: + kind = "namespace"; + break; + case Node::Class: + kind = "class"; + break; + case Node::Enum: + case Node::Typedef: + case Node::Property: + case Node::Function: + case Node::Variable: + case Node::Target: + default: + continue; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + default: + continue; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + continue; + + // *** Write the starting tag for the element here. *** + writer.writeStartElement("compound"); + writer.writeAttribute("kind", kind); + + if (node->type() == Node::Class) { + writer.writeTextElement("name", fullDocumentName(node)); + writer.writeTextElement("filename", fullDocumentLocation(node)); + + // Classes contain information about their base classes. + const ClassNode *classNode = static_cast<const ClassNode*>(node); + QList<RelatedClass> bases = classNode->baseClasses(); + foreach (const RelatedClass &related, bases) { + ClassNode *baseClassNode = related.node; + writer.writeTextElement("base", baseClassNode->name()); + } + + // Recurse to write all members. + generateTagFileMembers(writer, static_cast<const InnerNode *>(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast<const InnerNode *>(node)); + } else { + writer.writeTextElement("name", fullDocumentName(node)); + writer.writeTextElement("filename", fullDocumentLocation(node)); + + // Recurse to write all members. + generateTagFileMembers(writer, static_cast<const InnerNode *>(node)); + writer.writeEndElement(); + + // Recurse to write all compounds. + generateTagFileCompounds(writer, static_cast<const InnerNode *>(node)); + } + } +} + +/*! + */ +void Tree::generateTagFileMembers(QXmlStreamWriter &writer, + const InnerNode *inner) const +{ + foreach (const Node *node, inner->childNodes()) { + + if (!node->url().isEmpty()) + continue; + + QString nodeName; + QString kind; + switch (node->type()) { + case Node::Enum: + nodeName = "member"; + kind = "enum"; + break; + case Node::Typedef: + nodeName = "member"; + kind = "typedef"; + break; + case Node::Property: + nodeName = "member"; + kind = "property"; + break; + case Node::Function: + nodeName = "member"; + kind = "function"; + break; + case Node::Namespace: + nodeName = "namespace"; + break; + case Node::Class: + nodeName = "class"; + break; + case Node::Variable: + case Node::Target: + default: + continue; + } + + QString access; + switch (node->access()) { + case Node::Public: + access = "public"; + break; + case Node::Protected: + access = "protected"; + break; + case Node::Private: + default: + continue; + } + + QString objName = node->name(); + + // Special case: only the root node should have an empty name. + if (objName.isEmpty() && node != root()) + continue; + + // *** Write the starting tag for the element here. *** + writer.writeStartElement(nodeName); + if (!kind.isEmpty()) + writer.writeAttribute("kind", kind); + + switch (node->type()) { + + case Node::Class: + writer.writeCharacters(fullDocumentName(node)); + writer.writeEndElement(); + break; + case Node::Namespace: + writer.writeCharacters(fullDocumentName(node)); + writer.writeEndElement(); + break; + case Node::Function: + { + /* + Function nodes contain information about + the type of function being described. + */ + + const FunctionNode *functionNode = + static_cast<const FunctionNode*>(node); + writer.writeAttribute("protection", access); + + switch (functionNode->virtualness()) { + case FunctionNode::NonVirtual: + writer.writeAttribute("virtualness", "non"); + break; + case FunctionNode::ImpureVirtual: + writer.writeAttribute("virtualness", "virtual"); + break; + case FunctionNode::PureVirtual: + writer.writeAttribute("virtual", "pure"); + break; + default: + break; + } + writer.writeAttribute("static", + functionNode->isStatic() ? "yes" : "no"); + + if (functionNode->virtualness() == FunctionNode::NonVirtual) + writer.writeTextElement("type", functionNode->returnType()); + else + writer.writeTextElement("type", + "virtual " + functionNode->returnType()); + + writer.writeTextElement("name", objName); + QStringList pieces = fullDocumentLocation(node).split("#"); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + + // Write a signature attribute for convenience. + QStringList signatureList; + + foreach (const Parameter ¶meter, functionNode->parameters()) { + QString leftType = parameter.leftType(); + const Node *leftNode = const_cast<Tree *>(this)->findNode(parameter.leftType().split("::"), + Node::Typedef, 0, SearchBaseClasses|NonFunction); + if (!leftNode) { + leftNode = const_cast<Tree *>(this)->findNode( + parameter.leftType().split("::"), Node::Typedef, + node->parent(), SearchBaseClasses|NonFunction); + } + if (leftNode) { + const TypedefNode *typedefNode = static_cast<const TypedefNode *>(leftNode); + if (typedefNode->associatedEnum()) { + leftType = "QFlags<"+fullDocumentName(typedefNode->associatedEnum())+">"; + } + } + signatureList.append(leftType + " " + parameter.name()); + } + + QString signature = "("+signatureList.join(", ")+")"; + if (functionNode->isConst()) + signature += " const"; + if (functionNode->virtualness() == FunctionNode::PureVirtual) + signature += " = 0"; + writer.writeTextElement("arglist", signature); + } + writer.writeEndElement(); // member + break; + + case Node::Property: + { + const PropertyNode *propertyNode = static_cast<const PropertyNode*>(node); + writer.writeAttribute("type", propertyNode->dataType()); + writer.writeTextElement("name", objName); + QStringList pieces = fullDocumentLocation(node).split("#"); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + } + writer.writeEndElement(); // member + break; + + case Node::Enum: + { + const EnumNode *enumNode = static_cast<const EnumNode*>(node); + writer.writeTextElement("name", objName); + QStringList pieces = fullDocumentLocation(node).split("#"); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + writer.writeEndElement(); // member + + for (int i = 0; i < enumNode->items().size(); ++i) { + EnumItem item = enumNode->items().value(i); + writer.writeStartElement("member"); + writer.writeAttribute("name", item.name()); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + writer.writeEndElement(); // member + } + } + break; + + case Node::Typedef: + { + const TypedefNode *typedefNode = static_cast<const TypedefNode*>(node); + if (typedefNode->associatedEnum()) + writer.writeAttribute("type", fullDocumentName(typedefNode->associatedEnum())); + else + writer.writeAttribute("type", ""); + writer.writeTextElement("name", objName); + QStringList pieces = fullDocumentLocation(node).split("#"); + writer.writeTextElement("anchorfile", pieces[0]); + writer.writeTextElement("anchor", pieces[1]); + writer.writeTextElement("arglist", ""); + } + writer.writeEndElement(); // member + break; + + case Node::Variable: + case Node::Target: + default: + break; + } + } +} + +/*! + */ +void Tree::generateTagFile(const QString &fileName) const +{ + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) + return ; + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + + writer.writeStartElement("tagfile"); + + generateTagFileCompounds(writer, root()); + + writer.writeEndElement(); // tagfile + writer.writeEndDocument(); + file.close(); +} + +/*! + */ +void Tree::addExternalLink(const QString &url, const Node *relative) +{ + FakeNode *fakeNode = new FakeNode(root(), url, FakeNode::ExternalPage); + fakeNode->setAccess(Node::Public); + + // Create some content for the node. + QSet<QString> emptySet; + Location location(relative->doc().location()); + Doc doc(location, location, " ", emptySet); // placeholder + fakeNode->setDoc(doc); +} + +/*! + Returns the full document location for HTML-based documentation. + This should be moved into the HTML generator. + */ +QString Tree::fullDocumentLocation(const Node *node) const +{ + if (!node) + return ""; + if (!node->url().isEmpty()) + return node->url(); + + 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()) + return node->fileBase() + ".html"; + else + return ""; + } + else if (node->type() == Node::Fake) { + return node->fileBase() + ".html"; + } + else if (node->fileBase().isEmpty()) + return ""; + + QString parentName; + Node *parentNode = 0; + + if ((parentNode = node->relates())) + parentName = fullDocumentLocation(node->relates()); + else if ((parentNode = node->parent())) + parentName = fullDocumentLocation(node->parent()); + + switch (node->type()) { + case Node::Class: + case Node::Namespace: + if (parentNode && !parentNode->name().isEmpty()) + return parentName.replace(".html", "") + "-" + + node->fileBase().toLower() + ".html"; + else + return node->fileBase() + ".html"; + case Node::Function: + { + /* + Functions can be destructors, overloaded, or + have associated properties. + */ + const FunctionNode *functionNode = + static_cast<const FunctionNode *>(node); + + // Functions can be compatibility functions or be obsolete. + switch (node->status()) { + case Node::Compat: + parentName.replace(".html", "-qt3.html"); + break; + case Node::Obsolete: + parentName.replace(".html", "-obsolete.html"); + break; + default: + ; + } + + if (functionNode->metaness() == FunctionNode::Dtor) + return parentName + "#dtor." + functionNode->name().mid(1); + + if (functionNode->associatedProperty()) + return fullDocumentLocation(functionNode->associatedProperty()); + + if (functionNode->overloadNumber() > 1) + return parentName + "#" + functionNode->name() + + "-" + QString::number(functionNode->overloadNumber()); + else + return parentName + "#" + 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. + */ + case Node::Enum: + return parentName + "#" + node->name() + "-enum"; + case Node::Typedef: + return parentName + "#" + node->name() + "-typedef"; + case Node::Property: + return parentName + "#" + node->name() + "-prop"; + case Node::Variable: + return parentName + "#" + node->name() + "-var"; + case Node::Target: + return parentName + "#" + Doc::canonicalTitle(node->name()); + case Node::Fake: + { + QString pageName = node->name(); + return pageName.replace("/", "-").replace(".", "-") + ".html"; + } + break; + default: + break; + } + + return ""; +} + +/*! + */ +QString Tree::fullDocumentName(const Node *node) const +{ + if (!node) + return ""; + + QStringList pieces; + const Node *n = node; + + do { + if (!n->name().isEmpty()) + pieces.insert(0, n->name()); + + if (n->type() == Node::Fake) + break; + + // Examine the parent node if one exists. + if (n->parent()) + n = n->parent(); + else + break; + } while (true); + + // Create a name based on the type of the ancestor node. + if (n->type() == Node::Fake) + return pieces.join("#"); + else + return pieces.join("::"); +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/tree.h b/tools/qdoc3/tree.h new file mode 100644 index 0000000..8aebcaa --- /dev/null +++ b/tools/qdoc3/tree.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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); + Node *findNode(const QStringList &path, + Node::Type type, + Node *relative = 0, + int findFlags = 0); + 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); + QMultiMap<QString, Node *> groups() const; + QMultiMap<QString, QString> publicGroups() const; + void resolveInheritance(NamespaceNode *rootNode = 0); + void resolveProperties(); + void resolveGroups(); + void resolveTargets(); + 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; + const Node *findNode(const QStringList &path, + Node::Type type, const + Node *relative = 0, + int findFlags = 0) 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; + const Node *findUnambiguousTarget(const QString &target, Atom *&atom) 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 fullDocumentName(const Node *node) const; + QString fullDocumentLocation(const Node *node) const; + + 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/tools/qdoc3/uncompressor.cpp b/tools/qdoc3/uncompressor.cpp new file mode 100644 index 0000000..8f3897d --- /dev/null +++ b/tools/qdoc3/uncompressor.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + uncompressor.cpp +*/ + +#include "uncompressor.h" + +QT_BEGIN_NAMESPACE + +QList<Uncompressor *> Uncompressor::uncompressors; + + +/*! + \class Uncompressor + + \brief The Uncompressor class is a base class for classes that + know how to uncompress a certain kind of compressed file. + + The uncompressor contains a list of the filename extensions + of the file types that the uncompressor knows how to uncompress. + + It maintains a static list of all the instances of Uncompressor + that have been created. It also has a static function for searching + that list to find the uncompressor to use for uncompressing a file + with a certain extension. + */ + +/*! + The constructor takes a list of filename extensions, which it + copies and saves internally. This uncompressor is prepended + to the stack list. + */ +Uncompressor::Uncompressor( const QStringList& extensions ) + : fileExts( extensions ) +{ + uncompressors.prepend( this ); +} + +/*! + The destructor deletes all the filename extensions. + */ +Uncompressor::~Uncompressor() +{ + uncompressors.removeAll( this ); +} + +/*! + This function searches the static list of uncompressors to find the + first one that can handle \a fileName. If it finds an acceptable + uncompressor, it returns a pointer to it. Otherwise it returns null. +*/ +Uncompressor* +Uncompressor::uncompressorForFileName( const QString& fileName ) +{ + int dot = -1; + while ( (dot = fileName.indexOf(".", dot + 1)) != -1 ) { + QString ext = fileName.mid( dot + 1 ); + QList<Uncompressor *>::ConstIterator u = uncompressors.begin(); + while ( u != uncompressors.end() ) { + if ( (*u)->fileExtensions().contains(ext) ) + return *u; + ++u; + } + } + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/uncompressor.h b/tools/qdoc3/uncompressor.h new file mode 100644 index 0000000..4d5c9af --- /dev/null +++ b/tools/qdoc3/uncompressor.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + uncompressor.h +*/ + +#ifndef UNCOMPRESSOR_H +#define UNCOMPRESSOR_H + +#include <qstringlist.h> + +#include "location.h" + +QT_BEGIN_NAMESPACE + +class Uncompressor +{ +public: + Uncompressor( const QStringList& extensions ); + virtual ~Uncompressor(); + + virtual QString uncompressedFilePath( const QString& filePath ) = 0; + virtual void uncompressFile( const Location& location, + const QString& filePath, + const QString& outputFilePath ) = 0; + + static Uncompressor *uncompressorForFileName( const QString& fileName ); + +protected: + const QStringList& fileExtensions() const { return fileExts; } + +private: + QStringList fileExts; + + static QList<Uncompressor *> uncompressors; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/webxmlgenerator.cpp b/tools/qdoc3/webxmlgenerator.cpp new file mode 100644 index 0000000..0438adf --- /dev/null +++ b/tools/qdoc3/webxmlgenerator.cpp @@ -0,0 +1,1195 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + webxmlgenerator.cpp +*/ + +#include <QtXml> + +#include "codemarker.h" +#include "pagegenerator.h" +#include "webxmlgenerator.h" +#include "node.h" +#include "separator.h" +#include "tree.h" + +QT_BEGIN_NAMESPACE + +#define COMMAND_VERSION Doc::alias("version") + +WebXMLGenerator::WebXMLGenerator() + : PageGenerator() +{ +} + +WebXMLGenerator::~WebXMLGenerator() +{ +} + +void WebXMLGenerator::initializeGenerator(const Config &config) +{ + Generator::initializeGenerator(config); + + project = config.getString(CONFIG_PROJECT); + + projectDescription = config.getString(CONFIG_DESCRIPTION); + if (projectDescription.isEmpty() && !project.isEmpty()) + projectDescription = project + " Reference Documentation"; + + projectUrl = config.getString(CONFIG_URL); + + generateIndex = config.getBool(CONFIG_GENERATEINDEX); +} + +void WebXMLGenerator::terminateGenerator() +{ + PageGenerator::terminateGenerator(); +} + +QString WebXMLGenerator::format() +{ + return "WebXML"; +} + +QString WebXMLGenerator::fileExtension(const Node * /* node */) +{ + return "xml"; +} + +void WebXMLGenerator::generateTree(const Tree *tree, CodeMarker *marker) +{ + tre = tree; + moduleClassMap.clear(); + moduleNamespaceMap.clear(); + serviceClasses.clear(); + findAllClasses(tree->root()); + findAllNamespaces(tree->root()); + + PageGenerator::generateTree(tree, marker); + + if (generateIndex) + tre->generateIndex(outputDir() + "/" + project.toLower() + ".index", + projectUrl, projectDescription, false); +} + +void WebXMLGenerator::startText(const Node *relative, CodeMarker *marker) +{ + inLink = false; + inContents = false; + inSectionHeading = false; + numTableRows = 0; + sectionNumber.clear(); + PageGenerator::startText(relative, marker); +} + +int WebXMLGenerator::generateAtom(QXmlStreamWriter &writer, const Atom *atom, + const Node *relative, CodeMarker *marker) +{ + Q_UNUSED(writer); + + int skipAhead = 0; + + switch (atom->type()) { + default: + PageGenerator::generateAtom(atom, relative, marker); + } + return skipAhead; +} + +void WebXMLGenerator::generateClassLikeNode(const InnerNode *inner, + CodeMarker *marker) +{ + QByteArray data; + QXmlStreamWriter writer(&data); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("WebXML"); + writer.writeStartElement("document"); + + generateIndexSections(writer, inner, marker); + + writer.writeEndElement(); // document + writer.writeEndElement(); // WebXML + writer.writeEndDocument(); + + out() << data; + out().flush(); +} + +void WebXMLGenerator::generateFakeNode(const FakeNode *fake, CodeMarker *marker) +{ + QByteArray data; + QXmlStreamWriter writer(&data); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeStartElement("WebXML"); + writer.writeStartElement("document"); + + generateIndexSections(writer, fake, marker); + + writer.writeEndElement(); // document + writer.writeEndElement(); // WebXML + writer.writeEndDocument(); + + out() << data; + out().flush(); +} + +void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, + const Node *node, CodeMarker *marker) +{ + if (tre->generateIndexSection(writer, node, true)) { + + // Add documentation to this node if it exists. + writer.writeStartElement("description"); + writer.writeAttribute("path", node->doc().location().filePath()); + writer.writeAttribute("line", QString::number(node->doc().location().lineNo())); + writer.writeAttribute("column", QString::number(node->doc().location().columnNo())); + + if (node->type() == Node::Fake) { + + const FakeNode *fake = static_cast<const FakeNode *>(node); + + generateRelations(writer, node, marker); + + if (fake->subType() == FakeNode::Module) { + writer.writeStartElement("generatedlist"); + writer.writeAttribute("contents", "classesbymodule"); + + if (moduleNamespaceMap.contains(fake->name())) { + writer.writeStartElement("section"); + writer.writeStartElement("heading"); + writer.writeAttribute("level", "1"); + writer.writeCharacters("Namespaces"); + writer.writeEndElement(); // heading + generateAnnotatedList(writer, fake, marker, moduleNamespaceMap[fake->name()]); + writer.writeEndElement(); // section + } + if (moduleClassMap.contains(fake->name())) { + writer.writeStartElement("section"); + writer.writeStartElement("heading"); + writer.writeAttribute("level", "1"); + writer.writeCharacters("Classes"); + writer.writeEndElement(); // heading + generateAnnotatedList(writer, fake, marker, moduleClassMap[fake->name()]); + writer.writeEndElement(); // section + } + + writer.writeEndElement(); // generatedlist + } + } + + startText(node, marker); + + const Atom *atom = node->doc().body().firstAtom(); + while (atom) + atom = addAtomElements(writer, atom, node, marker); + + QList<Text> alsoList = node->doc().alsoList(); + supplementAlsoList(node, alsoList); + + if (!alsoList.isEmpty()) { + writer.writeStartElement("see-also"); + for (int i = 0; i < alsoList.size(); ++i) { + const Atom *atom = alsoList.at(i).firstAtom(); + while (atom) + atom = addAtomElements(writer, atom, node, marker); + } + writer.writeEndElement(); // see-also + } + + writer.writeEndElement(); // description + + if (node->isInnerNode()) { + const InnerNode *inner = static_cast<const InnerNode *>(node); + + // Recurse to generate an element for this child node and all its children. + foreach (const Node *child, inner->childNodes()) + generateIndexSections(writer, child, marker); + + writer.writeStartElement("related"); + if (inner->relatedNodes().size() > 0) { + foreach (const Node *child, inner->relatedNodes()) + generateIndexSections(writer, child, marker); + } + writer.writeEndElement(); // related + } + writer.writeEndElement(); + } +} + +void WebXMLGenerator::generateInnerNode(const InnerNode *node, CodeMarker *marker) +{ + if (!node->url().isNull()) + return; + + if (node->type() == Node::Fake) { + const FakeNode *fakeNode = static_cast<const FakeNode *>(node); + if (fakeNode->subType() == FakeNode::ExternalPage) + return; + } + + if ( node->parent() != 0 ) { + beginSubPage( node->location(), 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 || (*c)->status() == Node::Internal)) + generateInnerNode( (const InnerNode *) *c, marker ); + ++c; + } +} + +const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, + const Atom *atom, const Node *relative, CodeMarker *marker) +{ + switch (atom->type()) { + case Atom::AbstractLeft: + case Atom::AbstractRight: + break; + case Atom::AutoLink: + if (!inLink && !inSectionHeading) { + const Node *node = findNode(atom, relative, marker); + if (node) { + startLink(writer, atom, node, relative); + if (inLink) { + writer.writeCharacters(atom->string()); + writer.writeEndElement(); // link + inLink = false; + } + } else + writer.writeCharacters(atom->string()); + } else + writer.writeCharacters(atom->string()); + break; + case Atom::BaseName: + break; + case Atom::BriefLeft: + + writer.writeStartElement("brief"); + switch (relative->type()) { + case Node::Property: + writer.writeCharacters("This property"); + break; + case Node::Variable: + writer.writeCharacters("This variable"); + break; + default: + break; + } + if (relative->type() == Node::Property || relative->type() == Node::Variable) { + QString str; + const Atom *a = atom->next(); + while (a != 0 && a->type() != Atom::BriefRight) { + if (a->type() == Atom::String || a->type() == Atom::AutoLink) + str += a->string(); + a = a->next(); + } + str[0] = str[0].toLower(); + if (str.right(1) == ".") + str.chop(1); + + QStringList words = str.split(" "); + if (!(words.first() == "contains" || words.first() == "specifies" + || words.first() == "describes" || words.first() == "defines" + || words.first() == "holds" || words.first() == "determines")) + writer.writeCharacters(" holds "); + else + writer.writeCharacters(" "); + } + break; + + case Atom::BriefRight: + if (relative->type() == Node::Property || relative->type() == Node::Variable) + writer.writeCharacters("."); + + writer.writeEndElement(); // brief + break; + + case Atom::C: + writer.writeStartElement("teletype"); + if (inLink) + writer.writeAttribute("type", "normal"); + else + writer.writeAttribute("type", "highlighted"); + + writer.writeCharacters(plainCode(atom->string())); + writer.writeEndElement(); // teletype + break; + + case Atom::Code: + writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string()))); + break; + +#ifdef QDOC_QML + case Atom::Qml: + writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string()))); +#endif + + case Atom::CodeBad: + writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string()))); + break; + + case Atom::CodeNew: + writer.writeTextElement("para", "you can rewrite it as"); + writer.writeTextElement("newcode", trimmedTrailing(plainCode(atom->string()))); + break; + + case Atom::CodeOld: + writer.writeTextElement("para", "For example, if you have code like"); + writer.writeTextElement("oldcode", trimmedTrailing(plainCode(atom->string()))); + break; + + case Atom::CodeQuoteArgument: + if (quoteCommand == "dots") { + writer.writeAttribute("indent", atom->string()); + writer.writeCharacters("..."); + } else + writer.writeCharacters(atom->string()); + writer.writeEndElement(); // code + break; + + case Atom::CodeQuoteCommand: + quoteCommand = atom->string(); + writer.writeStartElement(quoteCommand); + break; + + case Atom::FootnoteLeft: + writer.writeStartElement("footnote"); + break; + + case Atom::FootnoteRight: + writer.writeEndElement(); // footnote + break; +/* + case Atom::FormatElse: + writer.writeStartElement("else"); + writer.writeEndElement(); // else + break; +*/ + case Atom::FormatEndif: + writer.writeEndElement(); // raw + break; + case Atom::FormatIf: + writer.writeStartElement("raw"); + writer.writeAttribute("format", atom->string()); + break; + case Atom::FormattingLeft: + { + if (atom->string() == ATOM_FORMATTING_BOLD) + writer.writeStartElement("bold"); + else if (atom->string() == ATOM_FORMATTING_ITALIC) + writer.writeStartElement("italic"); + else if (atom->string() == ATOM_FORMATTING_UNDERLINE) + writer.writeStartElement("underline"); + else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT) + writer.writeStartElement("subscript"); + else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT) + writer.writeStartElement("superscript"); + else if (atom->string() == ATOM_FORMATTING_TELETYPE) + writer.writeStartElement("teletype"); + else if (atom->string() == ATOM_FORMATTING_PARAMETER) + writer.writeStartElement("argument"); + else if (atom->string() == ATOM_FORMATTING_INDEX) + writer.writeStartElement("index"); + } + break; +/* 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; + } + } + }*/ + case Atom::FormattingRight: + { + if (atom->string() == ATOM_FORMATTING_BOLD) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_ITALIC) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_UNDERLINE) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_SUBSCRIPT) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_SUPERSCRIPT) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_TELETYPE) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_PARAMETER) + writer.writeEndElement(); + else if (atom->string() == ATOM_FORMATTING_INDEX) + writer.writeEndElement(); + } + if (inLink) { + writer.writeEndElement(); // link + inLink = false; + } + break; +/* if ( atom->string() == ATOM_FORMATTING_LINK ) { + if (inLink) { + if ( link.isEmpty() ) { + if (showBrokenLinks) + out() << "</i>"; + } else { + out() << "</a>"; + } + } + inLink = false; + } else { + out() << formattingRightMap()[atom->string()]; + }*/ + case Atom::GeneratedList: + writer.writeStartElement("generatedlist"); + writer.writeAttribute("contents", atom->string()); + writer.writeEndElement(); // generatedlist +/* + if (atom->string() == "annotatedclasses") { + generateAnnotatedList(relative, marker, nonCompatClasses); + } else if (atom->string() == "classes") { + generateCompactList(relative, marker, nonCompatClasses); + } 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)) { + QMap<QString, const Node *> editionClasses; + foreach (const QString &moduleName, editionModuleMap[editionName]) { + if (moduleClassMap.contains(moduleName)) + editionClasses.unite(moduleClassMap[moduleName]); + } + generateAnnotatedList(relative, marker, editionClasses); + } + } else if (atom->string() == "classhierarchy") { + generateClassHierarchy(relative, marker, nonCompatClasses); + } else if (atom->string() == "compatclasses") { + generateCompactList(relative, marker, compatClasses); + } 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); + } else if (atom->string() == "services") { + generateCompactList(relative, marker, serviceClasses); + } 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()) { + QMap<QString, const Node *> groupMembersMap; + foreach (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; +*/ + break; + case Atom::Image: + writer.writeStartElement("image"); + writer.writeAttribute("href", imageFileName(relative, atom->string())); + writer.writeEndElement(); // image + break; + + case Atom::InlineImage: + writer.writeStartElement("inlineimage"); + writer.writeAttribute("href", imageFileName(relative, atom->string())); + writer.writeEndElement(); // inlineimage + break; + + case Atom::ImageText: + break; + + case Atom::LegaleseLeft: + writer.writeStartElement("legalese"); + break; + + case Atom::LegaleseRight: + writer.writeEndElement(); // legalese + break; + + case Atom::Link: + case Atom::LinkNode: + if (!inLink) { + const Node *node = findNode(atom, relative, marker); + if (node) + startLink(writer, atom, node, relative); + } + break; + + case Atom::ListLeft: + writer.writeStartElement("list"); + + if (atom->string() == ATOM_LIST_BULLET) + writer.writeAttribute("type", "bullet"); + else if (atom->string() == ATOM_LIST_TAG) + writer.writeAttribute("type", "definition"); + else if (atom->string() == ATOM_LIST_VALUE) + writer.writeAttribute("type", "enum"); + else { + writer.writeAttribute("type", "ordered"); + if (atom->string() == ATOM_LIST_UPPERALPHA) + writer.writeAttribute("start", "A"); + else if (atom->string() == ATOM_LIST_LOWERALPHA) + writer.writeAttribute("start", "a"); + else if (atom->string() == ATOM_LIST_UPPERROMAN) + writer.writeAttribute("start", "I"); + else if (atom->string() == ATOM_LIST_LOWERROMAN) + writer.writeAttribute("start", "i"); + else // (atom->string() == ATOM_LIST_NUMERIC) + writer.writeAttribute("start", "1"); + } + break; + + case Atom::ListItemNumber: + break; + + case Atom::ListTagLeft: + { + writer.writeStartElement("definition"); + + writer.writeTextElement("term", plainCode( + marker->markedUpEnumValue(atom->next()->string(), relative))); + } + break; + + case Atom::ListTagRight: + writer.writeEndElement(); // definition + break; + + case Atom::ListItemLeft: + writer.writeStartElement("item"); + break; + + case Atom::ListItemRight: + writer.writeEndElement(); // item + break; + + case Atom::ListRight: + writer.writeEndElement(); // list + break; + + case Atom::Nop: + break; + + case Atom::ParaLeft: + writer.writeStartElement("para"); + break; + + case Atom::ParaRight: + writer.writeEndElement(); // para + break; + + case Atom::QuotationLeft: + writer.writeStartElement("quote"); + break; + + case Atom::QuotationRight: + writer.writeEndElement(); // quote + break; + + case Atom::RawString: + writer.writeCharacters(atom->string()); + break; + + case Atom::SectionLeft: + writer.writeStartElement("section"); + writer.writeAttribute("id", Doc::canonicalTitle(Text::sectionHeading(atom).toString())); + break; + + case Atom::SectionRight: + writer.writeEndElement(); // section + break; + + case Atom::SectionHeadingLeft: + writer.writeStartElement("heading"); + writer.writeAttribute("level", atom->string()); // + hOffset(relative) + inSectionHeading = true; + break; + + case Atom::SectionHeadingRight: + writer.writeEndElement(); // heading + inSectionHeading = false; + break; + + case Atom::SidebarLeft: + case Atom::SidebarRight: + break; + + case Atom::SnippetCommand: + writer.writeStartElement(atom->string()); + break; + + case Atom::SnippetIdentifier: + writer.writeAttribute("identifier", atom->string()); + writer.writeEndElement(); // snippet + break; + + case Atom::SnippetLocation: + writer.writeAttribute("location", atom->string()); + break; + + case Atom::String: + writer.writeCharacters(atom->string()); + break; + + case Atom::TableLeft: + writer.writeStartElement("table"); + if (atom->string().contains("%")) + writer.writeAttribute("width", atom->string()); + break; + + case Atom::TableRight: + writer.writeEndElement(); // table + break; + + case Atom::TableHeaderLeft: + writer.writeStartElement("header"); + break; + + case Atom::TableHeaderRight: + writer.writeEndElement(); // header + break; + + case Atom::TableRowLeft: + writer.writeStartElement("row"); + break; + + case Atom::TableRowRight: + writer.writeEndElement(); // row + break; + + case Atom::TableItemLeft: + { + writer.writeStartElement("item"); + QStringList spans = atom->string().split(","); + if (spans.size() == 2) { + if (spans.at(0) != "1") + writer.writeAttribute("colspan", spans.at(0).trimmed()); + if (spans.at(1) != "1") + writer.writeAttribute("rowspan", spans.at(1).trimmed()); + } + } + break; + + case Atom::TableItemRight: + writer.writeEndElement(); // item + break; + + case Atom::TableOfContents: + writer.writeStartElement("tableofcontents"); + writer.writeAttribute("details", atom->string()); + { + int numColumns = 1; + const Node *node = relative; + + Doc::SectioningUnit sectioningUnit = Doc::Section4; + QStringList params = atom->string().split(","); + QString columnText = params.at(0); + QStringList pieces = columnText.split(" ", QString::SkipEmptyParts); + if (pieces.size() >= 2) { + columnText = pieces.at(0); + pieces.pop_front(); + QString path = pieces.join(" ").trimmed(); + node = findNode(path, relative, marker); + if (node) + writer.writeAttribute("href", fileName(node)); + } + + if (params.size() == 2) { + numColumns = qMax(columnText.toInt(), numColumns); + sectioningUnit = (Doc::SectioningUnit)params.at(1).toInt(); + writer.writeAttribute("columns", QString::number(numColumns)); + writer.writeAttribute("unit", QString::number(sectioningUnit)); + } + + if (node) + generateTableOfContents(writer, node, sectioningUnit, numColumns, + relative); + } + writer.writeEndElement(); // tableofcontents + break; + + case Atom::Target: + writer.writeStartElement("target"); + writer.writeAttribute("name", Doc::canonicalTitle(atom->string())); + writer.writeEndElement(); // target + break; + + case Atom::UnhandledFormat: + case Atom::UnknownCommand: + writer.writeCharacters(atom->typeString()); + break; + default: + break; + } + + if (atom) + return atom->next(); + + return 0; +} +/* + QDomElement atomElement = document.createElement(atom->typeString().toLower()); + QDomText atomValue = document.createTextNode(atom->string()); + atomElement.appendChild(atomValue); + descriptionElement.appendChild(atomElement); +*/ + +/* + ### Warning: findNode() is a modified version of HtmlGenerator::getLink(). +*/ +const Node *WebXMLGenerator::findNode(const Atom *atom, const Node *relative, CodeMarker *marker) +{ + return findNode(atom->string(), relative, marker); +} + +const Node *WebXMLGenerator::findNode(const QString &title, const Node *relative, CodeMarker *marker) +{ + QString link; + if (title.contains(":") && + (title.startsWith("file:") + || title.startsWith("http:") + || title.startsWith("https:") + || title.startsWith("ftp:") + || title.startsWith("mailto:"))) { + + return 0; + } else if (title.count('@') == 1) { + return 0; + } else { + QStringList path; + if (title.contains('#')) { + path = title.split('#'); + } else { + path.append(title); + } + + const Node *node = 0; + Atom *targetAtom = 0; + + QString first = path.first().trimmed(); + if (first.isEmpty()) { + node = relative; + } else if (first.endsWith(".html")) { + node = tre->root()->findNode(first, Node::Fake); + } else { + node = marker->resolveTarget(first, tre, relative); + if (!node) + node = tre->findFakeNodeByTitle(first); + if (!node) + node = tre->findUnambiguousTarget(first, targetAtom); + } + + if (node) { + if (!node->url().isEmpty()) + return node; + else + path.removeFirst(); + } else { + return 0; + } + + while (!path.isEmpty()) { + targetAtom = tre->findTarget(path.first(), node); + if (targetAtom == 0) + break; + path.removeFirst(); + } +/* We would ideally treat targets as nodes to be consistent. + if (targetAtom && node && node->isInnerNode()) { + Node *parentNode = const_cast<Node *>(node); + node = new TargetNode(static_cast<InnerNode*>(parentNode), first); + } +*/ + return node; + } + return 0; +} + +void WebXMLGenerator::startLink(QXmlStreamWriter &writer, const Atom *atom, + const Node *node, const Node *relative) +{ + QString location = tre->fullDocumentLocation(node); + if (!location.isEmpty()) { + writer.writeStartElement("link"); + writer.writeAttribute("raw", atom->string()); + if (atom->string().contains("#") || node == relative) { + QString target = atom->string().split("#").last(); + Atom *targetAtom = tre->findTarget(target, node); + if (targetAtom) + location += "#" + Doc::canonicalTitle(target); + } + writer.writeAttribute("href", location); + QString type = targetType(node); + writer.writeAttribute("type", type); + switch (node->type()) { + case Node::Enum: + writer.writeAttribute("enum", tre->fullDocumentName(node)); + break; + case Node::Fake: + writer.writeAttribute("page", tre->fullDocumentName(node)); + break; + case Node::Property: + { + const PropertyNode *propertyNode = static_cast<const PropertyNode *>(node); + if (propertyNode->getters().size() > 0) + writer.writeAttribute("getter", tre->fullDocumentName(propertyNode->getters()[0])); + } + default: + ; + } + inLink = true; + } +} + +QString WebXMLGenerator::targetType(const Node *node) +{ + switch (node->type()) { + case Node::Namespace: + return "namespace"; + break; + case Node::Class: + return "class"; + break; + case Node::Fake: + return "page"; + break; + case Node::Enum: + return "enum"; + break; + case Node::Typedef: + return "typedef"; + break; + case Node::Property: + return "property"; + break; + case Node::Function: + return "function"; + break; + case Node::Variable: + return "variable"; + break; + case Node::Target: + return "target"; + break; + default: + return ""; + } + return ""; +} + +void WebXMLGenerator::generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker) +{ + if (node && !node->links().empty()) { + QPair<QString,QString> linkPair; + QPair<QString,QString> anchorPair; + const Node *linkNode; + + foreach (Node::LinkType relation, node->links().keys()) { + + linkPair = node->links()[relation]; + linkNode = findNode(linkPair.first, node, marker); + + if (!linkNode) + linkNode = node; + + if (linkNode == node) + anchorPair = linkPair; + else + anchorPair = anchorForNode(linkNode); + + writer.writeStartElement("relation"); + writer.writeAttribute("href", anchorPair.first); + writer.writeAttribute("type", targetType(linkNode)); + + switch (relation) { + case Node::StartLink: + writer.writeAttribute("meta", "start"); + break; + case Node::NextLink: + writer.writeAttribute("meta", "next"); + break; + case Node::PreviousLink: + writer.writeAttribute("meta", "previous"); + break; + case Node::ContentsLink: + writer.writeAttribute("meta", "contents"); + break; + case Node::IndexLink: + writer.writeAttribute("meta", "index"); + break; + default: + writer.writeAttribute("meta", ""); + } + writer.writeAttribute("description", anchorPair.second); + writer.writeEndElement(); // link + } + } +} + +// Classes adapted from HtmlGenerator. + +void WebXMLGenerator::generateTableOfContents(QXmlStreamWriter &writer, const Node *node, + Doc::SectioningUnit sectioningUnit, + int numColumns, const Node *relative) + +{ + 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; + + if (numColumns > 1) { + writer.writeStartElement("table"); + writer.writeAttribute("width", "100%"); + writer.writeStartElement("row"); + writer.writeStartElement("item"); + writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%"); + } + + // 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)sectioningUnit) + continue; + + if (sectionNumber.size() < nextLevel) { + do { + writer.writeStartElement("list"); + sectionNumber.append("1"); + } while (sectionNumber.size() < nextLevel); + } else { + while (sectionNumber.size() > nextLevel) { + writer.writeEndElement(); + sectionNumber.removeLast(); + } + sectionNumber.last() = QString::number(sectionNumber.last().toInt() + 1); + } + Text headingText = Text::sectionHeading(atom); + + if (sectionNumber.size() == 1 && columnSize > toc.size() / numColumns) { + writer.writeEndElement(); // list + writer.writeEndElement(); // item + writer.writeStartElement("item"); + writer.writeAttribute("width", QString::number((100 + numColumns - 1) / numColumns) + "%"); + writer.writeStartElement("list"); + columnSize = 0; + } + + writer.writeStartElement("item"); + writer.writeStartElement("para"); + writer.writeStartElement("link"); + writer.writeAttribute("href", nodeName + "#" + Doc::canonicalTitle(headingText.toString())); + writer.writeAttribute("type", "page"); + writer.writeCharacters(headingText.toString()); + writer.writeEndElement(); // link + writer.writeEndElement(); // para + writer.writeEndElement(); // item + + ++columnSize; + } + while (!sectionNumber.isEmpty()) { + writer.writeEndElement(); // list + sectionNumber.removeLast(); + } + + if (numColumns > 1) { + writer.writeEndElement(); // item + writer.writeEndElement(); // row + writer.writeEndElement(); // table + } + + inContents = false; + inLink = false; +} + +void WebXMLGenerator::generateAnnotatedList(QXmlStreamWriter &writer, + const Node *relative, CodeMarker *marker, const QMap<QString, const Node *> &nodeMap) +{ + writer.writeStartElement("table"); + writer.writeAttribute("width", "100%"); + + foreach (QString name, nodeMap.keys()) { + const Node *node = nodeMap[name]; + + writer.writeStartElement("row"); + writer.writeStartElement("heading"); + generateFullName(writer, node, relative, marker); + writer.writeEndElement(); // heading + + writer.writeStartElement("item"); + writer.writeCharacters(node->doc().briefText().toString()); + writer.writeEndElement(); // item + writer.writeEndElement(); // row + } + writer.writeEndElement(); // table +} + +void WebXMLGenerator::generateFullName(QXmlStreamWriter &writer, + const Node *apparentNode, const Node *relative, CodeMarker *marker, + const Node *actualNode) +{ + if ( actualNode == 0 ) + actualNode = apparentNode; + writer.writeStartElement("link"); + writer.writeAttribute("href", tre->fullDocumentLocation(actualNode)); + writer.writeAttribute("type", targetType(actualNode)); + writer.writeCharacters(fullName(apparentNode, relative, marker)); + writer.writeEndElement(); // link +} + +// Classes copied (and slightly adapted) from the HtmlGenerator. These need +// refactoring into a common ancestor class. + +void WebXMLGenerator::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; + + QString moduleName = (*c)->moduleName(); + 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)->isInnerNode()) { + findAllClasses(static_cast<InnerNode *>(*c)); + } + } + ++c; + } +} + +void WebXMLGenerator::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.isEmpty()) + moduleNamespaceMap[moduleName].insert((*c)->name(), *c); + } + } + } + } + ++c; + } +} + +const QPair<QString,QString> WebXMLGenerator::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; +} + +QT_END_NAMESPACE diff --git a/tools/qdoc3/webxmlgenerator.h b/tools/qdoc3/webxmlgenerator.h new file mode 100644 index 0000000..f7b4445 --- /dev/null +++ b/tools/qdoc3/webxmlgenerator.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + WebXMLGenerator.h +*/ + +#ifndef WEBXMLGENERATOR_H +#define WEBXMLGENERATOR_H + +#include "codemarker.h" +#include "config.h" +#include "pagegenerator.h" + +QT_BEGIN_NAMESPACE + +class WebXMLGenerator : public PageGenerator +{ +public: + WebXMLGenerator(); + ~WebXMLGenerator(); + + virtual void initializeGenerator(const Config &config); + virtual void terminateGenerator(); + virtual QString format(); + virtual void generateTree(const Tree *tree, CodeMarker *marker); + +protected: + virtual void startText( const Node *relative, CodeMarker *marker ); + virtual int generateAtom(QXmlStreamWriter &writer, 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); + + virtual const Atom *addAtomElements(QXmlStreamWriter &writer, const Atom *atom, + const Node *relative, CodeMarker *marker); + virtual void generateIndexSections(QXmlStreamWriter &writer, const Node *node, + CodeMarker *marker); + virtual void generateInnerNode( const InnerNode *node, CodeMarker *marker ); + +private: + const QPair<QString,QString> anchorForNode(const Node *node); + void findAllClasses(const InnerNode *node); + void findAllNamespaces(const InnerNode *node); + const Node *findNode(const Atom *atom, const Node *relative, CodeMarker *marker); + const Node *findNode(const QString &title, const Node *relative, CodeMarker *marker); + void generateAnnotatedList(QXmlStreamWriter &writer, const Node *relative, + CodeMarker *marker, const QMap<QString, + const Node *> &nodeMap); + void generateFullName(QXmlStreamWriter &writer, const Node *apparentNode, + const Node *relative, CodeMarker *marker, + const Node *actualNode = 0); + void generateRelations(QXmlStreamWriter &writer, const Node *node, CodeMarker *marker); + void generateTableOfContents(QXmlStreamWriter &writer, const Node *node, + Doc::SectioningUnit sectioningUnit, + int numColumns, const Node *relative); + void startLink(QXmlStreamWriter &writer, const Atom *atom, const Node *node, + const Node *relative); + QString targetType(const Node *node); + + const Tree *tre; + bool generateIndex; + bool inLink; + bool inContents; + bool inSectionHeading; + bool inTableHeader; + int numTableRows; + bool threeColumnEnumValueTable; + QMap<QString, QMap<QString, const Node *> > moduleClassMap; + QMap<QString, QMap<QString, const Node *> > moduleNamespaceMap; + QMap<QString, const Node *> namespaceIndex; + QMap<QString, const Node *> serviceClasses; + QString link; + QString project; + QString projectDescription; + QString projectUrl; + QString quoteCommand; + QStringList sectionNumber; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/qdoc3/yyindent.cpp b/tools/qdoc3/yyindent.cpp new file mode 100644 index 0000000..bbca39b --- /dev/null +++ b/tools/qdoc3/yyindent.cpp @@ -0,0 +1,1190 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $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(")") ) + 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(";") && + 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 Trolltech'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(",") && + (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(",") ) { + /* + 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(";") || 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(":") ) + typedIn = ':'; + + int indent = indentForBottomLine( p, typedIn ); + + if ( !(*line).trimmed().isEmpty() ) { + for ( int j = 0; j < indent; j++ ) + out += " "; + out += (*line).trimmed(); + } + out += "\n"; + ++line; + } + + while ( out.endsWith("\n") ) + out.truncate( out.length() - 1 ); + + printf( "%s\n", out.toLatin1().data() ); + return 0; +} + +#endif // Q_TEST_YYINDENT |