diff options
author | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-25 19:43:06 -0300 |
---|---|---|
committer | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-25 19:43:06 -0300 |
commit | fd52957d375eb990a0fabbfab35d36480ee057ae (patch) | |
tree | 3ef09ee06c63dc953dfa25829cc8f4a71bb81dde /generators | |
parent | 8f941405798b67204945d7ec94ef8e63e1535877 (diff) | |
parent | f8fba84d7b0230b1f48109c339197c407757bea1 (diff) |
Merge commit 'mainline/master' into metaclassname
Conflicts:
boostpythongenerator.cpp
Diffstat (limited to 'generators')
-rw-r--r-- | generators/CMakeLists.txt | 2 | ||||
-rw-r--r-- | generators/boostpython/CMakeLists.txt | 14 | ||||
-rw-r--r-- | generators/boostpython/boostpython.cpp | 29 | ||||
-rw-r--r-- | generators/boostpython/boostpythongenerator.cpp | 311 | ||||
-rw-r--r-- | generators/boostpython/boostpythongenerator.h | 129 | ||||
-rw-r--r-- | generators/boostpython/convertergenerator.cpp | 180 | ||||
-rw-r--r-- | generators/boostpython/convertergenerator.h | 77 | ||||
-rw-r--r-- | generators/boostpython/cppgenerator.cpp | 1439 | ||||
-rw-r--r-- | generators/boostpython/cppgenerator.h | 100 | ||||
-rw-r--r-- | generators/boostpython/hppgenerator.cpp | 220 | ||||
-rw-r--r-- | generators/boostpython/hppgenerator.h | 51 | ||||
-rw-r--r-- | generators/qtdoc/CMakeLists.txt | 10 | ||||
-rw-r--r-- | generators/qtdoc/qtdocgenerator.cpp | 1391 | ||||
-rw-r--r-- | generators/qtdoc/qtdocgenerator.h | 222 |
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 |