diff options
author | Hugo Lima <hugo.lima@openbossa.org> | 2009-08-21 18:09:37 -0300 |
---|---|---|
committer | Hugo Lima <hugo.lima@openbossa.org> | 2009-08-25 16:23:16 -0300 |
commit | 46595f8e48b3d3e53e02b0ccb39e593b3cd3c829 (patch) | |
tree | c44dfc1343993d305b7c747d1b1db5a39c11294a /generators | |
parent | 55ec8a2a4d1c9453e9ba5b27bf8542fa4d586939 (diff) |
Moved docgenerator to generators/qtdoc and renamed it to qtdocgenerator
Diffstat (limited to 'generators')
-rw-r--r-- | generators/qtdoc/qtdocgenerator.cpp | 1370 | ||||
-rw-r--r-- | generators/qtdoc/qtdocgenerator.h | 212 |
2 files changed, 1582 insertions, 0 deletions
diff --git a/generators/qtdoc/qtdocgenerator.cpp b/generators/qtdoc/qtdocgenerator.cpp new file mode 100644 index 000000000..ac3dbf378 --- /dev/null +++ b/generators/qtdoc/qtdocgenerator.cpp @@ -0,0 +1,1370 @@ +/* + * This file is part of the Boost Python Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "docgenerator.h" +#include <reporthandler.h> +#include <qtdocparser.h> +#include <algorithm> +#include <QtCore/QStack> +#include <QtCore/QTextStream> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QFile> +#include <QtCore/QDir> + +static Indentor INDENT; + +namespace +{ + +static bool functionSort(const AbstractMetaFunction *func1, const AbstractMetaFunction *func2) +{ + return func1->name() < func2->name(); +} + +QString createRepeatedChar(int i, char c) +{ + QString out; + for (int j = 0; j < i; ++j) + out += c; + + return out; +} + +QString escape(QString& str) +{ + return str + .replace("*", "\\*") + .replace("_", "\\_"); +} + +QString escape(const QStringRef& strref) +{ + QString str = strref.toString(); + return escape(str); +} + +} + +QtXmlToSphinx::QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context) + : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) +{ + m_handlerMap.insert("heading", &QtXmlToSphinx::handleHeadingTag); + m_handlerMap.insert("brief", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("para", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("italic", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("bold", &QtXmlToSphinx::handleBoldTag); + m_handlerMap.insert("see-also", &QtXmlToSphinx::handleSeeAlsoTag); + m_handlerMap.insert("snippet", &QtXmlToSphinx::handleSnippetTag); + m_handlerMap.insert("dots", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("codeline", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("table", &QtXmlToSphinx::handleTableTag); + m_handlerMap.insert("header", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("row", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("item", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("argument", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("teletype", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("link", &QtXmlToSphinx::handleLinkTag); + m_handlerMap.insert("inlineimage", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("image", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("list", &QtXmlToSphinx::handleListTag); + m_handlerMap.insert("term", &QtXmlToSphinx::handleTermTag); + m_handlerMap.insert("raw", &QtXmlToSphinx::handleRawTag); + m_handlerMap.insert("underline", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("superscript", &QtXmlToSphinx::handleSuperScriptTag); + m_handlerMap.insert("code", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("legalese", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("section", &QtXmlToSphinx::handleAnchorTag); + m_handlerMap.insert("quotefile", &QtXmlToSphinx::handleQuoteFileTag); + + // ignored tags + m_handlerMap.insert("generatedlist", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("tableofcontents", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("quotefromfile", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("skipto", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("target", &QtXmlToSphinx::handleIgnoredTag); + + // useless tags + m_handlerMap.insert("description", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("definition", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("printuntil", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("relation", &QtXmlToSphinx::handleUselessTag); + + m_result = transform(doc); +} + +void QtXmlToSphinx::pushOutputBuffer() +{ + QString* buffer = new QString(); + m_buffers << buffer; + m_output.setString(buffer); +} + +QString QtXmlToSphinx::popOutputBuffer() +{ + Q_ASSERT(!m_buffers.isEmpty()); + QString* str = m_buffers.pop(); + QString strcpy(*str); + delete str; + m_output.setString(m_buffers.isEmpty() ? 0 : m_buffers.top()); + return strcpy; +} + + +QString QtXmlToSphinx::transform(const QString& doc) +{ + Q_ASSERT(m_buffers.isEmpty()); + Indentation indentation(INDENT); + if (doc.trimmed().isEmpty()) + return doc; + + pushOutputBuffer(); + + QXmlStreamReader reader(doc); + + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (reader.hasError()) { + m_output << INDENT << "XML Error: " + reader.errorString() + "\n" + doc; + ReportHandler::warning("XML Error: " + reader.errorString() + "\n" + doc); + break; + } + + if (token == QXmlStreamReader::StartElement) { + QStringRef tagName = reader.name(); + TagHandler handler = m_handlerMap.value(tagName.toString(), &QtXmlToSphinx::handleUnknownTag); + if (!m_handlers.isEmpty() && ( (m_handlers.top() == &QtXmlToSphinx::handleIgnoredTag) || + (m_handlers.top() == &QtXmlToSphinx::handleRawTag)) ) + handler = &QtXmlToSphinx::handleIgnoredTag; + + m_handlers.push(handler); + } + if (!m_handlers.isEmpty()) + (this->*(m_handlers.top()))(reader); + + if (token == QXmlStreamReader::EndElement) { + m_handlers.pop(); + m_lastTagName = reader.name().toString(); + } + } + m_output.flush(); + QString retval = popOutputBuffer(); + Q_ASSERT(m_buffers.isEmpty()); + return retval; +} + +QString QtXmlToSphinx::readFromLocation(QString& location, QString& identifier) +{ + QFile inputFile; + inputFile.setFileName(location); + if (!inputFile.open(QIODevice::ReadOnly)) { + ReportHandler::warning("Couldn't read code snippet file: "+inputFile.fileName()); + return QString(); + } + + QRegExp searchString("//!\\s*\\[" + identifier + "\\]"); + QRegExp codeSnippetCode("//!\\s*\\[[\\w\\d\\s]+\\]"); + QString code; + QString line; + bool identifierIsEmpty = identifier.isEmpty(); + bool getCode = false; + + while (!inputFile.atEnd()) { + line = inputFile.readLine(); + if (identifierIsEmpty) + code += line; + else if (getCode && !line.contains(searchString)) + code += line.replace(codeSnippetCode, ""); + else if (line.contains(searchString)) + if (getCode) + break; + else + getCode = true; + } + + if (code.isEmpty()) + ReportHandler::warning("Code snippet file found ("+location+"), but snippet "+ identifier +" not found."); + + return code; +} + +void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) +{ + static QString heading; + static char type; + static char types[] = { '-', '^' }; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + int typeIdx = reader.attributes().value("level").toString().toInt(); + if (typeIdx >= sizeof(types)) + type = types[sizeof(types)-1]; + else + type = types[typeIdx]; + } else if (token == QXmlStreamReader::EndElement) { + m_output << createRepeatedChar(heading.length(), type) << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + heading = escape(reader.text()).trimmed(); + m_output << endl << endl << heading << endl; + } +} + +void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString result = popOutputBuffer().simplified(); + if (result.startsWith("**Warning:**")) + result.replace(0, 12, ".. warning:: "); + else if (result.startsWith("**Note:**")) + result.replace(0, 9, ".. note:: "); + + m_output << INDENT << result << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QString text = escape(reader.text()); + if (!m_output.string()->isEmpty()) { + QChar start = text[0]; + QChar end = m_output.string()->at(m_output.string()->length() - 1); + if ((end == '*' || end == '`') && start != ' ' && !start.isPunct()) + m_output << '\\'; + } + m_output << INDENT << text; + } +} + +void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideItalic = !m_insideItalic; + m_output << '*'; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideBold = !m_insideBold; + m_output << "**"; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) + m_output << "``"; + else if (token == QXmlStreamReader::Characters) + m_output << reader.text().toString().trimmed(); +} + +void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + m_output << INDENT << ".. seealso:: "; + else if (token == QXmlStreamReader::EndElement) + m_output << endl; +} + +void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + QString location = reader.attributes().value("location").toString(); + QString identifier = reader.attributes().value("identifier").toString(); + location.prepend(m_generator->codeSnippetDir() + '/'); + QString code = readFromLocation(location, identifier); + if (!consecutiveSnippet) + m_output << INDENT << "::\n\n"; + + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "<Code snippet \"" << location << ':' << identifier << "\" not found>" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} +void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + Indentation indentation(INDENT); + pushOutputBuffer(); + m_output << INDENT; + int indent = reader.attributes().value("indent").toString().toInt(); + for (int i = 0; i < indent; ++i) + m_output << ' '; + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer() << "\n\n\n"; + } +} + +void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_currentTable.clear(); + m_tableHasHeader = false; + } else if (token == QXmlStreamReader::EndElement) { + // write the table on m_output + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString().replace("::", "."); + } else if (token == QXmlStreamReader::EndElement) { + TableCell cell; + cell.data = popOutputBuffer().trimmed(); + m_currentTable << (TableRow() << cell); + } +} + + +void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + if (m_currentTable.isEmpty()) + m_currentTable << TableRow(); + TableRow& row = m_currentTable.last(); + TableCell cell; + cell.colSpan = reader.attributes().value("colspan").toString().toShort(); + cell.rowSpan = reader.attributes().value("rowspan").toString().toShort(); + row << cell; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString data = popOutputBuffer().trimmed(); + if (!m_currentTable.isEmpty()) { + TableRow& row = m_currentTable.last(); + if (!row.isEmpty()) + row.last().data = data; + } + } +} + +void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_tableHasHeader = reader.name() == "header"; + m_currentTable << TableRow(); + } +} + +void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) +{ + // BUG We do not support a list inside a table cell + static QString listType; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + listType = reader.attributes().value("type").toString(); + if (listType == "enum") { + m_currentTable << (TableRow() << "Constant" << "Description"); + m_tableHasHeader = true; + } + INDENT.indent--; + } else if (token == QXmlStreamReader::EndElement) { + INDENT.indent++; + if (!m_currentTable.isEmpty()) { + if (listType == "bullet") { + m_output << endl; + foreach (TableCell cell, m_currentTable.first()) { + QStringList itemLines = cell.data.split('\n'); + m_output << INDENT << "* " << itemLines.first() << endl; + for (int i = 1, max = itemLines.count(); i < max; ++i) + m_output << INDENT << " " << itemLines[i] << endl; + } + m_output << endl; + } else if (listType == "enum") { + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + } + } + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader) +{ + static QString l_linktag; + static QString l_linkref; + static QString l_linktext; + static QString l_linktagending; + static QString l_type; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + l_linktagending = "` "; + if (m_insideBold) { + l_linktag.prepend("**"); + l_linktagending.append("**"); + } else if (m_insideItalic) { + l_linktag.prepend('*'); + l_linktagending.append('*'); + } + l_type = reader.attributes().value("type").toString(); + + // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties + // are recognized as such or not in the binding + if (l_type == "property") + l_type = "function"; + + if (l_type == "typedef") + l_type = "class"; + + QString linkSource; + if (l_type == "function" || l_type == "class") { + linkSource = "raw"; + } else if (l_type == "enum") { + linkSource = "enum"; + } else if (l_type == "page") { + linkSource = "page"; + } else { + linkSource = "href"; + } + + l_linkref = reader.attributes().value(linkSource).toString(); + l_linkref.replace("::", "."); + l_linkref.remove("()"); + + if (l_type == "function" && !m_context.isEmpty()) { + l_linktag = " :meth:`"; + QStringList rawlinklist = l_linkref.split("."); + if (rawlinklist.size() == 1 || rawlinklist[0] == m_context) + l_linkref.prepend("~" + m_context + '.'); + } else if (l_type == "function" && m_context.isEmpty()) { + l_linktag = " :func:`"; + } else if (l_type == "class") { + l_linktag = " :class:`"; + } else if (l_type == "enum") { + l_linktag = " :attr:`"; + } else if (l_type == "page" && l_linkref == m_generator->moduleName()) { + l_linktag = " :mod:`"; + } else { + l_linktag = " :ref:`"; + } + + } else if (token == QXmlStreamReader::Characters) { + QString linktext = reader.text().toString(); + linktext.replace("::", "."); + QString item = l_linkref.split(".").last(); + if (l_linkref == linktext + || (l_linkref + "()") == linktext + || item == linktext + || (item + "()") == linktext) + l_linktext.clear(); + else + l_linktext = linktext + QLatin1String("<"); + } else if (token == QXmlStreamReader::EndElement) { + if (!l_linktext.isEmpty()) + l_linktagending.prepend('>'); + m_output << l_linktag << l_linktext << escape(l_linkref) << l_linktagending; + } +} + +void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString href = reader.attributes().value("href").toString(); + QDir dir(m_generator->outputDirectory() + '/' + m_generator->packageName().replace(".", "/")); + QString imgPath = dir.relativeFilePath(m_generator->libSourceDir() + "/doc/src/") + '/' + href; + + if (reader.name() == "image") + m_output << INDENT << ".. image:: " << imgPath << endl << endl; + else + m_output << ".. image:: " << imgPath << ' '; + } +} + +void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << ".. raw:: " << format.toLower() << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + } +} + +void QtXmlToSphinx::handleCodeTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << "::" << endl << endl; + INDENT.indent++; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + INDENT.indent--; + } +} + +void QtXmlToSphinx::handleUnknownTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + ReportHandler::warning("Unknow QtDoc tag: \"" + reader.name().toString() + "\"."); +} + +void QtXmlToSphinx::handleSuperScriptTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_output << " :sup:`"; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer(); + m_output << '`'; + } +} + +void QtXmlToSphinx::handleIgnoredTag(QXmlStreamReader&) +{ +} + +void QtXmlToSphinx::handleUselessTag(QXmlStreamReader&) +{ + // Tag "description" just marks the init of "Detailed description" title. + // Tag "definition" just marks enums. We have a different way to process them. +} + +void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString anchor; + if (reader.attributes().hasAttribute("id")) + anchor = reader.attributes().value("id").toString(); + else if (reader.attributes().hasAttribute("name")) + anchor = reader.attributes().value("name").toString(); + if (!anchor.isEmpty() && m_opened_anchor != anchor) { + m_opened_anchor = anchor; + m_output << INDENT << ".. _" << m_context << "_" << anchor.toLower() << ":" << endl << endl; + } + } else if (token == QXmlStreamReader::EndElement) { + m_opened_anchor = ""; + } +} + +void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::Characters) { + QString location = reader.text().toString(); + QString identifier = ""; + location.prepend(m_generator->libSourceDir() + '/'); + QString code = readFromLocation(location, identifier); + + m_output << INDENT << "::\n\n"; + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "<Code snippet \"" << location << "\" not found>" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} + +void QtXmlToSphinx::Table::normalize() +{ + if (m_normalized || isEmpty()) + return; + + int row; + int col; + QtXmlToSphinx::Table& self = *this; + + // add col spans + for (row = 0; row < count(); ++row) { + for (col = 0; col < at(row).count(); ++col) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + if (cell.colSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.colSpan = -1; + for (int i = 0, max = cell.colSpan-1; i < max; ++i) { + self[row].insert(col+1, newCell); + } + cell.colSpan = 0; + col++; + } + } + } + + // row spans + const int numCols = first().count(); + for (col = 0; col < numCols; ++col) { + for (row = 0; row < count(); ++row) { + if (col < self[row].count()) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + if (cell.rowSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.rowSpan = -1; + int max = std::min(cell.rowSpan - 1, count()); + cell.rowSpan = 0; + for (int i = 0; i < max; ++i) { + self[row+i+1].insert(col, newCell); + } + row++; + } + } + } + } + m_normalized = true; +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table) +{ + if (table.isEmpty()) + return s; + + if (!table.isNormalized()) { + ReportHandler::warning("Attempt to print an unnormalized table!"); + return s; + } + + // calc width and height of each column and row + QVector<int> colWidths(table.first().count()); + QVector<int> rowHeights(table.count()); + for (int i = 0, maxI = table.count(); i < maxI; ++i) { + const QtXmlToSphinx::TableRow& row = table[i]; + for (int j = 0, maxJ = row.count(); j < maxJ; ++j) { + QStringList rowLines = row[j].data.split('\n'); // cache this would be a good idea + foreach (QString str, rowLines) + colWidths[j] = std::max(colWidths[j], str.count()); + rowHeights[i] = std::max(rowHeights[i], row[j].data.count('\n') + 1); + } + } + + if (!*std::max_element(colWidths.begin(), colWidths.end())) + return s; // empty table (table with empty cells) + + // create a horizontal line to be used later. + QString horizontalLine("+"); + for (int i = 0, max = colWidths.count(); i < max; ++i) { + horizontalLine += createRepeatedChar(colWidths[i], '-'); + horizontalLine += '+'; + } + + // write table rows + for (int i = 0, maxI = table.count(); i < maxI; ++i) { // for each row + const QtXmlToSphinx::TableRow& row = table[i]; + + // print line + s << INDENT << '+'; + char c = (!i && table.hasHeader()) ? '=' : '-'; + for (int col = 0, max = colWidths.count(); col < max; ++col) { + char c; + if (row[col].rowSpan == -1) + c = ' '; + else if (i == 1 && table.hasHeader()) + c = '='; + else + c = '-'; + s << createRepeatedChar(colWidths[col], c) << '+'; + } + s << endl; + + + // Print the table cells + for (int rowLine = 0; rowLine < rowHeights[i]; ++rowLine) { // for each line in a row + for (int j = 0, maxJ = row.count(); j < maxJ; ++j) { // for each column + const QtXmlToSphinx::TableCell& cell = row[j]; + QStringList rowLines = cell.data.split('\n'); // FIXME: Cache this!!! + if (!j) // First column, so we need print the identation + s << INDENT; + + if (!j || !cell.colSpan) + s << '|'; + else + s << ' '; + s << qSetFieldWidth(colWidths[j]) << left; + s << (rowLine < rowLines.count() ? rowLines[rowLine] : ""); + s << qSetFieldWidth(0); + } + s << '|' << endl; + } + } + s << INDENT << horizontalLine << endl; + s << endl; + return s; +} + +static QString getClassName(const AbstractMetaClass *cppClass) { + return cppClass->name().replace("::", "."); +} + +static QString getFuncName(const AbstractMetaFunction *cppFunc) { + static bool hashInitialized = false; + static QHash<QString, QString> operatorsHash; + if (!hashInitialized) { + operatorsHash.insert("operator+", "__add__"); + operatorsHash.insert("operator+=", "__iadd__"); + operatorsHash.insert("operator-", "__sub__"); + operatorsHash.insert("operator-=", "__isub__"); + operatorsHash.insert("operator*", "__mul__"); + operatorsHash.insert("operator*=", "__imul__"); + operatorsHash.insert("operator/", "__div__"); + operatorsHash.insert("operator/=", "__idiv__"); + operatorsHash.insert("operator%", "__mod__"); + operatorsHash.insert("operator%=", "__imod__"); + operatorsHash.insert("operator<<", "__lshift__"); + operatorsHash.insert("operator<<=", "__ilshift__"); + operatorsHash.insert("operator>>", "__rshift__"); + operatorsHash.insert("operator>>=", "__irshift__"); + operatorsHash.insert("operator&", "__and__"); + operatorsHash.insert("operator&=", "__iand__"); + operatorsHash.insert("operator|", "__or__"); + operatorsHash.insert("operator|=", "__ior__"); + operatorsHash.insert("operator^", "__xor__"); + operatorsHash.insert("operator^=", "__ixor__"); + operatorsHash.insert("operator==", "__eq__"); + operatorsHash.insert("operator!=", "__ne__"); + operatorsHash.insert("operator<", "__lt__"); + operatorsHash.insert("operator<=", "__le__"); + operatorsHash.insert("operator>", "__gt__"); + operatorsHash.insert("operator>=", "__ge__"); + hashInitialized = true; + } + + QHash<QString, QString>::const_iterator it = operatorsHash.find(cppFunc->name()); + QString result = it != operatorsHash.end() ? it.value() : cppFunc->name(); + return result.replace("::", "."); +} + +QString DocGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const +{ + return QString("%1.rst").arg(getClassName(cppClass)); +} + +void DocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaClass) +{ + QString metaClassName; + + if (metaClass) + metaClassName = getClassName(metaClass); + + if (doc.format() == Documentation::Native) { + QtXmlToSphinx x(this, doc.value(), metaClassName); + s << x; + } else { + s << doc.value(); + } + + s << endl; +} + +void DocGenerator::writeFunctionBrief(QTextStream &s, + const AbstractMetaClass *cppClass, + const AbstractMetaFunction *cppFunction) +{ + s << INDENT << "def :meth:`" + << cppFunction->name() << "<"; + if (cppClass && cppClass->name() != cppFunction->name()) + s << getClassName(cppClass) << '.'; + + s << cppFunction->name() << ">`" + << " (" << parseArgDocStyle(cppClass, cppFunction) << "):"; +} + +void DocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass) +{ + QString doc; + QTextStream doc_s(&doc); + + ReportHandler::debugSparse("Generating Documentation for " + cppClass->fullName()); + s << ".. module:: " << packageName() << endl; + QString className = getClassName(cppClass); + s << ".. _" << className << ":" << endl << endl; + + s << className << endl; + s << createRepeatedChar(className.count(), '*') << endl << endl; + + s << ".. inheritance-diagram:: " << className << endl + << " :parts: 2" << endl << endl; // TODO: This would be a parameter in the future... + + //Function list + AbstractMetaFunctionList functionList = filterFunctions(cppClass); + qSort(functionList.begin(), functionList.end(), functionSort); + +#if 0 + if (functionList.size() > 0) + { + QtXmlToSphinx::Table functionTable; + QtXmlToSphinx::TableRow row; + + s << "Functions\n" + "---------\n\n"; + + + foreach (AbstractMetaFunction *func, functionList) { + if ((func->isConstructor() || func->isModifiedRemoved()) || + (func->declaringClass() != cppClass)) + continue; + + QString rowString; + QTextStream rowStream(&rowString); + + writeFunctionBrief(rowStream, cppClass, func); + row << rowString; + functionTable << row; + row.clear(); + } + functionTable.normalize(); + s << functionTable; + } + +#endif + + doc_s << "Detailed Description\n" + "--------------------\n\n"; + + writeInjectDocumentation(doc_s, DocModification::Prepend, cppClass, 0); + writeFormatedText(doc_s, cppClass->documentation(), cppClass); + + + if (!cppClass->isNamespace()) { + + writeConstructors(doc_s, cppClass); + writeEnums(doc_s, cppClass); + writeFields(doc_s, cppClass); + + foreach (AbstractMetaFunction *func, functionList) { + if ((func->isConstructor() || func->isModifiedRemoved()) || + (func->declaringClass() != cppClass)) + continue; + + if (func->isStatic()) + doc_s << ".. staticmethod:: "; + else + doc_s << ".. method:: "; + + writeFunction(doc_s, true, cppClass, func); + } + } + + writeInjectDocumentation(doc_s, DocModification::Append, cppClass, 0); + + writeFunctionList(s, doc, cppClass); + + s << doc; +} + +QString DocGenerator::parseFunctionDeclaration(const QString &doc, const AbstractMetaClass *cppClass) +{ + //.. method:: QObject.childEvent(arg__1) + //def :meth:`removeEventFilter<QObject.removeEventFilter>` (arg__1): + + QString data = doc; + QString markup; + + if (data.startsWith(".. method::")) + markup = ".. method::"; + else if (data.startsWith(".. staticmethod::")) + markup = ".. staticmethod::"; + else + return QString(); + + data = data.mid(markup.size()); //remove .. method:: + data = data.mid(data.indexOf(".") + 1); //remove class name + + QString methName = data.mid(0, data.indexOf("(")); + QString methArgs = data.mid(data.indexOf("(")); + + data = QString("def :meth:`%1<%2.%3>` %4") + .arg(methName) + .arg(cppClass->name()) + .arg(methName) + .arg(methArgs); + + return data; +} + + +void DocGenerator::writeFunctionList(QTextStream &s, const QString &content, const AbstractMetaClass *cppClass) +{ + QStringList functionList; + QStringList staticFunctionList; + + QStringList lst = content.split("\n"); + foreach(QString row, lst) { + QString data = row.trimmed(); + if (data.startsWith(".. method::")) { + functionList << parseFunctionDeclaration(data, cppClass); + } + else if (data.startsWith(".. staticmethod::")) { + staticFunctionList << parseFunctionDeclaration(data, cppClass); + } + } + + if ((functionList.size() > 0) || (staticFunctionList.size() > 0)) + { + QtXmlToSphinx::Table functionTable; + QtXmlToSphinx::TableRow row; + + s << "Synopsis" << endl + << "--------" << endl << endl; + + if (functionList.size() > 0) { + s << "Functions" << endl + << "^^^^^^^^^" << endl << endl; + + qSort(functionList); + foreach (QString func, functionList) { + row << func; + functionTable << row; + row.clear(); + } + + functionTable.normalize(); + s << functionTable << endl; + functionTable.clear(); + } + + if (staticFunctionList.size() > 0) { + s << "Static functions" << endl + << "^^^^^^^^^^^^^^^^" << endl; + + qSort(staticFunctionList); + foreach (QString func, staticFunctionList) { + row << func; + functionTable << row; + row.clear(); + } + + functionTable.normalize(); + s << functionTable << endl; + } + } +} + +void DocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaEnum *en, cppClass->enums()) { + s << section_title << getClassName(cppClass) << "." << en->name() << endl << endl; + writeFormatedText(s, en->documentation(), cppClass); + } +} + +void DocGenerator::writeFields(QTextStream &s, const AbstractMetaClass *cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaField *field, cppClass->fields()) { + s << section_title << getClassName(cppClass) << "." << field->name() << endl << endl; + //TODO: request for member ‘documentation’ is ambiguous + writeFormatedText(s, field->AbstractMetaAttributes::documentation(), cppClass); + } +} + +void DocGenerator::writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass) +{ + static const QString sectionTitle = ".. class:: "; + static const QString sectionTitleSpace = QString(sectionTitle.size(), ' '); + + AbstractMetaFunctionList lst = cppClass->queryFunctions(AbstractMetaClass::Constructors | AbstractMetaClass::Visible); + + bool first = true; + QHash<QString, AbstractMetaArgument *> arg_map; + + foreach(AbstractMetaFunction *func, lst) { + if (func->isModifiedRemoved()) + continue; + + if (first) { + first = false; + s << sectionTitle; + } else { + s << sectionTitleSpace; + } + writeFunction(s, false, cppClass, func); + foreach(AbstractMetaArgument *arg, func->arguments()) + { + if (!arg_map.contains(arg->argumentName())) { + arg_map.insert(arg->argumentName(), arg); + } + } + } + + s << endl; + + foreach (AbstractMetaArgument *arg, arg_map.values()) { + Indentation indentation(INDENT); + writeParamerteType(s, cppClass, arg); + } + + s << endl; + + foreach (AbstractMetaFunction *func, lst) { + writeFormatedText(s, func->documentation(), cppClass); + } +} + +QString DocGenerator::parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func) +{ + QString ret; + bool optional = false; + + foreach (AbstractMetaArgument *arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + if (arg->argumentIndex() > 0) + ret += ","; + + if (!arg->defaultValueExpression().isEmpty() && (!optional)) { + ret += "["; + optional = true; + } + + ret += arg->argumentName(); + + if (optional) + ret += "=" + arg->defaultValueExpression(); + } + + if (optional) + ret += "]"; + + return ret; +} + +void DocGenerator::writeDocSnips(QTextStream &s, + const CodeSnipList &codeSnips, + CodeSnip::Position position, + TypeSystem::Language language) +{ + Indentation indentation(INDENT); + QStringList invalidStrings; + const static QString startMarkup("[sphinx-begin]"); + const static QString endMarkup("[sphinx-end]"); + + invalidStrings << "*" << "//" << "/*" << "*/"; + + foreach (CodeSnip snip, codeSnips) { + if ((snip.position != position) || + !(snip.language & language)) + continue; + + QString code = snip.code(); + while (code.contains(startMarkup) && code.contains(endMarkup)) { + int startBlock = code.indexOf(startMarkup) + startMarkup.size(); + int endBlock = code.indexOf(endMarkup); + + if ((startBlock == -1) || (endBlock == -1)) + break; + + QString codeBlock = code.mid(startBlock, endBlock - startBlock); + QStringList rows = codeBlock.split("\n"); + int currenRow = 0; + int offset = 0; + + foreach(QString row, rows) { + foreach(QString invalidString, invalidStrings) { + row = row.remove(invalidString); + } + + if (row.trimmed().size() == 0) { + if (currenRow == 0) + continue; + else + s << endl; + } + + if (currenRow == 0) { + //find offset + for (int i=0, i_max = row.size(); i < i_max; i++) { + if (row[i] == ' ') + offset++; + else if (row[i] == '\n') + offset = 0; + else + break; + } + } + row = row.mid(offset); + s << row << endl; + currenRow++; + } + + code = code.mid(endBlock+endMarkup.size()); + } + } +} + +void DocGenerator::writeInjectDocumentation(QTextStream &s, + DocModification::Mode mode, + const AbstractMetaClass *cppClass, + const AbstractMetaFunction *func) +{ + Indentation indentation(INDENT); + + foreach (DocModification mod, cppClass->typeEntry()->docModifications()) { + if (mod.mode() == mode) { + bool modOk = func ? mod.signature() == func->minimalSignature() : mod.signature().isEmpty(); + + if (modOk) { + Documentation doc; + Documentation::Format fmt; + + if (mod.format == TypeSystem::NativeCode) + fmt = Documentation::Native; + else if (mod.format == TypeSystem::TargetLangCode) + fmt = Documentation::Target; + else + continue; + + doc.setValue(mod.code() , fmt); + s << INDENT; + writeFormatedText(s, doc, cppClass); + } + } + } + + s << endl; + + if (func) { + writeDocSnips(s, getCodeSnips(func), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } else { + writeDocSnips(s, cppClass->typeEntry()->codeSnips(), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } +} + +void DocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + if (!func->isConstructor()) + s << getClassName(cppClass) << '.'; + s << getFuncName(func) << "(" << parseArgDocStyle(cppClass, func) << ")"; +} + +QString DocGenerator::translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass) +{ + QString originalType = translateType(type, cppClass, Generator::ExcludeConst | Generator::ExcludeReference); + QString strType = originalType; + + //remove "*" + strType.remove("*"); + TypeEntry *te = TypeDatabase::instance()->findType(originalType.trimmed()); + if (te) { + return te->targetLangName(); + } else { + //remove <, > + strType.remove(">"); + strType.remove("<"); + + //replace :: + strType.replace("::", "."); + + //Translate ContainerType + if (strType.contains("QList") || strType.contains("QVector")) { + strType.replace("QList", "List of "); + strType.replace("QVector", "List of "); + } else if (strType.contains("QHash") || strType.contains("QMap")) { + strType.remove("QHash"); + strType.remove("QMap"); + QStringList types = strType.split(","); + strType = QString("Dictionary with keys of type %1 and values of type %2.") + .arg(types[0]).arg(types[1]); + } + return strType; + } +} + +void DocGenerator::writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg) +{ + s << INDENT << ":param " << arg->argumentName() << ": " + << translateToPythonType(arg->type(), cppClass) << endl; +} + +void DocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + + s << endl; + foreach (AbstractMetaArgument *arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + writeParamerteType(s, cppClass, arg); + } + + if (!func->isConstructor() && func->type()) { + s << INDENT << ":rtype: " << translateToPythonType(func->type(), cppClass) << endl; + } + s << endl; +} + +void DocGenerator::writeFunction(QTextStream &s, bool writeDoc, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +{ + writeFunctionSignature(s, cppClass, func); + s << endl; + + if (writeDoc) { + s << endl; + writeFunctionParametersType(s, cppClass, func); + s << endl; + writeInjectDocumentation(s, DocModification::Prepend, cppClass, func); + writeFormatedText(s, func->documentation(), cppClass); + writeInjectDocumentation(s, DocModification::Append, cppClass, func); + } +} + +void DocGenerator::finishGeneration() +{ + if (classes().isEmpty()) + return; + + QFile input(outputDirectory() + '/' + subDirectoryForPackage(packageName()) + "/index.rst"); + input.open(QIODevice::WriteOnly); + QTextStream s(&input); + + s << ".. module:: " << packageName() << endl << endl; + + QString title = packageName() + " contents"; + s << title << endl; + s << createRepeatedChar(title.length(), '*') << endl << endl; + s << ".. toctree::" << endl; + + /* Avoid showing "Detailed Description for *every* class in toc tree */ + Indentation indentation(INDENT); + s << INDENT << ":maxdepth: 1" << endl << endl; + + QStringList classList; + foreach (AbstractMetaClass *cls, classes()) { + if (!shouldGenerate(cls)) + continue; + classList << getClassName(cls); + } + classList.sort(); + + foreach (QString clazz, classList) + s << INDENT << clazz << endl; + + s << endl << endl; + + s << "Detailed Description" << endl; + s << "--------------------" << endl << endl; + + if (m_moduleDoc.format() == Documentation::Native) { + QtXmlToSphinx x(this, m_moduleDoc.value(), moduleName()); + s << x; + } else { + s << m_moduleDoc.value(); + } +} + +bool DocGenerator::prepareGeneration(const QMap<QString, QString>& args) +{ + BoostPythonGenerator::prepareGeneration(args); + m_libSourceDir = args.value("library-source-dir"); + setOutputDirectory(args.value("documentation-out-dir")); + m_docDataDir = args.value("documentation-data-dir"); + m_codeSnippetDir = args.value("documentation-code-snippets-dir", m_libSourceDir); + + if (m_libSourceDir.isEmpty() || m_docDataDir.isEmpty()) { + ReportHandler::warning("Documentation data dir and/or Qt source dir not informed, " + "documentation will not be extracted from Qt sources."); + return false; + } else { + QtDocParser docParser; + docParser.setPackageName(packageName()); + docParser.setDocumentationDataDirectory(m_docDataDir); + docParser.setLibrarySourceDirectory(m_libSourceDir); + foreach(AbstractMetaClass* cppClass, classes()) { + docParser.fillDocumentation(cppClass); + } + m_moduleDoc = docParser.retrieveModuleDocumentation(); + return true; + } +} + + +QMap<QString, QString> DocGenerator::options() const +{ + QMap<QString, QString> options; + options.insert("library-source-dir", "Directory where library source code is located"); + options.insert("documentation-out-dir", "The directory where the generated documentation files will be written"); + options.insert("documentation-data-dir", "Directory with XML files generated by documentation tool (qdoc3 or Doxygen)"); + options.insert("documentation-code-snippets-dir", "Directory used to search code snippets used by the documentation"); + return options; +} + diff --git a/generators/qtdoc/qtdocgenerator.h b/generators/qtdoc/qtdocgenerator.h new file mode 100644 index 000000000..cef7fa7a3 --- /dev/null +++ b/generators/qtdoc/qtdocgenerator.h @@ -0,0 +1,212 @@ +/* + * This file is part of the Boost Python Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef DOCGENERATOR_H +#define DOCGENERATOR_H + +#include "boostpythongenerator.h" +#include <QtCore/QStack> + +class QXmlStreamReader; +class DocGenerator; + +class QtXmlToSphinx +{ +public: + struct TableCell + { + short rowSpan; + short colSpan; + QString data; + + TableCell(const QString& text = QString()) : rowSpan(0), colSpan(0), data(text) {} + TableCell(const char* text) : rowSpan(0), colSpan(0), data(text) {} + }; + + typedef QList<TableCell> TableRow; + class Table : public QList<TableRow> + { + public: + Table() : m_hasHeader(false), m_normalized(false) + { + } + + void enableHeader(bool enable) + { + m_hasHeader = enable; + } + + bool hasHeader() const + { + return m_hasHeader; + } + + void normalize(); + + bool isNormalized() const + { + return m_normalized; + } + + void clear() { + m_normalized = false; + QList<TableRow>::clear(); + } + + private: + bool m_hasHeader; + bool m_normalized; + }; + + QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context = QString()); + + QString result() const + { + return m_result; + } + +private: + QString transform(const QString& doc); + + void handleHeadingTag(QXmlStreamReader& reader); + void handleParaTag(QXmlStreamReader& reader); + void handleItalicTag(QXmlStreamReader& reader); + void handleBoldTag(QXmlStreamReader& reader); + void handleArgumentTag(QXmlStreamReader& reader); + void handleSeeAlsoTag(QXmlStreamReader& reader); + void handleSnippetTag(QXmlStreamReader& reader); + void handleDotsTag(QXmlStreamReader& reader); + void handleLinkTag(QXmlStreamReader& reader); + void handleImageTag(QXmlStreamReader& reader); + void handleListTag(QXmlStreamReader& reader); + void handleTermTag(QXmlStreamReader& reader); + void handleSuperScriptTag(QXmlStreamReader& reader); + void handleQuoteFileTag(QXmlStreamReader& reader); + + // table tagsvoid QtXmlToSphinx::handleValueTag(QXmlStreamReader& reader) + + void handleTableTag(QXmlStreamReader& reader); + void handleRowTag(QXmlStreamReader& reader); + void handleItemTag(QXmlStreamReader& reader); + void handleRawTag(QXmlStreamReader& reader); + void handleCodeTag(QXmlStreamReader& reader); + + void handleIgnoredTag(QXmlStreamReader& reader); + void handleUnknownTag(QXmlStreamReader& reader); + void handleUselessTag(QXmlStreamReader& reader); + void handleAnchorTag(QXmlStreamReader& reader); + + typedef void (QtXmlToSphinx::*TagHandler)(QXmlStreamReader&); + QHash<QString, TagHandler> m_handlerMap; + QStack<TagHandler> m_handlers; + QTextStream m_output; + QString m_result; + + QStack<QString*> m_buffers; + + + Table m_currentTable; + bool m_tableHasHeader; + QString m_context; + DocGenerator* m_generator; + bool m_insideBold; + bool m_insideItalic; + QString m_lastTagName; + QString m_opened_anchor; + + QString readFromLocation(QString& location, QString& identifier); + void pushOutputBuffer(); + QString popOutputBuffer(); + void writeTable(Table& table); +}; + +inline QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx& xmlToSphinx) +{ + return s << xmlToSphinx.result(); +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table); + +/** +* The DocGenerator generates documentation from library being binded. +*/ +class DocGenerator : public BoostPythonGenerator +{ +public: + virtual GeneratorType type() const + { + return DocumentationType; + } + + QString libSourceDir() const + { + return m_libSourceDir; + } + + virtual bool prepareGeneration(const QMap<QString, QString>& args); + + const char* name() const + { + return "DocGenerator"; + } + + QMap<QString, QString> options() const; + + QString codeSnippetDir() const + { + return m_codeSnippetDir; + } + +protected: + QString fileNameForClass(const AbstractMetaClass* cppClass) const; + void generateClass(QTextStream& s, const AbstractMetaClass* cppClass); + void finishGeneration(); +private: + void writeEnums(QTextStream& s, const AbstractMetaClass* cppClass); + + void writeFields(QTextStream &s, const AbstractMetaClass *cppClass); + void writeArguments(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + void writeFunctionBrief(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *cppFunction); + void writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunction(QTextStream& s, bool writeDoc, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func); + void writeFunctionList(QTextStream &se, const QString &content, const AbstractMetaClass *cppClass); + void writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg); + + void writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass); + void writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaclass = 0); + void writeInjectDocumentation(QTextStream &s, DocModification::Mode mode, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + void writeDocSnips(QTextStream &s, const CodeSnipList &codeSnips, CodeSnip::Position position, TypeSystem::Language language); + + + QString parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + QString parseFunctionDeclaration(const QString &data, const AbstractMetaClass *cppClass); + QString translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass); + + QString m_docDataDir; + QString m_libSourceDir; + QString m_codeSnippetDir; + QStringList m_functionList; + Documentation m_moduleDoc; +}; + +#endif // DOCGENERATOR_H |