aboutsummaryrefslogtreecommitdiffstats
path: root/generators
diff options
context:
space:
mode:
authorMarcelo Lira <marcelo.lira@openbossa.org>2009-08-25 19:43:06 -0300
committerMarcelo Lira <marcelo.lira@openbossa.org>2009-08-25 19:43:06 -0300
commitfd52957d375eb990a0fabbfab35d36480ee057ae (patch)
tree3ef09ee06c63dc953dfa25829cc8f4a71bb81dde /generators
parent8f941405798b67204945d7ec94ef8e63e1535877 (diff)
parentf8fba84d7b0230b1f48109c339197c407757bea1 (diff)
Merge commit 'mainline/master' into metaclassname
Conflicts: boostpythongenerator.cpp
Diffstat (limited to 'generators')
-rw-r--r--generators/CMakeLists.txt2
-rw-r--r--generators/boostpython/CMakeLists.txt14
-rw-r--r--generators/boostpython/boostpython.cpp29
-rw-r--r--generators/boostpython/boostpythongenerator.cpp311
-rw-r--r--generators/boostpython/boostpythongenerator.h129
-rw-r--r--generators/boostpython/convertergenerator.cpp180
-rw-r--r--generators/boostpython/convertergenerator.h77
-rw-r--r--generators/boostpython/cppgenerator.cpp1439
-rw-r--r--generators/boostpython/cppgenerator.h100
-rw-r--r--generators/boostpython/hppgenerator.cpp220
-rw-r--r--generators/boostpython/hppgenerator.h51
-rw-r--r--generators/qtdoc/CMakeLists.txt10
-rw-r--r--generators/qtdoc/qtdocgenerator.cpp1391
-rw-r--r--generators/qtdoc/qtdocgenerator.h222
14 files changed, 4175 insertions, 0 deletions
diff --git a/generators/CMakeLists.txt b/generators/CMakeLists.txt
new file mode 100644
index 000000000..73b5eea22
--- /dev/null
+++ b/generators/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(boostpython)
+add_subdirectory(qtdoc)
diff --git a/generators/boostpython/CMakeLists.txt b/generators/boostpython/CMakeLists.txt
new file mode 100644
index 000000000..b29b8cf89
--- /dev/null
+++ b/generators/boostpython/CMakeLists.txt
@@ -0,0 +1,14 @@
+project(boostpython)
+
+set(boostpython_generator_SRC
+boostpythongenerator.cpp
+convertergenerator.cpp
+cppgenerator.cpp
+hppgenerator.cpp
+boostpython.cpp
+)
+
+add_library(boostpython_generator SHARED ${boostpython_generator_SRC})
+target_link_libraries(boostpython_generator ${APIEXTRACTOR_LIBRARY} ${QT_QTCORE_LIBRARY} genrunner)
+
+install(TARGETS boostpython_generator DESTINATION ${LIB_INSTALL_DIR})
diff --git a/generators/boostpython/boostpython.cpp b/generators/boostpython/boostpython.cpp
new file mode 100644
index 000000000..e165f93b9
--- /dev/null
+++ b/generators/boostpython/boostpython.cpp
@@ -0,0 +1,29 @@
+/*
+* This file is part of the API Extractor 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 "generator.h"
+#include "hppgenerator.h"
+#include "cppgenerator.h"
+#include "convertergenerator.h"
+
+EXPORT_GENERATOR_PLUGIN(new HppGenerator << new CppGenerator << new ConverterGenerator)
diff --git a/generators/boostpython/boostpythongenerator.cpp b/generators/boostpython/boostpythongenerator.cpp
new file mode 100644
index 000000000..8930c40cf
--- /dev/null
+++ b/generators/boostpython/boostpythongenerator.cpp
@@ -0,0 +1,311 @@
+/*
+ * 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 "boostpythongenerator.h"
+#include <reporthandler.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QVariant>
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+
+#define NULL_VALUE "NULL"
+#define COMMENT_LINE_WIDTH 77
+
+static Indentor INDENT;
+static void dump_function(AbstractMetaFunctionList lst);
+
+QString BoostPythonGenerator::getWrapperName(const AbstractMetaClass* metaClass)
+{
+ QString result = metaClass->typeEntry()->qualifiedCppName().toLower();
+ result.replace("::", "_");
+ result += "_wrapper";
+ return result;
+}
+
+QString BoostPythonGenerator::argumentString(const AbstractMetaFunction *cppFunction,
+ const AbstractMetaArgument *cppArgument,
+ uint options) const
+{
+ QString modifiedType = cppFunction->typeReplaced(cppArgument->argumentIndex() + 1);
+ QString arg;
+
+ if (modifiedType.isEmpty())
+ arg = translateType(cppArgument->type(), cppFunction->implementingClass(), (Generator::Option) options);
+ else
+ arg = modifiedType.replace('$', '.');
+
+ if (!(options & Generator::SkipName)) {
+ arg += " ";
+ arg += cppArgument->argumentName();
+ }
+
+ QList<ReferenceCount> referenceCounts;
+ referenceCounts = cppFunction->referenceCounts(cppFunction->implementingClass(), cppArgument->argumentIndex() + 1);
+ if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues &&
+ !cppArgument->defaultValueExpression().isEmpty()) {
+ QString defaultValue = cppArgument->defaultValueExpression();
+ if (defaultValue == "NULL")
+ defaultValue = NULL_VALUE;
+
+ //WORKAROUND: fix this please
+ if (defaultValue.startsWith("new "))
+ defaultValue.remove(0, 4);
+
+ arg += " = " + defaultValue;
+ }
+
+ return arg;
+}
+
+void BoostPythonGenerator::writeArgument(QTextStream &s,
+ const AbstractMetaFunction *func,
+ const AbstractMetaArgument *cppArgument,
+ uint options) const
+{
+ s << argumentString(func, cppArgument, options);
+}
+
+void BoostPythonGenerator::writeFunctionArguments(QTextStream &s,
+ const AbstractMetaFunction *func,
+ uint options) const
+{
+ AbstractMetaArgumentList arguments = func->arguments();
+
+ if (options & Generator::WriteSelf) {
+ s << func->implementingClass()->name() << '&';
+ if (!(options & SkipName))
+ s << " self";
+ }
+
+ int argUsed = 0;
+ for (int i = 0; i < arguments.size(); ++i) {
+ if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i + 1))
+ continue;
+
+ if ((options & Generator::WriteSelf) || argUsed)
+ s << ", ";
+
+ writeArgument(s, func, arguments[i], options);
+ argUsed++;
+ }
+}
+
+QString BoostPythonGenerator::functionReturnType(const AbstractMetaFunction* func, int option)
+{
+ QString modifiedReturnType = QString(func->typeReplaced(0));
+ if (!modifiedReturnType.isNull() && (!(option & OriginalTypeDescription)))
+ return modifiedReturnType;
+ else
+ return translateType(func->type(), func->implementingClass(), option);
+}
+
+QString BoostPythonGenerator::functionSignature(const AbstractMetaFunction *func,
+ QString prepend,
+ QString append,
+ int option,
+ int argCount)
+{
+ AbstractMetaArgumentList arguments = func->arguments();
+ int argument_count = argCount < 0 ? arguments.size() : argCount;
+
+
+ QString result;
+ QTextStream s(&result);
+ // The actual function
+ if (!(func->isEmptyFunction() ||
+ func->isNormal() ||
+ func->isSignal())) {
+ option = Option(option | Generator::SkipReturnType);
+ } else {
+ s << functionReturnType(func, option) << ' ';
+ }
+
+ // name
+ QString name(func->originalName());
+ if (func->isConstructor())
+ name = getWrapperName(func->ownerClass());
+
+ s << prepend << name << append << "(";
+ writeFunctionArguments(s, func, option);
+ s << ")";
+
+ if (func->isConstant() && (!(option & Generator::ExcludeMethodConst)))
+ s << " const";
+
+ return result;
+}
+
+QString BoostPythonGenerator::signatureForDefaultVirtualMethod(const AbstractMetaFunction *cppFunction,
+ QString prepend,
+ QString append,
+ int option,
+ int arg_count)
+{
+ QString defaultMethodSignature = functionSignature(cppFunction, prepend, append, option, arg_count);
+ QString staticSelf("(");
+ if (cppFunction->isConstant())
+ staticSelf += "const ";
+
+ staticSelf += cppFunction->ownerClass()->qualifiedCppName() + "& ";
+ if (!(option & SkipName))
+ staticSelf += " self";
+
+ if (cppFunction->arguments().size() > 0)
+ staticSelf += ", ";
+
+ defaultMethodSignature.replace(defaultMethodSignature.lastIndexOf(") const"), 7, ")");
+ defaultMethodSignature.replace(defaultMethodSignature.indexOf('('), 1, staticSelf);
+ return defaultMethodSignature;
+}
+
+void BoostPythonGenerator::writeArgumentNames(QTextStream &s,
+ const AbstractMetaFunction *func,
+ uint options) const
+{
+ AbstractMetaArgumentList arguments = func->arguments();
+ int argCount = 0;
+ for (int j = 0, max = arguments.size(); j < max; j++) {
+
+ if ((options & Generator::SkipRemovedArguments) &&
+ (func->argumentRemoved(arguments.at(j)->argumentIndex() + 1))) {
+ continue;
+ }
+
+ if (argCount > 0)
+ s << ", ";
+
+ if ((options & Generator::BoxedPrimitive) &&
+ !arguments.at(j)->type()->isReference() &&
+ (arguments.at(j)->type()->isQObject() ||
+ arguments.at(j)->type()->isObject())) {
+ s << "PySide::ptr( " << arguments.at(j)->argumentName() << ")";
+ } else {
+ s << arguments.at(j)->argumentName();
+ }
+ argCount++;
+ }
+}
+
+void BoostPythonGenerator::writeFunctionCall(QTextStream &s,
+ const AbstractMetaFunction* func,
+ uint options)
+
+{
+ if (!(options & Generator::SkipName))
+ s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName());
+
+ s << '(';
+ writeArgumentNames(s, func, options);
+ s << ')';
+}
+
+void BoostPythonGenerator::writeCodeSnips(QTextStream &s,
+ const CodeSnipList &codeSnips,
+ CodeSnip::Position position,
+ TypeSystem::Language language,
+ const AbstractMetaFunction *func)
+{
+ Indentation indentation(INDENT);
+ foreach (CodeSnip snip, codeSnips) {
+ if ((snip.position != position) ||
+ !(snip.language & language)) {
+ continue;
+ }
+
+ QString code;
+ QTextStream tmpStream(&code);
+ formatCode(tmpStream, snip.code(), INDENT);
+
+ if (func)
+ replaceTemplateVariables(code, func);
+
+ s << code << endl;
+ }
+}
+
+bool BoostPythonGenerator::canCreateWrapperFor(const AbstractMetaClass* cppClass)
+{
+ return !cppClass->hasPrivateDestructor() && !cppClass->isNamespace();
+}
+
+
+
+QStringList BoostPythonGenerator::getBaseClasses(const AbstractMetaClass *cppClass)
+{
+ QStringList baseClass;
+
+ if (!cppClass->baseClassName().isEmpty() &&
+ (cppClass->name() != cppClass->baseClassName())) {
+ baseClass.append(cppClass->baseClassName());
+ }
+
+ foreach (AbstractMetaClass *interface, cppClass->interfaces()) {
+ AbstractMetaClass *aux = interface->primaryInterfaceImplementor();
+ if (!aux)
+ continue;
+
+ //skip templates
+ if (aux->templateArguments().size() > 0)
+ continue;
+
+ if (!aux->name().isEmpty() && (cppClass->qualifiedCppName() != aux->qualifiedCppName()))
+ baseClass.append(aux->qualifiedCppName());
+ }
+
+ return baseClass;
+}
+
+
+bool BoostPythonGenerator::isCopyable(const AbstractMetaClass *cppClass)
+{
+ if (cppClass->typeEntry()->copyable() == ComplexTypeEntry::Unknown)
+ return cppClass->hasCloneOperator();
+ else
+ return (cppClass->typeEntry()->copyable() == ComplexTypeEntry::CopyableSet);
+
+ return false;
+}
+
+static void dump_function(AbstractMetaFunctionList lst)
+{
+ qDebug() << "DUMP FUNCTIONS: ";
+ foreach (AbstractMetaFunction *func, lst) {
+ qDebug() << "*" << func->ownerClass()->name()
+ << func->signature()
+ << "Private: " << func->isPrivate()
+ << "Empty: " << func->isEmptyFunction()
+ << "Static:" << func->isStatic()
+ << "Signal:" << func->isSignal()
+ << "ClassImplements: " << (func->ownerClass() != func->implementingClass())
+ << "is operator:" << func->isOperatorOverload()
+ << "is global:" << func->isInGlobalScope();
+ }
+}
+
+
+bool BoostPythonGenerator::doSetup(const QMap<QString, QString>&)
+{
+ return true;
+}
diff --git a/generators/boostpython/boostpythongenerator.h b/generators/boostpython/boostpythongenerator.h
new file mode 100644
index 000000000..14311b2e0
--- /dev/null
+++ b/generators/boostpython/boostpythongenerator.h
@@ -0,0 +1,129 @@
+/*
+ * 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 BOOSTPYTHONGENERATOR_H
+#define BOOSTPYTHONGENERATOR_H
+
+#include <QtCore/QTextStream>
+#include "generator.h"
+
+class DocParser;
+
+/**
+* Abstract generator that contains common methods used in CppGenerator and HppGenerator.
+*/
+class BoostPythonGenerator : public Generator
+{
+public:
+ /**
+ * Write a function argument in the boost::python format in the text stream \p s.
+ * This function just call \code s << argumentString(); \endcode
+ * \param s text stream used to write the output.
+ * \param boost_fuction the current metafunction.
+ * \param boost_argument metaargument information to be parsed.
+ * \param options some extra options.
+ */
+ void writeArgument(QTextStream &s,
+ const AbstractMetaFunction *boost_function,
+ const AbstractMetaArgument *boost_argument,
+ uint options = 0) const;
+ /**
+ * Create a QString in the boost::python format to an function argument.
+ * \param boost_fuction the current metafunction.
+ * \param boost_argument metaargument information to be parsed.
+ * \param options some extra options.
+ */
+ QString argumentString(const AbstractMetaFunction *boost_function,
+ const AbstractMetaArgument *boost_argument,
+ uint options = 0) const;
+
+ void writeArgumentNames(QTextStream &s,
+ const AbstractMetaFunction *cpp_function,
+ uint options = 0) const;
+
+ /**
+ * Function used to write the fucntion arguments on the class buffer.
+ * \param s the class output buffer
+ * \param boost_function the pointer to metafunction information
+ * \param count the number of function arguments
+ * \param options some extra options used during the parser
+ */
+ void writeFunctionArguments(QTextStream &s,
+ const AbstractMetaFunction *boost_function,
+ uint options = 0) const;
+ QString functionReturnType(const AbstractMetaFunction* func, int option = NoOption);
+ /**
+ * Write a code snip into the buffer \p s.
+ * CodeSnip are codes inside inject-code tags.
+ * \param s the buffer
+ * \param cpp_function the cpp function
+ * \param code_snips a list of code snips
+ * \param position the position to insert the code snip
+ * \param language the kind of code snip
+ */
+ void writeCodeSnips(QTextStream &s,
+ const CodeSnipList &code_snips,
+ CodeSnip::Position position,
+ TypeSystem::Language language,
+ const AbstractMetaFunction *cpp_function = 0);
+ static bool canCreateWrapperFor(const AbstractMetaClass* cppClass);
+ /**
+ * Function witch parse the metafunction information
+ * \param cpp_function the function witch will be parserd
+ * \param option some extra options
+ * \param arg_count the number of function arguments
+ */
+ QString functionSignature(const AbstractMetaFunction *boost_function,
+ QString prepend = "",
+ QString append = "",
+ int option = NoOption,
+ int arg_count = -1);
+
+ QString signatureForDefaultVirtualMethod(const AbstractMetaFunction *cpp_function,
+ QString prepend = "",
+ QString append = "_default",
+ int option = NoOption,
+ int arg_count = -1);
+
+ virtual QString subDirectoryForClass(const AbstractMetaClass* metaClass) const
+ {
+ return subDirectoryForPackage(metaClass->package());
+ }
+
+ QStringList getBaseClasses(const AbstractMetaClass* cppClass);
+
+ static QString getWrapperName(const AbstractMetaClass* clazz);
+
+
+ virtual bool doSetup(const QMap<QString, QString>& args);
+
+protected:
+ // verify if the class is copyalbe
+ bool isCopyable(const AbstractMetaClass *cpp_class);
+
+ void writeFunctionCall(QTextStream &s, const AbstractMetaFunction *cpp_func, uint options = 0);
+};
+
+
+#endif // BOOSTPYTHONGENERATOR_H
+
diff --git a/generators/boostpython/convertergenerator.cpp b/generators/boostpython/convertergenerator.cpp
new file mode 100644
index 000000000..ea52b9193
--- /dev/null
+++ b/generators/boostpython/convertergenerator.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 <QtCore/QDebug>
+#include <fileout.h>
+#include "convertergenerator.h"
+
+static Indentor INDENT;
+
+ConverterGenerator::ConverterGenerator()
+{
+ // QPair
+ m_conversions << qMakePair(QString("QPair<"), &m_qpairTypes);
+ // QList
+ m_conversions << qMakePair(QString("QList<"), &m_qlistTypes);
+ // QVector
+ m_conversions << qMakePair(QString("QVector<"), &m_qvectorTypes);
+ // QMap
+ m_conversions << qMakePair(QString("QMap<"), &m_qmapTypes);
+ // QHash
+ m_conversions << qMakePair(QString("QHash<"), &m_qhashTypes);
+ // QMultiMap
+ m_conversions << qMakePair(QString("QMultiMap<"), &m_qmultiMapTypes);
+
+}
+
+void ConverterGenerator::finishGeneration()
+{
+ if (!classes().size())
+ return;
+
+ QString fileOutPath;
+
+ foreach (AbstractMetaClass *cls, classes()) {
+ if (!shouldGenerate(cls))
+ continue;
+
+ if (fileOutPath.isNull()) {
+ m_packageName = cls->package();
+ fileOutPath = outputDirectory() + '/' + subDirectoryForClass(cls)
+ + "/converter_register_" + moduleName().toLower() + ".hpp";
+ }
+
+ foreach (AbstractMetaFunction* func, filterFunctions(cls))
+ checkFunctionMetaTypes(func);
+ }
+
+ FileOut fileOut(fileOutPath);
+ QTextStream& s = fileOut.stream;
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ s << "#ifndef CONVERTERREGISTER_" << moduleName().toUpper() << "_HPP\n";
+ s << "#define CONVERTERREGISTER_" << moduleName().toUpper() << "_HPP\n\n";
+
+ //Includes
+ QStringList includes;
+ foreach (AbstractMetaClass *cls, classes()) {
+ if (cls->typeEntry()->include().isValid()) {
+ QString include_file = cls->typeEntry()->include().toString();
+ if (!includes.contains(include_file)) {
+ s << include_file << endl;
+ includes << include_file;
+ }
+ }
+
+ if (cls->typeEntry()->generateCode()) {
+ QList<Include> extra_includes = cls->typeEntry()->extraIncludes();
+ foreach (Include include, extra_includes) {
+ if (!includes.contains(include.toString())) {
+ s << include.toString() << endl;
+ includes << include.toString();
+ }
+ }
+ }
+ }
+
+ s << "#include \"type_converter.hpp\"\n\n";
+
+ s << "void register_type_converters_" << moduleName().toLower() << "()\n{\n";
+ Indentation indent(INDENT);
+ writeConverterRegistration(s, "register_qpair_converter", "QPair", m_qpairTypes);
+ writeConverterRegistration(s, "register_container_converter", "QList", m_qlistTypes);
+ writeConverterRegistration(s, "register_container_converter", "QVector", m_qvectorTypes);
+ writeConverterRegistration(s, "register_dict_converter", "QMap", m_qmapTypes);
+ writeConverterRegistration(s, "register_dict_converter", "QHash", m_qhashTypes);
+ writeConverterRegistration(s, "register_multimap_converter", "QMultiMap", m_qmultiMapTypes);
+ s << "}\n\n";
+ s << "#endif\n\n";
+
+ m_numGeneratedWritten = m_qpairTypes.size() + m_qlistTypes.size() +
+ m_qvectorTypes.size() + m_qmapTypes.size() +
+ m_qhashTypes.size();
+}
+
+void ConverterGenerator::writeConverterRegistration(QTextStream& out,
+ const QString& funcName,
+ const QString& type,
+ const QSet<QString>& params)
+{
+ foreach (QString param, params) {
+ QString completeType(QMetaObject::normalizedType(
+ (type + '<' + param + " >").toLatin1().data()));
+ out << INDENT << "PySide::" << funcName;
+ out << '<' << completeType << " >(\"";
+ out << completeType << "\");" << endl;
+ }
+}
+
+void ConverterGenerator::checkFunctionMetaTypes(AbstractMetaFunction* func)
+{
+ if (func->type())
+ checkMetaType(functionReturnType(func));
+
+ foreach (AbstractMetaArgument* arg, func->arguments()) {
+ if (arg->type()) {
+ checkMetaType(argumentString(func, arg,
+ (Generator::SkipName | Generator::SkipDefaultValues)));
+ }
+ }
+}
+
+// FIXME Use some AbstracyMetaAnything info instead of parse the cpp signature?
+void ConverterGenerator::checkMetaType(const QString& cppSignature)
+{
+ QRegExp typeRegex("Q\\w+");
+
+ foreach (Conversion conv, m_conversions) {
+ int index = cppSignature.indexOf(conv.first);
+ if (index >= 0) {
+ QString templateArg = extractTemplateArgument(cppSignature.right(cppSignature.length() - index - conv.first.length()));
+ conv.second->insert(templateArg);
+ // detect types to generate includes
+ int offset = 0;
+ while ((offset = typeRegex.indexIn(templateArg, offset)) != -1) {
+ const QString cap(typeRegex.cap(0));
+ offset += cap.length();
+ }
+ }
+ }
+}
+
+QString ConverterGenerator::extractTemplateArgument(const QString& templateParams)
+{
+ int stack = 0;
+ for (int i = 0; i < templateParams.length(); ++i) {
+ QChar c = templateParams[i];
+ if (c == '<') {
+ stack++;
+ } else if (c == '>') {
+ stack--;
+ if (stack < 0)
+ return templateParams.left(i).trimmed();
+ }
+ }
+ Q_ASSERT(false);
+ return QString();
+}
+
diff --git a/generators/boostpython/convertergenerator.h b/generators/boostpython/convertergenerator.h
new file mode 100644
index 000000000..8f91377c0
--- /dev/null
+++ b/generators/boostpython/convertergenerator.h
@@ -0,0 +1,77 @@
+/*
+ * 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 CONVERTERGENERATOR_H
+#define CONVERTERGENERATOR_H
+
+// #include <QRegExp>
+#include <QtCore/QSet>
+#include "boostpythongenerator.h"
+
+/**
+* Generator for convertions between python collections and Qt collections.
+*
+* It generates a file called converter_register_MODULENAME.hpp with only one
+* function called register_type_converters_MODULENAME, where MODULENAME is the current module name.
+* QPair are converted to python tuples, QList, QVector and QLinkedList to python lists, QHash and QMap to python dicts.
+*/
+class ConverterGenerator : public BoostPythonGenerator
+{
+public:
+ ConverterGenerator();
+
+ const char* name() const
+ {
+ return "ConverterGenerator";
+ }
+
+protected:
+ void generateClass(QTextStream& s, const AbstractMetaClass* clazz)
+ {
+ }
+
+ void finishGeneration();
+ QString fileNameForClass(const AbstractMetaClass* cppClass) const
+ {
+ return QString();
+ }
+private:
+ void checkFunctionMetaTypes(AbstractMetaFunction* func);
+ void checkMetaType(const QString& cppSignature);
+ QString extractTemplateArgument(const QString& templateParams);
+
+ void writeConverterRegistration(QTextStream& out, const QString& func_name, const QString& type, const QSet<QString>& params);
+
+ typedef QPair<QString, QSet<QString>* > Conversion;
+ typedef QList<Conversion> ConversionList;
+ ConversionList m_conversions;
+ QSet<QString> m_qpairTypes;
+ QSet<QString> m_qlistTypes;
+ QSet<QString> m_qvectorTypes;
+ QSet<QString> m_qmapTypes;
+ QSet<QString> m_qhashTypes;
+ QSet<QString> m_qmultiMapTypes;
+};
+
+#endif // CONVERSIONGENERATOR_H
+
diff --git a/generators/boostpython/cppgenerator.cpp b/generators/boostpython/cppgenerator.cpp
new file mode 100644
index 000000000..ca4b704ff
--- /dev/null
+++ b/generators/boostpython/cppgenerator.cpp
@@ -0,0 +1,1439 @@
+/*
+ * 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 "cppgenerator.h"
+#include <apiextractor/reporthandler.h>
+#include <apiextractor/fileout.h>
+#include <apiextractor/abstractmetalang.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QVariant>
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+#include <QtCore/QListIterator>
+
+static Indentor INDENT;
+
+// utiliy functions
+inline QString getMethodPointerString(const AbstractMetaFunction* func)
+{
+ QString className;
+ if (!func->declaringClass()->isAbstract())
+ className = func->declaringClass()->qualifiedCppName();
+ else
+ className = func->ownerClass()->qualifiedCppName();
+
+ return '&' + className + "::" + func->originalName();
+}
+
+static QString nameForModifiedCtorFunction(const AbstractMetaFunction* func) {
+ QString res = func->ownerClass()->name().toLower().replace("::", "_");
+ res += "_constructor";
+ foreach (AbstractMetaArgument* arg, func->arguments()) {
+ res += '_';
+ res += arg->type()->name().toLower();
+ }
+ return res;
+}
+
+static QString createStaticFunctionName(const AbstractMetaFunction* func)
+{
+ QString funcName;
+ QString originalName(func->name());
+
+
+ funcName = func->ownerClass()->name().toLower();
+
+ //remove initial 'Q'
+ if (funcName.startsWith('q'))
+ funcName = funcName.remove(0, 1);
+
+ //upercase first letter
+ funcName += originalName[0].toUpper() + originalName.mid(1);
+
+ return funcName;
+}
+
+QString CppGenerator::fileNameForClass(const AbstractMetaClass* cppClass) const
+{
+ return getWrapperName(cppClass) + QLatin1String(".cpp");
+}
+
+QString CppGenerator::getFuncTypedefName(const AbstractMetaFunction* func) const
+{
+ return func->name() + QLatin1String("_type");
+}
+
+void CppGenerator::writeConstructorInitialization(QTextStream &s, const AbstractMetaFunction *function)
+{
+ QStringList nonOpts;
+ QStringList opts;
+
+ foreach (AbstractMetaArgument *arg, function->arguments()) {
+ uint options = SkipName | SkipDefaultValues;
+ QString argType = argumentString(function, arg, options);
+ if (arg->defaultValueExpression().isEmpty())
+ nonOpts << argType;
+ else
+ opts << argType;
+ }
+
+ bool hasModifications = function->allowThread() || function->hasInjectedCode();
+
+ if (hasModifications) {
+ s << "\"__init__\", python::make_constructor("
+ << nameForModifiedCtorFunction(function);
+ } else {
+ s << "python::init< ";
+
+ if (nonOpts.size() > 0)
+ s << nonOpts.join(", ");
+
+ if (opts.size() > 0) {
+ if (nonOpts.size() > 0)
+ s << ", ";
+
+ s << "python::optional< " << opts.join(",") << " > ";
+ }
+
+ s << " > ()";
+ }
+
+ QString callPolicy = getFunctionCallPolicy(function);
+ QString parentType;
+ const AbstractMetaClass *cppClass = function->ownerClass();
+ uint closePolicy = 0;
+ bool hasPolicy = false;
+
+ if (
+ !hasModifications &&
+ (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor() || cppClass->isNamespace())
+ ) {
+ closePolicy++;
+ hasPolicy = true;
+ s << "[ PySide::register_wrapper_object< "
+ << function->ownerClass()->qualifiedCppName();
+ }
+
+ if (callPolicy.isEmpty()) {
+ int parentIndex = -1;
+ //try find for parent arg to create callPolicy
+ foreach (AbstractMetaArgument *arg, function->arguments()) {
+ if (arg->argumentName() == "parent") {
+ parentIndex = arg->argumentIndex();
+ parentType = translateType(arg->type(), function->ownerClass(),
+ Generator::ExcludeConst | Generator::ExcludeReference).replace("*", "");
+ break;
+ }
+ }
+ if (parentIndex != -1) {
+ if (!closePolicy)
+ s << (hasModifications ? ", " : "[ ");
+ else
+ s << ", ";
+
+ s << "parent_policy_add< " << parentIndex + 2 << ", 1, "
+ << parentType << " , " << function->ownerClass()->qualifiedCppName();
+
+ hasPolicy = true;
+ closePolicy++;
+ }
+ } else {
+ if (!closePolicy)
+ s << (hasModifications ? ", " : "[ ");
+ else
+ s << ", ";
+
+ if (callPolicy.endsWith("()"))
+ callPolicy = callPolicy.remove(callPolicy.size() - 2, 2);
+
+ s << callPolicy;
+ hasPolicy = true;
+ }
+
+ while(closePolicy) {
+ s << " > ";
+ closePolicy--;
+ }
+
+ if (hasModifications)
+ s << ')';
+ else if (hasPolicy)
+ s << "() ]";
+}
+
+QString CppGenerator::getFunctionReturnType(const AbstractMetaFunction* func)
+{
+ QString modifiedReturnType = QString(func->typeReplaced(0));
+
+ return modifiedReturnType.isNull() ? translateType(func->type(), func->implementingClass()) : modifiedReturnType;
+}
+
+QString CppGenerator::writeFunctionCast(QTextStream &s,
+ const AbstractMetaFunction* func,
+ const QString& castNameSuffix,
+ const QString& className)
+{
+ QString castName = getFuncTypedefName(func) + castNameSuffix;
+ const AbstractMetaClass* cppClass = func->ownerClass();
+ bool isWrapped = !func->isVirtual() &&
+ (func->hasInjectedCode() || func->isThread() || func->allowThread());
+ bool isVirtualMethodDefault = castNameSuffix == "_default";
+
+ s << INDENT << "typedef ";
+ s << getFunctionReturnType(func);
+ s << " (";
+ if (cppClass && !func->isStatic() && func->ownerClass() && !isVirtualMethodDefault) {
+ if (!isWrapped) {
+ // pointer to a class method
+ if (!className.isEmpty())
+ s << className;
+ else if (func->isVirtual() && !func->declaringClass()->isAbstract())
+ s << func->declaringClass()->qualifiedCppName();
+ else
+ s << cppClass->qualifiedCppName();
+
+ s << "::";
+ }
+ }
+
+ s << '*' << castName << ") (";
+ if (isVirtualMethodDefault) {
+ if (func->isConstant())
+ s << "const ";
+
+ s << func->implementingClass()->qualifiedCppName() << "&";
+ if (func->arguments().size() > 0)
+ s << ", ";
+ }
+ int options = SkipName | SkipDefaultValues | SkipRemovedArguments;
+ if (isWrapped && !func->isStatic())
+ options |= WriteSelf;
+
+ writeFunctionArguments(s, func, options);
+ s << ')';
+
+ if (func->isConstant() && !isWrapped && !isVirtualMethodDefault)
+ s << " const";
+
+ s << ';' << endl;
+
+ return castName;
+}
+
+QString CppGenerator::verifyDefaultReturnPolicy(const AbstractMetaFunction *cppFunction, const QString& callPolicy)
+{
+ AbstractMetaType *type = cppFunction->type();
+
+ //If return type replaced, the return policy need be set manually.
+ if (!type || !cppFunction->typeReplaced(0).isEmpty())
+ return QString();
+
+ //avoid natives types
+ if (!type->name().startsWith("Q"))
+ return QString();
+
+ QString returnPolicy;
+
+ if (type->isConstant() && type->isReference()) {
+ returnPolicy = "python::return_value_policy<python::copy_const_reference";
+ if (!callPolicy.isEmpty())
+ returnPolicy += ", " + callPolicy;
+ returnPolicy += " >()";
+ } else if (type->isReference() || type->isQObject() || type->isObject() || type->isNativePointer()) {
+ bool cppOwnership = type->isConstant();
+ if (cppFunction->isStatic() || cppOwnership) {
+ returnPolicy = QString("python::return_value_policy<PySide::return_ptr_object<")
+ + (cppOwnership ? "true" : "false") + QString("> >()");
+ } else if (type->isQObject() || type->isObject()) {
+ returnPolicy = QString("PySide::return_object<1, 0, %1, %2 %3 %4 >()")
+ .arg(getArgumentType(cppFunction->ownerClass(), cppFunction, -1))
+ .arg(getArgumentType(cppFunction->ownerClass(), cppFunction, 0))
+ .arg(callPolicy.isEmpty() ? "" : ",")
+ .arg(callPolicy);
+ } else {
+ returnPolicy = QString("python::return_internal_reference<%1 %2>()")
+ .arg(callPolicy.isEmpty() ? "" : ",")
+ .arg(callPolicy);
+ }
+ } else if (!callPolicy.isEmpty()) {
+ returnPolicy = callPolicy + "()";
+ }
+
+ return returnPolicy;
+}
+
+static int boost_parent_policy_index(int i, const AbstractMetaFunction* func = 0)
+{
+ if (func && func->isStatic())
+ return i;
+
+ if (i == -1)
+ return 1;
+ else if (i > 0)
+ return i + 1;
+ else
+ return i;
+}
+
+QString CppGenerator::getArgumentType(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func, int idx)
+{
+ QString retval;
+ if (idx == -1) {
+ retval = cppClass->qualifiedCppName();
+ } else if (idx == 0 && func->type()) {
+ retval = translateType(func->type(), cppClass,
+ Generator::ExcludeConst | Generator::ExcludeReference);
+ } else if (idx > 0) {
+ retval = argumentString(func, func->arguments()[idx-1],
+ Generator::SkipDefaultValues | Generator::ExcludeConst |
+ Generator::ExcludeReference | Generator::SkipName);
+ }
+
+ retval = retval.trimmed();
+ if (retval.endsWith('*'))
+ retval.chop(1);
+ return retval;
+}
+
+QString CppGenerator::getFunctionCallPolicy(const AbstractMetaFunction *func)
+{
+ QString callPolicy;
+ QStringList callPolicies;
+ bool returnChild = false;
+ const AbstractMetaClass* cppClass = func->implementingClass();
+
+ const int numArgs = func->arguments().count();
+
+ for (int i = -1; i <= numArgs; ++i) {
+ ArgumentOwner ao = func->argumentOwner(cppClass, i);
+ //Parent Policy
+ if ((ao.index != -2) && (ao.index != i)) {
+ switch (ao.action) {
+ case ArgumentOwner::Add:
+ if (!i) {
+ callPolicy = "return_object<";
+ returnChild = true;
+ } else {
+ callPolicy = "parent_policy_add<";
+ }
+ break;
+ case ArgumentOwner::Remove:
+ callPolicy = "parent_policy_remove<";
+ break;
+ default:
+ continue;
+ }
+
+ callPolicy += QString("%1, %2, %3, %4")
+ .arg(boost_parent_policy_index(ao.index, func))
+ .arg(boost_parent_policy_index(i, func))
+ .arg(getArgumentType(cppClass, func, ao.index))
+ .arg(getArgumentType(cppClass, func, i));
+
+ callPolicies << callPolicy;
+ } else if (i) { //only function args ignore return value
+ //Ownership policy
+ bool changeOwnership = false;
+ bool releaseOwnership = false;
+ TypeSystem::Ownership owner = func->ownership(cppClass,
+ TypeSystem::TargetLangCode, i);
+
+ switch(owner)
+ {
+ case TypeSystem::CppOwnership:
+ releaseOwnership = true;
+ case TypeSystem::TargetLangOwnership:
+ changeOwnership = true;
+ break;
+ default:
+ changeOwnership = false;
+ }
+
+ if (changeOwnership)
+ {
+ QString ownershipPolicy = QString("transfer_ownership<%1, %2, %3")
+ .arg(boost_parent_policy_index(i, func))
+ .arg(releaseOwnership ? "true" : "false")
+ .arg(getArgumentType(cppClass, func, i));
+ callPolicies << ownershipPolicy;
+ }
+ }
+ }
+
+ if (callPolicies.size() > 0) {
+ callPolicy = callPolicies.join(", ");
+ for (int i = 0; i < callPolicies.count(); ++i)
+ callPolicy += " >";
+ }
+
+ QString returnPolicy;
+
+ //return value
+ bool cppOwnership = false;
+
+ if (!returnChild) {
+ switch (func->ownership(cppClass, TypeSystem::TargetLangCode, 0))
+ {
+ case TypeSystem::CppOwnership:
+ cppOwnership = true;
+ case TypeSystem::TargetLangOwnership:
+ {
+ QString cppOwnershipFlag = (cppOwnership ? "true" : "false");
+ returnPolicy = "python::return_value_policy< PySide::return_ptr_object<" + cppOwnershipFlag + "> ";
+ if (!callPolicy.isEmpty())
+ returnPolicy += ", " + callPolicy;
+ returnPolicy += " >()";
+ break;
+ }
+ default:
+ returnPolicy = verifyDefaultReturnPolicy(func, callPolicy);
+ break;
+ }
+ }
+
+ //return policy
+ if (func->shouldReturnThisObject())
+ return "python::return_self< " + callPolicy + " >()";
+ else if (!returnPolicy.isEmpty())
+ return returnPolicy;
+ else if (!callPolicy.isEmpty())
+ return callPolicy + "()";
+
+ return QString();
+}
+
+/*!\internal
+ Function used to write the enum boost code on the buffer
+ \param s the output buffer
+ \param cpp_enum the pointer to metaenum information to be translated to boost
+*/
+void CppGenerator::writeEnum(QTextStream &s,
+ const AbstractMetaEnum *cppEnum,
+ const QString &nameSpace)
+{
+ s << INDENT << "python::enum_<" << nameSpace << cppEnum->name();
+ s << ">(\"" << cppEnum->name() << "\")" << endl;
+ const AbstractMetaEnumValueList &values = cppEnum->values();
+ EnumTypeEntry *ete = cppEnum->typeEntry();
+
+ foreach (const AbstractMetaEnumValue* enumValue, values) {
+ Indentation indent(INDENT);
+ if (ete->isEnumValueRejected(enumValue->name()))
+ continue;
+
+ s << INDENT << ".value(\"" << enumValue->name() << "\", ";
+ s << nameSpace << enumValue->name() << ")" << endl;
+ }
+
+ //Export values to current scope
+ s << INDENT << INDENT << ".export_values()" << endl;
+ s << INDENT << ";" << endl << endl;
+
+ FlagsTypeEntry* flagsEntry = cppEnum->typeEntry()->flags();
+
+ if (flagsEntry) {
+ s << INDENT << "PySide::declare_" << (cppEnum->typeEntry()->forceInteger() ? "int_" : "") << "qflags< "
+ << flagsEntry->originalName() << " >(\"" << flagsEntry->flagsName() << "\");" << endl;
+ }
+
+ //register enum in typemanager
+ s << INDENT
+ << "type_manager::instance().register_native_type<int>(\""
+ << cppEnum->qualifier() << "::" << cppEnum->name() << "\");\n\n";
+}
+
+void CppGenerator::writeEnums(QTextStream &s, const AbstractMetaClass *cppClass, bool useNamespace)
+{
+ AbstractMetaEnumList enums = cppClass->enums();
+ if (!enums.size())
+ return;
+
+ s << INDENT << "// Enums" << endl;
+ QString name_space;
+ if (useNamespace || !cppClass->isPolymorphic() || cppClass->hasPrivateDestructor())
+ name_space = cppClass->qualifiedCppName() + "::";
+
+ foreach (AbstractMetaEnum *cpp_enum, enums)
+ writeEnum(s, cpp_enum, name_space);
+}
+
+void CppGenerator::writeImplicitlyConversion(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+#if 0
+ if (cppClass->isNamespace())
+ return;
+ s << endl << "// Implicitly conversions" << endl;
+ QStringList interfaces = getBaseClasses(cppClass);
+
+ if (!interfaces.size()) {
+ s << INDENT << "python::implicitly_convertible< " << endl;
+ s << INDENT << INDENT << "std::auto_ptr< " << getWrapperName(cppClass->name()) << " >," << endl;
+ s << INDENT << INDENT << "std::auto_ptr< " << cppClass->name() << " > >();" << endl;
+ } else {
+ foreach (QString base_class, interfaces) {
+ s << INDENT << "python::implicitly_convertible< " << endl;
+ s << INDENT << INDENT << "std::auto_ptr< " << cppClass->name() << " >," << endl;
+ s << INDENT << INDENT << "std::auto_ptr< " << base_class << " > >();" << endl;
+ }
+ }
+#endif
+}
+
+
+void CppGenerator::writeDestructor(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+ Indentation indentation(INDENT);
+ QString wrapperName = getWrapperName(cppClass);
+ s << wrapperName << "::~" << wrapperName << "()" << endl << "{" << endl
+ << INDENT << "PySide::qptr_base::invalidate(this);" << endl << "}" << endl;
+}
+
+/*!
+ Function used to write the class generated boost code on the buffer
+ \param s the output buffer
+ \param cppClass the pointer to metaclass information
+*/
+void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+ ReportHandler::debugSparse("Generating wrapper implementation for " + cppClass->fullName());
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ QString localStr, globalStr;
+ QTextStream includesLocal(&localStr);
+ QTextStream includesGlobal(&globalStr);
+
+ bool canCreateWrapper = canCreateWrapperFor(cppClass);
+
+ QList<Include> includes = cppClass->typeEntry()->extraIncludes();
+ qSort(includes.begin(), includes.end());
+
+ foreach (Include inc, includes) {
+ if (inc.type == Include::IncludePath)
+ includesGlobal << inc.toString() << endl;
+ else
+ includesLocal << inc.toString() << endl;
+ }
+
+ //workaround to access protected functions
+ s << "//workaround to access protected functions" << endl;
+ s << "#define protected public" << endl;
+
+ s << "//Base Class" << endl;
+ if (cppClass->typeEntry()->include().isValid())
+ s << cppClass->typeEntry()->include().toString() << endl << endl;
+
+ s << "//Extra includes [global]" << endl;
+ s << globalStr << endl;
+
+ s << "#undef protected" << endl;
+ s << "//Base include" << endl;
+ s << "#include \"pyside.hpp\"" << endl;
+ s << "#include \"" << getWrapperName(cppClass) << ".hpp\"" << endl;
+ foreach (AbstractMetaClass* innerClass, cppClass->innerClasses()) {
+ if (shouldGenerate(innerClass))
+ s << "#include \"" << getWrapperName(innerClass) << ".hpp\"" << endl;
+ }
+ s << endl << "//Extra includes [local]" << endl;
+ s << localStr << endl;
+
+ s << endl << "using namespace boost;" << endl;
+ s << "using namespace PySide;" << endl;
+ s << endl;
+
+ if (cppClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated)
+ s << "#Deprecated" << endl;
+
+ if (canCreateWrapper) {
+ writePrelude(s, cppClass);
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor())
+ writeDestructor(s, cppClass);
+ }
+ writeBoostDeclaration(s, cppClass);
+}
+
+void CppGenerator::writePrelude(QTextStream& s, const AbstractMetaClass* cppClass)
+{
+ //inject code native beginner
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::Beginning, TypeSystem::NativeCode);
+
+ foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) {
+ if ((func->isPrivate() || func->isModifiedRemoved()) && !func->isAbstract())
+ continue;
+
+ if (func->isConstructor() && (func->allowThread() || func->hasInjectedCode())) {
+ writeModifiedConstructorImpl(s, func);
+ } else if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor() &&
+ func->isConstructor() && !func->isCopyConstructor()) {
+ writeConstructorImpl(s, func);
+ } else if (func->isVirtual() || func->isAbstract()) {
+ writeVirtualMethodImpl(s, func);
+ } else if (func->hasInjectedCode() || func->isThread() || func->allowThread()) {
+ writeNonVirtualModifiedFunctionImpl(s, func);
+ } else if (func->isInGlobalScope() && func->isOperatorOverload()) {
+ writeGlobalOperatorOverloadImpl(s, func);
+ }
+ }
+
+ //inject code native end
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::End, TypeSystem::NativeCode);
+}
+
+
+void CppGenerator::writeModifiedConstructorImpl ( QTextStream& s, const AbstractMetaFunction* func )
+{
+ Indentation indentation(INDENT);
+ const AbstractMetaClass* clazz = func->ownerClass();
+ s << "static " << clazz->name() << "* " << nameForModifiedCtorFunction(func) << '(';
+ writeFunctionArguments(s, func, SkipDefaultValues);
+ s << ")\n{" << endl;
+
+ s << INDENT << clazz->name() << "* _self = 0;" << endl;
+ s << INDENT << '{' << endl;
+ {
+ Indentation indentation(INDENT);
+ if (func->allowThread())
+ s << INDENT << "py_allow_threads allow_threads;" << endl;
+
+ s << INDENT << "_self = new ";
+ writeFunctionCall(s, func);
+ s << ';' << endl;
+ }
+ s << INDENT << '}' << endl;
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func);
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+ s << INDENT << "python::object _obj(PySide::ptr(_self));" << endl;
+ s << INDENT << "return _self;" << endl;
+ s << '}' << endl;
+}
+
+void CppGenerator::writeConstructorImpl(QTextStream& s, const AbstractMetaFunction* func)
+{
+ QString wrapperName = getWrapperName(func->ownerClass());
+ s << wrapperName << "::" << wrapperName << "(PyObject *py_self" << (func->arguments().size() ? ", " : "");
+ writeFunctionArguments(s, func, OriginalTypeDescription | SkipDefaultValues);
+ s << ")" << endl;
+ s << INDENT << " : ";
+ writeFunctionCall(s, func);
+ s << ", wrapper(py_self)" << endl << "{" << endl;
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func);
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+ s << '}' << endl << endl;
+}
+
+void CppGenerator::writeVirtualMethodImplHead(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+ s << INDENT << "thread_locker lock;" << endl;
+
+ if (func->hasInjectedCode()) {
+ writeCodeSnips(s, getCodeSnips(func),
+ CodeSnip::Beginning, TypeSystem::NativeCode, func);
+ }
+
+ s << INDENT << "python::object method = get_override(\"" << func->implementingClass()->name();
+ if (func->implementingClass()->typeEntry()->isObject() || func->implementingClass()->typeEntry()->isQObject())
+ s << '*';
+
+ s << "\", \"" << func->name() << "\");" << endl
+ << INDENT << "if (method)" << endl << INDENT << "{" << endl;
+
+ {
+ Indentation indentation(INDENT);
+ s << INDENT;
+ if (func->type())
+ s << "python::object __result = ";
+
+ s << "method(";
+ writeArgumentNames(s, func, BoxedPrimitive);
+ s << ");" << endl;
+
+ QString typeName = getFunctionReturnType(func);
+ if (!typeName.isEmpty()) {
+
+ CodeSnipList codeSnips = getCodeSnips(func);
+ bool hasVirtualBeginningCode = false;
+ foreach(CodeSnip cs, codeSnips) {
+ if ((cs.position == CodeSnip::Beginning) && (cs.language == TypeSystem::TargetLangCode)) {
+ hasVirtualBeginningCode = true;
+ break;
+ }
+ }
+
+ if (hasVirtualBeginningCode) {
+ writeCodeSnips(s, codeSnips, CodeSnip::Beginning, TypeSystem::TargetLangCode, func);
+ } else if (func->type()) {
+ s << INDENT << typeName << " __return_value = " << "python::extract<" << typeName << " >(__result);" << endl;
+ bool boxedPointer = false;
+ if (func->type() && !func->type()->isConstant() &&
+ (func->type()->isObject() || func->type()->isQObject())) {
+
+ s << INDENT << "PySide::qptr<" << QString(typeName).replace("*", "") << " > __ptr(__result.ptr());" << endl
+ << INDENT << "python::incref(__result.ptr());" << endl
+ << INDENT << "__ptr.release_ownership();" << endl;
+ }
+
+ s << INDENT << "return __return_value;" << endl;
+ }
+ }
+ }
+ s << INDENT << "}" << endl;
+}
+
+void CppGenerator::writeVirtualMethodImpl(QTextStream& s, const AbstractMetaFunction* func)
+{
+ if (func->isModifiedRemoved())
+ return;
+
+ if (!func->isAbstract() && !func->ownerClass()->hasPrivateDestructor() &&
+ func->implementingClass() == func->ownerClass()) {
+ writeVirtualDefaultFunction(s, func);
+ }
+
+
+ QString prefix = getWrapperName(func->ownerClass()) + "::";
+ s << functionSignature(func, prefix, "",
+ Generator::OriginalTypeDescription | Generator::SkipDefaultValues)
+ << endl << "{" << endl;
+
+ writeVirtualMethodImplHead(s, func);
+
+ if (func->isAbstract())
+ writePureVirtualMethodImplFoot(s, func);
+ else
+ writeVirtualMethodImplFoot(s, func);
+
+ s << '}' << endl << endl;
+}
+
+
+void CppGenerator::writePureVirtualMethodImplFoot(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+ s << INDENT << "else" << endl
+ << INDENT << "{" << endl;
+ {
+ Indentation indentation(INDENT);
+ s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \""
+ << func->ownerClass()->name() << "." << func->name() << " : "
+ << "You need to implement pure virtual functions in python\");" << endl
+ << INDENT << "throw python::error_already_set();" << endl;
+ }
+ s << INDENT << "}" << endl;
+}
+
+void CppGenerator::writeVirtualMethodImplFoot(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+ s << INDENT << "else" << endl << INDENT << "{" << endl;
+ {
+ Indentation indentation(INDENT);
+ QString returnKeyword = func->type() ? QLatin1String("return ") : QString();
+
+ if (func->allowThread())
+ s << INDENT << "py_allow_threads allow_threads;" << endl;
+
+ s << INDENT << returnKeyword << func->implementingClass()->qualifiedCppName() << "::";
+ writeFunctionCall(s, func);
+ s << ';' << endl;
+ }
+ s << INDENT << '}' << endl;
+}
+
+void CppGenerator::writeVirtualDefaultFunction(QTextStream &s, const AbstractMetaFunction *func)
+{
+ Indentation indentation(INDENT);
+ QString returnKeyword = func->type() ? QLatin1String("return ") : QString();
+ QString defaultMethodSignature = signatureForDefaultVirtualMethod(func, getWrapperName(func->ownerClass()) + "::", "_default", Generator::SkipDefaultValues);
+ s << defaultMethodSignature << endl << '{' << endl;
+
+ if (func->allowThread())
+ s << INDENT << "py_allow_threads allow_threads;" << endl;
+
+ CodeSnipList codeSnips = getCodeSnips(func);
+ bool hasVirtualEndCode = false;
+ foreach(CodeSnip cs, codeSnips) {
+ if ((cs.position == CodeSnip::End) && (cs.language == TypeSystem::TargetLangCode)) {
+ hasVirtualEndCode = true;
+ break;
+ }
+ }
+
+ if (!hasVirtualEndCode) {
+ s << INDENT << returnKeyword << "self." << func->implementingClass()->qualifiedCppName() << "::";
+ writeFunctionCall(s, func);
+ s << ";" << endl;
+ } else {
+ writeCodeSnips(s, getCodeSnips(func),
+ CodeSnip::End, TypeSystem::TargetLangCode, func);
+ }
+ s << '}' << endl << endl;
+
+}
+
+
+
+void CppGenerator::writeNonVirtualModifiedFunctionImpl(QTextStream& s, const AbstractMetaFunction* func)
+{
+ Indentation indentation(INDENT);
+
+ s << "static " << getFunctionReturnType(func) << ' ';
+ s << func->ownerClass()->name() << '_' << func->originalName() << "_modified(";
+
+ uint options = SkipRemovedArguments | SkipDefaultValues;
+ if (!func->isStatic())
+ options |= WriteSelf;
+
+ writeFunctionArguments(s, func, options);
+ s << ")" << endl << "{" << endl;
+
+ if (func->isThread())
+ s << INDENT << "thread_locker lock;" << endl;
+
+ if (func->allowThread())
+ s << INDENT << "py_allow_threads allow_threads;" << endl;
+
+ if (getCodeSnips(func).size() > 0) {
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func);
+ writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func);
+ } else {
+ s << INDENT;
+ if (func->type())
+ s << "return ";
+
+ if (func->isStatic())
+ s << func->declaringClass()->name() << "::";
+ else
+ s << "self.";
+
+ writeFunctionCall(s, func);
+ s << ";" << endl;
+ }
+
+ s << '}' << endl << endl;
+}
+
+AbstractMetaFunction* CppGenerator::findMainConstructor(const AbstractMetaClass* clazz)
+{
+ foreach (AbstractMetaFunction* func, clazz->functions()) {
+ if (func->isConstructor() &&
+ func->isPublic() &&
+ !func->isModifiedRemoved() &&
+ !func->isPrivate()) {
+ return func;
+ }
+ }
+ return 0;
+}
+
+void CppGenerator::writeHashFunction(QTextStream& s, const AbstractMetaClass* cppClass)
+{
+ QString argType;
+
+ //WORKAROUND: diferent way to QChar
+ if (cppClass->name() == "QChar")
+ argType = "QChar";
+ else
+ argType = "const " + cppClass->name() + "&";
+
+ s << "// Hash function" << endl
+ << "{" << endl
+ << INDENT << INDENT << "typedef uint (*hash_type) ( " << argType << " );"
+ << INDENT << INDENT << "python_cls.def(\"__hash__\", hash_type(&"
+ << cppClass->typeEntry()->hashFunction() << "));" << endl
+ << "}" << endl;
+}
+
+QString CppGenerator::baseClassName(const QString& name)
+{
+ QStringList lst = name.split("::");
+ return lst.last();
+}
+
+void CppGenerator::writeBoostDeclaration(QTextStream& s, const AbstractMetaClass* cppClass)
+{
+ Indentation indent(INDENT);
+ QString wrapperName = getWrapperName(cppClass);
+
+ s << "void " << wrapperName << "::define_python_class() throw() {" << endl;
+
+ const AbstractMetaFunction* mainCtor = 0;
+ bool mainCtorHasModifications = false;
+ if (!cppClass->isNamespace()) {
+ // python_cls declaration
+ mainCtor = findMainConstructor(cppClass);
+ if (mainCtor)
+ mainCtorHasModifications = mainCtor->allowThread() || mainCtor->hasInjectedCode();
+
+ s << INDENT;
+ if (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor())
+ s << wrapperName << "::";
+
+ s << "class_type python_cls(\""
+ << baseClassName(cppClass->name()) << "\", ";
+
+ if (!mainCtor || mainCtorHasModifications)
+ s << "python::no_init";
+ else
+ writeConstructorInitialization(s, mainCtor);
+
+ s << ");" << endl << endl;
+ } else {
+ QRegExp reg("(?:\\w+::)*(\\w+)");
+ reg.indexIn(cppClass->name());
+ s << INDENT << "python::class_<Namespace> python_cls(\"" << reg.cap(1) << "\");" << endl;
+ }
+ // scope declaration
+ s << INDENT << "python::scope " << wrapperName << "_scope(python_cls);" << endl;
+
+ if (cppClass->templateBaseClass() && cppClass->templateBaseClass()->typeEntry()->isContainer()) {
+ s << endl << INDENT << "//Index suite for QContainer" << endl
+ << INDENT << "python_cls.def(qcontainer_indexing_suite< " << cppClass->qualifiedCppName() << " >());" << endl << endl;
+ }
+
+ if (isCopyable(cppClass) && !cppClass->isNamespace()) {
+ s << INDENT << "python_cls.def(python::init<const ";
+ s << cppClass->qualifiedCppName() << "&>());" << endl;
+ }
+
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor() && canCreateWrapperFor(cppClass)) {
+ QString heldType = cppClass->typeEntry()->heldTypeValue();
+ if (heldType.isEmpty())
+ heldType = "PySide::qptr";
+
+ s << INDENT << "python::implicitly_convertible< "
+ << heldType << "<" << wrapperName << ">, "
+ << heldType << "<" << cppClass->qualifiedCppName() << "> >();" << endl;
+ }
+
+ //Enums
+ writeEnums(s, cppClass, cppClass->hasPrivateDestructor() || cppClass->isNamespace());
+
+ if (cppClass->innerClasses().count()) {
+ s << endl << INDENT << "// Inner classes" << endl;
+ foreach (AbstractMetaClass* innerClass, cppClass->innerClasses()) {
+ if (!innerClass->typeEntry()->generateCode())
+ continue;
+ s << INDENT << getWrapperName(innerClass) << "::define_python_class();" << endl;
+ }
+ }
+
+ //Fields
+ foreach (AbstractMetaField *field, cppClass->fields()) {
+ QString strAccess;
+
+ if (field->isPublic()) {
+ if (field->type()->isConstant())
+ strAccess = "def_readonly";
+ else
+ strAccess = "def_readwrite";
+
+ s << INDENT << "python_cls."
+ << strAccess
+ << "(\"" << field->name() << "\", &"
+ << field->enclosingClass()->typeEntry()->qualifiedCppName() << "::" << field->name() << ");" << endl;
+ }
+ }
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::Beginning, TypeSystem::TargetLangCode);
+
+ QSet<QString> staticMethods;
+
+ if (!cppClass->isNamespace()) {
+ //search for all static methods to match with normal functions
+ //to rename when match with one member function
+ foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) {
+ if (func->isStatic() && !func->isOperatorOverload())
+ staticMethods << func->name();
+ }
+ }
+
+ foreach (AbstractMetaFunction *func, filterFunctions(cppClass)) {
+ if (func->isModifiedRemoved() || func->isPrivate() || func->isSignal())
+ continue;
+
+ //rename static function when is the same name as member function
+ if (!cppClass->isNamespace() && func->isStatic()) {
+ QString staticName(createStaticFunctionName(func));
+ QSet<QString>::iterator staticFuncInter = staticMethods.find(staticName);
+ if (staticFuncInter != staticMethods.end())
+ func->setName(staticName);
+ } else {
+ QSet<QString>::iterator staticFuncInter = staticMethods.find(func->name());
+ if (staticFuncInter != staticMethods.end()) {
+ staticMethods.erase(staticFuncInter);
+ staticMethods << createStaticFunctionName(func);
+ }
+ }
+
+ if (func->isOperatorOverload()) {
+ // Do not join the ifs -- isOperatorOverload must be checked alone
+ if (func->originalName() == func->name())
+ writeOperatorOverload(s, func);
+ } else if (func->isConstructor()) {
+ if (mainCtorHasModifications || func != mainCtor)
+ writeConstructor(s, func);
+ } else if (!func->isVirtual() &&
+ (func->hasInjectedCode() ||
+ func->isThread() || func->allowThread())) {
+ writeModifiedMethodDef(s, func);
+ } else if (func->implementingClass() == func->ownerClass()) {
+ writeNormalMethodDef(s, func);
+ }
+
+ //if is namespace all methothds is stattic
+ if (cppClass->isNamespace())
+ s << INDENT << "python_cls.staticmethod(\"" << func->name() << "\");" << endl;
+ }
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::End, TypeSystem::TargetLangCode);
+
+ if (!cppClass->isNamespace()) {
+ // Static methods
+ if (!staticMethods.isEmpty())
+ s << INDENT << "// Static methods" << endl;
+
+ foreach (QString funcName, staticMethods)
+ s << INDENT << "python_cls.staticmethod(\"" << funcName << "\");" << endl;
+ }
+
+ // qHash usage
+ if (!cppClass->typeEntry()->hashFunction().isEmpty())
+ writeHashFunction(s, cppClass);
+
+ // implicity conversions
+ writeImplicitlyConversion(s, cppClass);
+
+ // register object/value type
+ if (!cppClass->isNamespace()) {
+ QString className = cppClass->qualifiedCppName();
+ const char* funcName = (cppClass->typeEntry()->isObject() || !isCopyable(cppClass)) ? "object" : "value";
+ s << INDENT
+ << "type_manager::instance().register_"
+ << funcName
+ << "_type<" << className << " >(\""
+ << cppClass->qualifiedCppName() << (cppClass->typeEntry()->isObject() ? "*" : "") << "\");\n";
+ }
+ s << '}' << endl;
+}
+
+void CppGenerator::writeConstructor(QTextStream& s, const AbstractMetaFunction* func)
+{
+ s << INDENT << "python_cls.def(";
+ writeConstructorInitialization(s, func);
+ s << ");" << endl;
+}
+
+void CppGenerator::writeFunctionArgsDef(QTextStream &sOut,
+ const AbstractMetaFunction *cppFunction)
+{
+ bool hasDefaultValue = false;
+ int argUsed = 0;
+ QString aux;
+ QTextStream s(&aux);
+
+ foreach (const AbstractMetaArgument *arg, cppFunction->arguments()) {
+ if (cppFunction->argumentRemoved(arg->argumentIndex() + 1))
+ continue;
+
+ if (argUsed > 0)
+ s << ", ";
+
+ if (!m_disableNamedArgs)
+ s << "python::arg(\"" << arg->argumentName() << "\")";
+ else
+ s << "python::arg(0)";
+
+ if (!arg->defaultValueExpression().isEmpty()) {
+ QString defaultValue = arg->defaultValueExpression();
+ bool isPointer = arg->type()->isObject() ||
+ arg->type()->isQObject() ||
+ arg->type()->isNativePointer();
+
+ if (isPointer && defaultValue == "0") {
+ defaultValue = "python::object()";
+ } else if (arg->type()->isFlags()) {
+ defaultValue = " (int) " + defaultValue;
+ } else if (arg->type()->isEnum()) {
+ QString enumName = arg->type()->minimalSignature();
+ QRegExp reg("(.*::)");
+ reg.indexIn(enumName);
+ if (!defaultValue.startsWith(reg.cap(1)))
+ defaultValue = reg.cap(1) + defaultValue;
+ }
+
+ s << "=" << defaultValue;
+ hasDefaultValue = true;
+ }
+ argUsed++;
+ }
+
+ if (hasDefaultValue || ((argUsed > 0) && !m_disableNamedArgs))
+ sOut << "," << endl << INDENT << INDENT << "(" << aux << ")";
+}
+
+void CppGenerator::writeNormalMethodDef(QTextStream& s, const AbstractMetaFunction* func)
+{
+ s << INDENT << '{' << endl;
+ {
+ Indentation indentation(INDENT);
+ QString wrapperClassName = getWrapperName(func->ownerClass());
+ bool needDefaultFunction = func->isVirtual() && !func->isAbstract() && !func->ownerClass()->hasPrivateDestructor();
+ QString castName;
+
+ if (needDefaultFunction)
+ castName = writeFunctionCast(s, func, "_default", func->implementingClass()->qualifiedCppName());
+ else
+ castName = writeFunctionCast(s, func);
+
+ s << INDENT << "python_cls.def(\"" << func->name() << "\", ";
+
+ if (needDefaultFunction) { // add the default function
+ s << castName << "(&" << wrapperClassName << "::" << func->originalName() << "_default)";
+ } else {
+ if (func->isAbstract())
+ s << "python::pure_virtual";
+ s << '(' << castName << '(' << getMethodPointerString(func) << "))";
+ }
+
+ QString functionPolicy = getFunctionCallPolicy(func);
+ if (!functionPolicy.isEmpty())
+ s << ", " << functionPolicy;
+
+ writeFunctionArgsDef(s, func);
+ s << ");" << endl;
+ }
+ s << INDENT << '}' << endl;
+}
+
+void CppGenerator::writeModifiedMethodDef(QTextStream& s, const AbstractMetaFunction* func)
+{
+ s << INDENT << '{' << endl;
+ {
+ Indentation indentation(INDENT);
+ QString castName = writeFunctionCast(s, func);
+ s << INDENT
+ << "python_cls.def(\""
+ << func->name() << "\", "
+ << castName
+ << "(&" << func->implementingClass()->name()
+ << "_" << func->originalName()
+ << "_modified)";
+ QString functionPolicy = getFunctionCallPolicy(func);
+ if (!functionPolicy.isEmpty())
+ s << ", " << functionPolicy;
+
+ writeFunctionArgsDef(s, func);
+ s << ");" << endl;
+ }
+ s << INDENT << '}' << endl;
+}
+
+QString CppGenerator::operatorFunctionName(const AbstractMetaFunction *cppFunction)
+{
+ QString funcName = QString("%1_operator_%2_")
+ .arg(cppFunction->arguments()[0]->type()->name())
+ .arg(cppFunction->arguments()[1]->type()->name());
+
+ if (cppFunction->name().contains(">>")) {
+ funcName += "rshift";
+ } else if (cppFunction->name().contains("<<")) {
+ funcName += "lshift";
+ } else {
+ //TODO: implemente support to others operators
+ return QString();
+ }
+
+ return funcName;
+}
+
+void CppGenerator::writeGlobalOperatorOverloadImpl(QTextStream& s, const AbstractMetaFunction* cppFunction)
+{
+ Indentation indent(INDENT);
+ QString operatorStr;
+
+ if (cppFunction->name().contains(">>")) {
+ operatorStr = " >> ";
+ } else if (cppFunction->name().contains("<<")) {
+ operatorStr = " << ";
+ } else {
+ //TODO: implemente support to others operators
+ return;
+ }
+
+ QString funcName = operatorFunctionName(cppFunction);
+ bool reverse = cppFunction->isReverseOperator();
+
+ const AbstractMetaClass *klass = cppFunction->ownerClass();
+ s << "python::object " << funcName << "(";
+ writeFunctionArguments(s, cppFunction, SkipDefaultValues | SkipRemovedArguments);
+ s << ")" << endl << "{" << endl
+ << INDENT << cppFunction->arguments()[reverse]->argumentName()
+ << operatorStr << cppFunction->arguments()[!reverse]->argumentName() << ";" << endl
+ << INDENT << "return python::object(PySide::ptr(&"
+ << cppFunction->arguments()[reverse]->argumentName() << "));" << endl
+ << "}" << endl;
+}
+
+void CppGenerator::writeGlobalOperatorOverload(QTextStream &s, const AbstractMetaFunction *cppFunction)
+{
+ QString funcName = operatorFunctionName(cppFunction);
+ if (funcName.isEmpty())
+ return;
+
+ bool reverse = cppFunction->isReverseOperator();
+ QString operatorStr;
+ if (cppFunction->name().contains(">>")) {
+ operatorStr = QString("__%1rshift__").arg(reverse ? "r" : "");
+ } else if (cppFunction->name().contains("<<")) {
+ operatorStr = QString("__%1lshift__").arg(reverse ? "r" : "");
+ } else {
+ //TODO: implemente support to others operators
+ return;
+ }
+
+ s << INDENT << "python_cls.def(\"" << operatorStr << "\", " << funcName << ");\n";
+}
+
+QString CppGenerator::getOperatorArgumentTypeName(const AbstractMetaFunction *cppFunction, int argumentIndex)
+{
+ AbstractMetaType* type = cppFunction->arguments()[argumentIndex]->type();
+ if (type->name() == cppFunction->implementingClass()->name())
+ return QLatin1String("python::self");
+
+ QString typeName = translateType(type, cppFunction->implementingClass(),
+ (Option)(ExcludeReference));
+ return type->isPrimitive() ? "(" + typeName + ")(0)" : "python::other<" + typeName + " >()";
+}
+
+void CppGenerator::writeOperatorOverload(QTextStream& s, const AbstractMetaFunction* cppFunction)
+{
+ static QRegExp operatorRegex("operator(.+)");
+
+ if (!operatorRegex.exactMatch(cppFunction->originalName())) {
+ qWarning("What kind of operator is that!? %s",
+ cppFunction->originalName().toLocal8Bit().data());
+ return;
+ }
+
+ QString op(operatorRegex.cap(1));
+ if (op == "=" || op == "[]") {
+ // = is handled by type boost and type conversions, [] by someone...
+ return;
+ }
+
+ // no args == member unary operator
+ if (!cppFunction->arguments().count()) {
+ // check if it is a name instead of an operator symbol
+ // this means it is a conversion operator that will be ignored for now
+ static QRegExp ConversionOperatorRegex("[A-Za-z]+");
+ if (ConversionOperatorRegex.indexIn(op) < 0)
+ s << INDENT << "python_cls.def(" << op << "python::self);" << endl;
+ return;
+ }
+
+ //this because global operators use first arg with current class
+ if (cppFunction->isInGlobalScope()) {
+ writeGlobalOperatorOverload(s, cppFunction);
+ return;
+ }
+
+ QString operand1, operand2;
+ if (cppFunction->arguments().count() == 1) {
+ operand1 = "python::self";
+ operand2 = getOperatorArgumentTypeName(cppFunction, 0);
+ } else {
+ operand1 = getOperatorArgumentTypeName(cppFunction, 0);
+ operand2 = getOperatorArgumentTypeName(cppFunction, 1);
+ }
+ s << INDENT << "python_cls.def(" << operand1 << ' ' << op << ' ' << operand2 << ");\n";
+}
+
+void CppGenerator::finishGeneration()
+{
+ //Generate boost wrapper file
+ QString classFiles;
+ QTextStream sClassFiles(&classFiles);
+ QString classPythonDefines;
+ QTextStream sClassPythonDefines(&classPythonDefines);
+
+ Indentation indent(INDENT);
+
+ foreach (AbstractMetaClass *cls, classes()) {
+ if (!shouldGenerate(cls) || cls->enclosingClass())
+ continue;
+
+ if (m_packageName.isEmpty())
+ m_packageName = cls->package();
+
+ QString wrapperName = getWrapperName(cls);
+ QString boostFilename;
+ boostFilename += wrapperName + ".hpp";
+ sClassFiles << "#include \"" << boostFilename << "\"" << endl;
+
+ QString define_str = wrapperName + "::";
+ define_str += "define_python_class();";
+
+ sClassPythonDefines << INDENT << define_str << endl;
+ }
+
+ QString moduleFileName(outputDirectory() + "/" + subDirectoryForPackage(m_packageName));
+ moduleFileName += "/" + moduleName().toLower() + "_module_wrapper.cpp";
+
+ QFile file(moduleFileName);
+ if (file.open(QFile::WriteOnly)) {
+ QTextStream s(&file);
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ s << "#include \"converter_register_" << moduleName().toLower();
+ s << ".hpp\"" << endl << endl;
+
+ s << classFiles << endl;
+
+ s << "using namespace boost;" << endl << endl;
+ s << "using namespace PySide;" << endl << endl;
+
+ s << "// forward decl. for global func. register\n";
+ s << "void register_global_functions_" << moduleName().toLower() << "();\n\n";
+
+ s << "BOOST_PYTHON_MODULE(" << moduleName() << ")" << endl;
+ s << "{" << endl;
+
+ foreach (QString requiredModule, TypeDatabase::instance()->requiredTargetImports()) {
+ s << INDENT << "if (";
+ s << "PyImport_ImportModule(\"" << requiredModule << "\") == NULL) {" << endl;
+ s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError,";
+ s << "\"could not import " << requiredModule << "\");" << endl;
+ s << INDENT << INDENT << "return;" << endl;
+ s << INDENT << "}" << endl;
+ }
+ s << endl;
+
+ s << INDENT << "register_type_converters_" << moduleName().toLower() << "();" << endl << endl
+ << classPythonDefines << endl
+ << INDENT << "register_global_functions_" << moduleName().toLower() << "();" << endl
+ << INDENT << "//Namespaces" << endl;
+
+
+ s << "}" << endl << endl;
+ }
+
+ writeGlobalFunctions();
+}
+
+void CppGenerator::writeGlobalFunctions()
+{
+ QString fileName = moduleName().toLower() + "_globals_wrapper.cpp";
+
+ FileOut fileOut(outputDirectory() + "/" + subDirectoryForPackage(m_packageName) + "/" + fileName);
+
+ QSet<QString> includes;
+ QString defsStr;
+ QTextStream defsStream(&defsStr);
+
+ foreach (AbstractMetaFunction* func, globalFunctions()) {
+ QString incFile = func->includeFile();
+ QRegExp regex("\\b" + moduleName() + "\\b");
+ //FIXME: this regex doesn't work with all cases, e.g.:
+ // moduleName() = local
+ // incFile = /usr/local/include/local
+ if (regex.indexIn(incFile) == -1)
+ continue;
+
+ int idx = incFile.indexOf(moduleName());
+ QString cleanPath = QDir::cleanPath(incFile.mid(idx));
+ if (!cleanPath.startsWith(moduleName()))
+ continue;
+
+ includes << cleanPath;
+ defsStream << INDENT << "{\n" << INDENT;
+ QString castName = writeFunctionCast(defsStream, func);
+ defsStream << INDENT << INDENT << "python::def(\"" << func->name();
+ defsStream << "\", " << castName << '(' << func->name() << ')';
+ if (func->type() && func->type()->isReference())
+ defsStream << ", python::return_internal_reference<>()";
+ defsStream << ");\n";
+ defsStream << INDENT << "}\n";
+ }
+
+ QTextStream& s = fileOut.stream;
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ s << "#include \"pyside.hpp\"" << endl;
+
+ foreach (QString include, includes)
+ s << "#include <" << include << ">\n";
+
+ s << "using namespace boost;\n\n";
+ s << "using namespace PySide;\n\n";
+
+ // Add module level code snippets to 'Global' class
+ TypeSystemTypeEntry *moduleEntry = dynamic_cast<TypeSystemTypeEntry *>(
+ TypeDatabase::instance()->findType(m_packageName));
+ QString sEnd;
+ QTextStream snipEnd(&sEnd);
+ if (moduleEntry && moduleEntry->codeSnips().size() > 0) {
+ foreach (CodeSnip snip, moduleEntry->codeSnips()) {
+ if (snip.position == CodeSnip().Beginning)
+ formatCode(s, snip.code(), INDENT);
+ else
+ formatCode(snipEnd, snip.code(), INDENT);
+ }
+ }
+
+ s << "\nvoid register_global_functions_" << moduleName().toLower() << "() {\n";
+ { //global enums
+ QString name_space;
+
+ foreach (AbstractMetaEnum *cppEnum, globalEnums()) {
+ if (cppEnum)
+ writeEnum(s, cppEnum, name_space);
+ }
+ }
+ s << sEnd;
+ s << defsStr;
+ s << "}\n";
+}
+
+QMap<QString, QString> CppGenerator::options() const
+{
+ QMap<QString, QString> res;
+ res.insert("disable-named-arg", "Disable Python names arguments.");
+ return res;
+}
+
+bool CppGenerator::doSetup(const QMap<QString, QString>& args )
+{
+ m_disableNamedArgs = args.contains("disable-named-arg");
+ return BoostPythonGenerator::doSetup(args);
+}
diff --git a/generators/boostpython/cppgenerator.h b/generators/boostpython/cppgenerator.h
new file mode 100644
index 000000000..5fcc6f38f
--- /dev/null
+++ b/generators/boostpython/cppgenerator.h
@@ -0,0 +1,100 @@
+/*
+ * 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 CPPGENERATOR_H
+#define CPPGENERATOR_H
+
+#include "boostpythongenerator.h"
+
+/**
+* The CppGenerator generate the implementation of boost::python bindings classes.
+*/
+class CppGenerator : public BoostPythonGenerator
+{
+public:
+ const char* name() const
+ {
+ return "CppGenerator";
+ }
+
+ QMap<QString, QString> options() const;
+ bool doSetup(const QMap<QString, QString>& args);
+
+protected:
+ QString fileNameForClass(const AbstractMetaClass *cppClass) const;
+ void generateClass(QTextStream &s, const AbstractMetaClass *cppClass);
+ void finishGeneration();
+
+private:
+ void writePrelude(QTextStream &s, const AbstractMetaClass *cppClass);
+ void writeBoostDeclaration(QTextStream &s, const AbstractMetaClass *cppClass);
+
+ // method declaration writers
+ void writeConstructor(QTextStream &s, const AbstractMetaFunction *func);
+ void writeConstructorInitialization(QTextStream &s, const AbstractMetaFunction *func);
+ void writeNormalMethodDef(QTextStream &s, const AbstractMetaFunction *func);
+ void writeModifiedMethodDef(QTextStream &s, const AbstractMetaFunction *func);
+ void writeOperatorOverload(QTextStream &s, const AbstractMetaFunction *func);
+ void writeGlobalOperatorOverload(QTextStream &s, const AbstractMetaFunction *func);
+ void writeFunctionArgsDef(QTextStream &s_out, const AbstractMetaFunction *func);
+ void writeGlobalFunctions();
+ void writeDestructor(QTextStream &s, const AbstractMetaClass *cppClass);
+
+ // method implementation writers
+ void writeModifiedConstructorImpl(QTextStream &s, const AbstractMetaFunction *func);
+ void writeConstructorImpl(QTextStream &s, const AbstractMetaFunction *func);
+ void writeVirtualMethodImpl(QTextStream &s, const AbstractMetaFunction *func);
+ void writeVirtualMethodImplHead(QTextStream &s, const AbstractMetaFunction *func);
+ void writeVirtualMethodImplFoot(QTextStream &s, const AbstractMetaFunction *func);
+ void writePureVirtualMethodImplFoot(QTextStream &s, const AbstractMetaFunction *func);
+ void writeNonVirtualModifiedFunctionImpl(QTextStream &s, const AbstractMetaFunction *func);
+ void writeGlobalOperatorOverloadImpl(QTextStream& s, const AbstractMetaFunction* func);
+
+ // helper functions
+ QString writeFunctionCast(QTextStream& s, const AbstractMetaFunction* func, const QString& castNameSuffix = QString(), const QString& className = QString());
+ QString getFuncTypedefName(const AbstractMetaFunction *func) const;
+ QString getFunctionReturnType(const AbstractMetaFunction *func);
+ AbstractMetaFunction* findMainConstructor(const AbstractMetaClass *clazz);
+ QString getArgumentType(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func, int idx);
+ QString operatorFunctionName(const AbstractMetaFunction *func);
+ QString getOperatorArgumentTypeName(const AbstractMetaFunction *func, int argumentIndex);
+
+ // call policy related
+ QString verifyDefaultReturnPolicy(const AbstractMetaFunction *func, const QString &callPolicy);
+ QString getFunctionCallPolicy(const AbstractMetaFunction *func);
+
+ // enum related
+ void writeEnums(QTextStream &s, const AbstractMetaClass *cppClass, bool useNamespace);
+ void writeEnum(QTextStream &s, const AbstractMetaEnum *cppEnum, const QString &nameSpace);
+ // write implicitly conversions
+ void writeImplicitlyConversion(QTextStream &s, const AbstractMetaClass *cppClass);
+ void writeVirtualDefaultFunction(QTextStream &s, const AbstractMetaFunction *arg2);
+
+ void writeHashFunction(QTextStream &s, const AbstractMetaClass *cppClass);
+ QString baseClassName(const QString &name);
+
+ bool m_disableNamedArgs;
+};
+
+#endif // CPPGENERATOR_H
+
diff --git a/generators/boostpython/hppgenerator.cpp b/generators/boostpython/hppgenerator.cpp
new file mode 100644
index 000000000..f6d576d08
--- /dev/null
+++ b/generators/boostpython/hppgenerator.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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 "hppgenerator.h"
+#include <apiextractor/reporthandler.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+#include <QtCore/QVariant>
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+
+static Indentor INDENT;
+
+QString HppGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const
+{
+ return getWrapperName(cppClass) + QLatin1String(".hpp");
+}
+
+void HppGenerator::writeCopyCtor(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+ s << INDENT << getWrapperName(cppClass) << "(PyObject *py_self, const " << cppClass->qualifiedCppName() << "& self)"
+ << " : " << cppClass->qualifiedCppName() << "(self), wrapper(py_self)" << endl
+ << INDENT << "{" << endl
+ << INDENT << "}" << endl;
+}
+
+void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass)
+{
+ ReportHandler::debugSparse("Generating header for " + cppClass->fullName());
+ Indentation indent(INDENT);
+
+ // write license comment
+ s << licenseComment() << endl;
+
+ QString wrapperName = HppGenerator::getWrapperName(cppClass);
+ // Header
+ s << "#ifndef __" << wrapperName.toUpper() << "__" << endl;
+ s << "#define __" << wrapperName.toUpper() << "__" << endl << endl;
+
+ s << "#include <pyside.hpp>" << endl;
+ //Includes
+ if (cppClass->typeEntry()->include().isValid())
+ s << cppClass->typeEntry()->include().toString() << endl << endl;
+
+ s << "using namespace PySide;" << endl << endl;
+
+ if (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor() || cppClass->isNamespace())
+ s << "namespace " << wrapperName << " {" << endl << endl;
+
+ bool needWriteBackReference = false;
+ if (cppClass->isNamespace()) {
+ s << INDENT << "struct Namespace {};" << endl;
+ } else {
+ QString className;
+ bool create_wrapper = canCreateWrapperFor(cppClass);
+ bool is_wrapper = false;
+ // detect the held type
+ QString held_type = cppClass->typeEntry()->heldTypeValue();
+ if (held_type.isEmpty() && create_wrapper)
+ held_type = "qptr";
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::Declaration, TypeSystem::NativeCode);
+
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) {
+ // Class
+ s << "class PYSIDE_LOCAL " << wrapperName;
+ if (create_wrapper) {
+ s << " : public " << cppClass->qualifiedCppName() << ", public PySide::wrapper";
+ }
+ s << endl;
+ s << "{" << endl;
+ }
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::Declaration, TypeSystem::ShellDeclaration);
+
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) {
+ s << endl << "private:" << endl;
+ className = wrapperName;
+ is_wrapper = true;
+ } else {
+ className = cppClass->qualifiedCppName();
+ }
+
+ // print the huge boost::python::class_ typedef
+ s << INDENT << "typedef boost::python::class_< " << cppClass->qualifiedCppName();
+
+ writeBaseClass(s, cppClass);
+
+ if (!held_type.isEmpty())
+ s << ", PySide::" << held_type << " < " << className << ", qptr_base::no_check_cache | qptr_base::"
+ << ( is_wrapper ? "wrapper_pointer" : "no_wrapper_pointer") << "> ";
+
+ if (!isCopyable(cppClass))
+ s << ", boost::noncopyable";
+
+ s << " > class_type;" << endl;
+
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) {
+ s << "public:" << endl;
+
+ if (isCopyable(cppClass))
+ writeCopyCtor(s, cppClass);
+
+ foreach (AbstractMetaFunction *func, filterFunctions(cppClass))
+ writeFunction(s, func);
+
+ if (create_wrapper) {
+ //destructor
+ s << INDENT << "~" << wrapperName << "();" << endl;
+
+ if (cppClass->isQObject() && (cppClass->name() != "QObject"))
+ s << INDENT << "using QObject::parent;" << endl;
+ }
+ }
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::End, TypeSystem::ShellDeclaration);
+ }
+
+ QString staticKeyword = cppClass->isNamespace() ? QLatin1String("") : QLatin1String("static ");
+ s << INDENT;
+ if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) {
+ s << "//static member used to export class" << endl;
+ s << INDENT << staticKeyword;
+ }
+ s << "void define_python_class() throw();" << endl << endl;
+
+ writeCodeSnips(s, cppClass->typeEntry()->codeSnips(),
+ CodeSnip::PrototypeInitialization, TypeSystem::NativeCode);
+
+
+ s << "};" << endl << endl;
+
+ s << "#endif // __" << wrapperName.toUpper() << "__" << endl << endl;
+}
+
+void HppGenerator::writeFunction(QTextStream &s, const AbstractMetaFunction* func)
+{
+ // pure virtual functions need a default implementation
+ if ((func->isPrivate() && !func->isConstructor()) || (func->isModifiedRemoved() && !func->isAbstract()))
+ return;
+
+ // do not write copy ctors here.
+ if (func->isCopyConstructor())
+ return;
+
+ if (func->isConstructor() || func->isAbstract() || func->isVirtual()) {
+ if (func->isVirtual() && !func->isAbstract() && !func->isConstructor()
+ && !func->ownerClass()->hasPrivateDestructor()
+ && func->implementingClass() == func->ownerClass()) {
+ s << INDENT << "static " << signatureForDefaultVirtualMethod(func, "", "_default", Generator::SkipName) << ';' << endl;
+ }
+
+ if (func->isConstructor()) {
+ s << INDENT << getWrapperName(func->ownerClass()) << "(PyObject *py_self" << (func->arguments().size() ? "," : "");
+ writeFunctionArguments(s, func, Generator::OriginalTypeDescription | Generator::SkipName);
+ s << ")";
+ } else {
+ s << INDENT << functionSignature(func, "", "", Generator::OriginalTypeDescription | Generator::SkipName);
+ }
+
+ if (func->isModifiedRemoved() && func->isAbstract())
+ writeDefaultImplementation(s, func);
+ else
+ s << ';' << endl;
+ }
+}
+
+void HppGenerator::writeDefaultImplementation(QTextStream& s, const AbstractMetaFunction* func)
+{
+ QString returnValue;
+ if (func->type()) {
+ if (func->type()->isObject() || func->type()->isQObject() || func->type()->name() == "void")
+ returnValue = "0";
+ else
+ returnValue = functionReturnType(func) + "()";
+ }
+ s << " { return " << returnValue << "; }" << endl;
+}
+
+void HppGenerator::writeBaseClass(QTextStream& s, const AbstractMetaClass* cppClass)
+{
+ if (!cppClass->isNamespace() && !cppClass->isInterface()) {
+ QStringList baseClass = getBaseClasses(cppClass);
+
+ if (baseClass.isEmpty()) {
+ const ComplexTypeEntry *type = cppClass->typeEntry();
+ if (cppClass->name() != type->defaultSuperclass()) {
+ QString sc = type->defaultSuperclass();
+ if (!sc.isEmpty())
+ s << ", python::bases< " << sc << "> ";
+ }
+ } else {
+ s << ", boost::python::bases< " << baseClass.join(", ") << " > ";
+ }
+ }
+}
+
diff --git a/generators/boostpython/hppgenerator.h b/generators/boostpython/hppgenerator.h
new file mode 100644
index 000000000..8e0f5f03b
--- /dev/null
+++ b/generators/boostpython/hppgenerator.h
@@ -0,0 +1,51 @@
+/*
+ * 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 HPPGENERATOR_H
+#define HPPGENERATOR_H
+
+#include "boostpythongenerator.h"
+
+/**
+* The HppGenerator generate the declarations of boost::python bindings classes.
+*/
+class HppGenerator : public BoostPythonGenerator
+{
+protected:
+ QString fileNameForClass(const AbstractMetaClass* cppClass) const;
+ void generateClass(QTextStream& s, const AbstractMetaClass* cppClass);
+ void finishGeneration() {}
+ const char* name() const
+ {
+ return "HppGenerator";
+ }
+private:
+ void writeFunction(QTextStream& s, const AbstractMetaFunction* func);
+ void writePureVirtualEmptyImpl(QTextStream& , const AbstractMetaFunction* func);
+ void writeBaseClass(QTextStream& s, const AbstractMetaClass* cppClass);
+ void writeCopyCtor(QTextStream &s, const AbstractMetaClass* cppClass);
+ void writeDefaultImplementation(QTextStream& s, const AbstractMetaFunction* func);
+};
+
+#endif // HPPGENERATOR_H
+
diff --git a/generators/qtdoc/CMakeLists.txt b/generators/qtdoc/CMakeLists.txt
new file mode 100644
index 000000000..fb71784ab
--- /dev/null
+++ b/generators/qtdoc/CMakeLists.txt
@@ -0,0 +1,10 @@
+project(qtdoc_generator)
+
+set(qtdoc_generator_SRC
+qtdocgenerator.cpp
+)
+
+add_library(qtdoc_generator SHARED ${qtdoc_generator_SRC})
+target_link_libraries(qtdoc_generator ${APIEXTRACTOR_LIBRARY} ${QT_QTCORE_LIBRARY} genrunner)
+
+install(TARGETS qtdoc_generator DESTINATION ${LIB_INSTALL_DIR})
diff --git a/generators/qtdoc/qtdocgenerator.cpp b/generators/qtdoc/qtdocgenerator.cpp
new file mode 100644
index 000000000..762c8a9b6
--- /dev/null
+++ b/generators/qtdoc/qtdocgenerator.cpp
@@ -0,0 +1,1391 @@
+/*
+ * 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 "qtdocgenerator.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(QtDocGenerator* 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::resolveContextForMethod(const QString& methodName)
+{
+ QString currentClass = m_context.split(".").last();
+
+ const AbstractMetaClass* metaClass = 0;
+ foreach (const AbstractMetaClass* cls, m_generator->classes()) {
+ if (cls->name() == currentClass) {
+ metaClass = cls;
+ break;
+ }
+ }
+
+ if (metaClass) {
+ QList<const AbstractMetaFunction*> funcList;
+ foreach (const AbstractMetaFunction* func, metaClass->queryFunctionsByName(methodName)) {
+ if (methodName == func->name())
+ funcList.append(func);
+ }
+
+ const AbstractMetaClass* implementingClass = 0;
+ foreach (const AbstractMetaFunction* func, funcList) {
+ implementingClass = func->implementingClass();
+ if (implementingClass->name() == currentClass)
+ break;
+ }
+
+ if (implementingClass)
+ return implementingClass->name();
+ }
+
+ return m_context;
+}
+
+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.first() == m_context) {
+ QString context = resolveContextForMethod(rawlinklist.last());
+ l_linkref.prepend('~' + context + '.');
+ }
+ } else if (l_type == "function" && m_context.isEmpty()) {
+ l_linktag = " :func:`";
+ } else if (l_type == "class") {
+ l_linktag = " :class:`";
+ QStringList rawlinklist = l_linkref.split(".");
+ QStringList splittedContext = m_context.split(".");
+ if (rawlinklist.size() == 1 || rawlinklist.first() == splittedContext.last()) {
+ splittedContext.removeLast();
+ l_linkref.prepend('~' + splittedContext.join(".") + '.');
+ }
+ } 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 QString(cppClass->typeEntry()->qualifiedCppName()).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 QtDocGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const
+{
+ return QString("%1.rst").arg(getClassName(cppClass));
+}
+
+void QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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);
+
+ 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 QtDocGenerator::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("("));
+
+ QString scope = cppClass->name();
+ QStringList splittedMethName = methName.split(".");
+
+ if (splittedMethName.first() == scope) {
+ splittedMethName.removeFirst();
+ methName = splittedMethName.join(".");
+ }
+ scope.append(".");
+
+ data = QString("def :meth:`%1<%2%3>` %4")
+ .arg(methName)
+ .arg(scope)
+ .arg(methName)
+ .arg(methArgs);
+
+ return data;
+}
+
+
+void QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func)
+{
+ if (!func->isConstructor())
+ s << getClassName(cppClass) << '.';
+ else if (func->implementingClass() && func->implementingClass()->enclosingClass())
+ s << func->implementingClass()->enclosingClass()->name() << '.';
+ s << getFuncName(func) << "(" << parseArgDocStyle(cppClass, func) << ")";
+}
+
+QString QtDocGenerator::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 QtDocGenerator::writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg)
+{
+ s << INDENT << ":param " << arg->argumentName() << ": "
+ << translateToPythonType(arg->type(), cppClass) << endl;
+}
+
+void QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::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 QtDocGenerator::doSetup(const QMap<QString, QString>& 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> QtDocGenerator::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..b312d4455
--- /dev/null
+++ b/generators/qtdoc/qtdocgenerator.h
@@ -0,0 +1,222 @@
+/*
+ * 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 <QtCore/QStack>
+#include <QtCore/QHash>
+#include <QtCore/QTextStream>
+#include <apiextractor/abstractmetalang.h>
+#include "generator.h"
+
+class AbstractMetaFunction;
+class AbstractMetaClass;
+class QXmlStreamReader;
+class QtDocGenerator;
+
+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(QtDocGenerator* generator, const QString& doc, const QString& context = QString());
+
+ QString result() const
+ {
+ return m_result;
+ }
+
+private:
+ QString resolveContextForMethod(const QString& methodName);
+ 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;
+ QtDocGenerator* 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 QtDocGenerator : public Generator
+{
+public:
+ QString libSourceDir() const
+ {
+ return m_libSourceDir;
+ }
+
+ bool doSetup(const QMap<QString, QString>& args);
+
+ const char* name() const
+ {
+ return "QtDocGenerator";
+ }
+
+ 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();
+
+ void writeFunctionArguments(QTextStream&, const AbstractMetaFunction*, uint) const {}
+ void writeArgumentNames(QTextStream&, const AbstractMetaFunction*, uint) const {}
+ QString subDirectoryForClass(const AbstractMetaClass* clazz) const
+ {
+ Q_ASSERT(false);
+ return QString();
+ }
+
+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