##// END OF EJS Templates
generate extra wrappers for virtual slots to allow correct deriving via Python and to avoid recursion...
generate extra wrappers for virtual slots to allow correct deriving via Python and to avoid recursion git-svn-id: svn://svn.code.sf.net/p/pythonqt/code/trunk@214 ea8d5007-eb21-0410-b261-ccb3ea6e24a9

File last commit:

r124:72837eb4c23c
r175:7bc6abba6c12
Show More
typesystem.cpp
2013 lines | 75.8 KiB | text/x-c | CppLexer
/****************************************************************************
**
** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Script Generator project on Qt Labs.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "typesystem.h"
#include "generator.h"
#include "customtypes.h"
#include <reporthandler.h>
#include <memory>
#include <QtXml>
QString strings_Object = QLatin1String("Object");
QString strings_String = QLatin1String("String");
QString strings_Thread = QLatin1String("Thread");
QString strings_char = QLatin1String("char");
QString strings_java_lang = QLatin1String("java.lang");
QString strings_jchar = QLatin1String("jchar");
QString strings_jobject = QLatin1String("jobject");
static void addRemoveFunctionToTemplates(TypeDatabase *db);
class StackElement
{
public:
enum ElementType {
None = 0x0,
// Type tags (0x1, ... , 0xff)
ObjectTypeEntry = 0x1,
ValueTypeEntry = 0x2,
InterfaceTypeEntry = 0x3,
NamespaceTypeEntry = 0x4,
ComplexTypeEntryMask = 0xf,
// Non-complex type tags (0x10, 0x20, ... , 0xf0)
PrimitiveTypeEntry = 0x10,
EnumTypeEntry = 0x20,
TypeEntryMask = 0xff,
// Simple tags (0x100, 0x200, ... , 0xf00)
ExtraIncludes = 0x100,
Include = 0x200,
ModifyFunction = 0x300,
ModifyField = 0x400,
Root = 0x500,
CustomMetaConstructor = 0x600,
CustomMetaDestructor = 0x700,
ArgumentMap = 0x800,
SuppressedWarning = 0x900,
Rejection = 0xa00,
LoadTypesystem = 0xb00,
RejectEnumValue = 0xc00,
Template = 0xd00,
TemplateInstanceEnum = 0xe00,
Replace = 0xf00,
SimpleMask = 0xf00,
// Code snip tags (0x1000, 0x2000, ... , 0xf000)
InjectCode = 0x1000,
InjectCodeInFunction = 0x2000,
CodeSnipMask = 0xf000,
// Function modifier tags (0x010000, 0x020000, ... , 0xf00000)
Access = 0x010000,
Removal = 0x020000,
Rename = 0x040000,
ModifyArgument = 0x080000,
FunctionModifiers = 0xff0000,
// Argument modifier tags (0x01000000 ... 0xf0000000)
ConversionRule = 0x01000000,
ReplaceType = 0x02000000,
ReplaceDefaultExpression = 0x04000000,
RemoveArgument = 0x08000000,
DefineOwnership = 0x10000000,
RemoveDefaultExpression = 0x20000000,
NoNullPointers = 0x40000000,
ReferenceCount = 0x80000000,
ArgumentModifiers = 0xff000000
};
StackElement(StackElement *p) : entry(0), type(None), parent(p){ }
TypeEntry *entry;
ElementType type;
StackElement *parent;
union {
TemplateInstance *templateInstance;
TemplateEntry *templateEntry;
CustomFunction *customFunction;
} value;
};
class Handler : public QXmlDefaultHandler
{
public:
Handler(TypeDatabase *database, bool generate)
: m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
{
m_current_enum = 0;
current = 0;
tagNames["rejection"] = StackElement::Rejection;
tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry;
tagNames["object-type"] = StackElement::ObjectTypeEntry;
tagNames["value-type"] = StackElement::ValueTypeEntry;
tagNames["interface-type"] = StackElement::InterfaceTypeEntry;
tagNames["namespace-type"] = StackElement::NamespaceTypeEntry;
tagNames["enum-type"] = StackElement::EnumTypeEntry;
tagNames["extra-includes"] = StackElement::ExtraIncludes;
tagNames["include"] = StackElement::Include;
tagNames["inject-code"] = StackElement::InjectCode;
tagNames["modify-function"] = StackElement::ModifyFunction;
tagNames["modify-field"] = StackElement::ModifyField;
tagNames["access"] = StackElement::Access;
tagNames["remove"] = StackElement::Removal;
tagNames["rename"] = StackElement::Rename;
tagNames["typesystem"] = StackElement::Root;
tagNames["custom-constructor"] = StackElement::CustomMetaConstructor;
tagNames["custom-destructor"] = StackElement::CustomMetaDestructor;
tagNames["argument-map"] = StackElement::ArgumentMap;
tagNames["suppress-warning"] = StackElement::SuppressedWarning;
tagNames["load-typesystem"] = StackElement::LoadTypesystem;
tagNames["define-ownership"] = StackElement::DefineOwnership;
tagNames["replace-default-expression"] = StackElement::ReplaceDefaultExpression;
tagNames["reject-enum-value"] = StackElement::RejectEnumValue;
tagNames["replace-type"] = StackElement::ReplaceType;
tagNames["conversion-rule"] = StackElement::ConversionRule;
tagNames["modify-argument"] = StackElement::ModifyArgument;
tagNames["remove-argument"] = StackElement::RemoveArgument;
tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression;
tagNames["template"] = StackElement::Template;
tagNames["insert-template"] = StackElement::TemplateInstanceEnum;
tagNames["replace"] = StackElement::Replace;
tagNames["no-null-pointer"] = StackElement::NoNullPointers;
tagNames["reference-count"] = StackElement::ReferenceCount;
}
bool startElement(const QString &namespaceURI, const QString &localName,
const QString &qName, const QXmlAttributes &atts);
bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);
QString errorString() const { return m_error; }
bool error(const QXmlParseException &exception);
bool fatalError(const QXmlParseException &exception);
bool warning(const QXmlParseException &exception);
bool characters(const QString &ch);
private:
void fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
QHash<QString, QString> *acceptedAttributes);
bool importFileElement(const QXmlAttributes &atts);
bool convertBoolean(const QString &, const QString &, bool);
TypeDatabase *m_database;
StackElement* current;
QString m_defaultPackage;
QString m_defaultSuperclass;
QString m_error;
TypeEntry::CodeGeneration m_generate;
EnumTypeEntry *m_current_enum;
CodeSnipList m_code_snips;
FunctionModificationList m_function_mods;
FieldModificationList m_field_mods;
QHash<QString, StackElement::ElementType> tagNames;
};
bool Handler::error(const QXmlParseException &e)
{
qWarning("Error: line=%d, column=%d, message=%s\n",
e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
return false;
}
bool Handler::fatalError(const QXmlParseException &e)
{
qWarning("Fatal error: line=%d, column=%d, message=%s\n",
e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
return false;
}
bool Handler::warning(const QXmlParseException &e)
{
qWarning("Warning: line=%d, column=%d, message=%s\n",
e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
return false;
}
void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
QHash<QString, QString> *acceptedAttributes)
{
Q_ASSERT(acceptedAttributes != 0);
for (int i=0; i<atts.length(); ++i) {
QString key = atts.localName(i).toLower();
QString val = atts.value(i);
if (!acceptedAttributes->contains(key)) {
ReportHandler::warning(QString("Unknown attribute for '%1': '%2'").arg(name).arg(key));
} else {
(*acceptedAttributes)[key] = val;
}
}
}
bool Handler::endElement(const QString &, const QString &localName, const QString &)
{
QString tagName = localName.toLower();
if(tagName == "import-file")
return true;
if (!current)
return true;
switch (current->type) {
case StackElement::ObjectTypeEntry:
case StackElement::ValueTypeEntry:
case StackElement::InterfaceTypeEntry:
case StackElement::NamespaceTypeEntry:
{
ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(current->entry);
centry->setFunctionModifications(m_function_mods);
centry->setFieldModifications(m_field_mods);
centry->setCodeSnips(m_code_snips);
if (centry->designatedInterface()) {
centry->designatedInterface()->setCodeSnips(m_code_snips);
centry->designatedInterface()->setFunctionModifications(m_function_mods);
}
m_code_snips = CodeSnipList();
m_function_mods = FunctionModificationList();
m_field_mods = FieldModificationList();
}
break;
case StackElement::CustomMetaConstructor:
{
current->entry->setCustomConstructor(*current->value.customFunction);
delete current->value.customFunction;
}
break;
case StackElement::CustomMetaDestructor:
{
current->entry->setCustomDestructor(*current->value.customFunction);
delete current->value.customFunction;
}
break;
case StackElement::EnumTypeEntry:
m_current_enum = 0;
break;
case StackElement::Template:
m_database->addTemplate(current->value.templateEntry);
break;
case StackElement::TemplateInstanceEnum:
if(current->parent->type == StackElement::InjectCode){
m_code_snips.last().addTemplateInstance(current->value.templateInstance);
}else if(current->parent->type == StackElement::Template){
current->parent->value.templateEntry->addTemplateInstance(current->value.templateInstance);
}else if(current->parent->type == StackElement::CustomMetaConstructor || current->parent->type == StackElement::CustomMetaConstructor){
current->parent->value.customFunction->addTemplateInstance(current->value.templateInstance);
}else if(current->parent->type == StackElement::ConversionRule){
m_function_mods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(current->value.templateInstance);
}else if(current->parent->type == StackElement::InjectCodeInFunction){
m_function_mods.last().snips.last().addTemplateInstance(current->value.templateInstance);
}
break;
default:
break;
}
StackElement *child = current;
current=current->parent;
delete(child);
return true;
}
bool Handler::characters(const QString &ch)
{
if(current->type == StackElement::Template){
current->value.templateEntry->addCode(ch);
return true;
}
if (current->type == StackElement::CustomMetaConstructor || current->type == StackElement::CustomMetaDestructor){
current->value.customFunction->addCode(ch);
return true;
}
if (current->type == StackElement::ConversionRule){
m_function_mods.last().argument_mods.last().conversion_rules.last().addCode(ch);
return true;
}
if (current->parent){
if ((current->type & StackElement::CodeSnipMask) != 0) {
switch (current->parent->type) {
case StackElement::Root:
((TypeSystemTypeEntry *) current->parent->entry)->snips.last().addCode(ch);
break;
case StackElement::ModifyFunction:
m_function_mods.last().snips.last().addCode(ch);
break;
case StackElement::NamespaceTypeEntry:
case StackElement::ObjectTypeEntry:
case StackElement::ValueTypeEntry:
case StackElement::InterfaceTypeEntry:
m_code_snips.last().addCode(ch);
break;
default:
Q_ASSERT(false);
};
return true;
}
}
return true;
}
bool Handler::importFileElement(const QXmlAttributes &atts)
{
QString fileName = atts.value("name");
if(fileName.isEmpty()){
m_error = "Required attribute 'name' missing for include-file tag.";
return false;
}
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
file.setFileName(":/trolltech/generator/" + fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
m_error = QString("Could not open file: '%1'").arg(fileName);
return false;
}
}
QString quoteFrom = atts.value("quote-after-line");
bool foundFromOk = quoteFrom.isEmpty();
bool from = quoteFrom.isEmpty();
QString quoteTo = atts.value("quote-before-line");
bool foundToOk = quoteTo.isEmpty();
bool to = true;
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
if(from && to && line.contains(quoteTo)) {
to = false;
foundToOk = true;
break;
}
if(from && to)
characters(line + "\n");
if(!from && line.contains(quoteFrom)) {
from = true;
foundFromOk = true;
}
}
if(!foundFromOk || !foundToOk){
QString fromError = QString("Could not find quote-after-line='%1' in file '%2'.").arg(quoteFrom).arg(fileName);
QString toError = QString("Could not find quote-before-line='%1' in file '%2'.").arg(quoteTo).arg(fileName);
if(!foundToOk)
m_error = toError;
if(!foundFromOk)
m_error = fromError;
if(!foundFromOk && !foundToOk)
m_error = fromError + " " + toError;
return false;
}
return true;
}
bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue)
{
QString value = _value.toLower();
if (value == "true" || value == "yes") {
return true;
} else if (value == "false" || value == "no") {
return false;
} else {
QString warn = QString("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
.arg(value).arg(attributeName).arg(defaultValue ? "yes" : "no");
ReportHandler::warning(warn);
return defaultValue;
}
}
bool Handler::startElement(const QString &, const QString &n,
const QString &, const QXmlAttributes &atts)
{
QString tagName = n.toLower();
if(tagName == "import-file"){
return importFileElement(atts);
}
std::auto_ptr<StackElement> element(new StackElement(current));
if (!tagNames.contains(tagName)) {
m_error = QString("Unknown tag name: '%1'").arg(tagName);
return false;
}
element->type = tagNames[tagName];
if (element->type & StackElement::TypeEntryMask) {
if (current->type != StackElement::Root) {
m_error = "Nested types not supported";
return false;
}
QHash<QString, QString> attributes;
attributes["name"] = QString();
switch (element->type) {
case StackElement::PrimitiveTypeEntry:
attributes["java-name"] = QString();
attributes["jni-name"] = QString();
attributes["preferred-conversion"] = "yes";
attributes["preferred-java-type"] = "yes";
break;
case StackElement::EnumTypeEntry:
attributes["flags"] = "no";
attributes["upper-bound"] = QString();
attributes["lower-bound"] = QString();
attributes["force-integer"] = "no";
attributes["extensible"] = "no";
break;
case StackElement::ObjectTypeEntry:
case StackElement::ValueTypeEntry:
attributes["force-abstract"] = QString("no");
attributes["deprecated"] = QString("no");
// fall throooough
case StackElement::InterfaceTypeEntry:
attributes["default-superclass"] = m_defaultSuperclass;
attributes["polymorphic-id-expression"] = QString();
attributes["delete-in-main-thread"] = QString("no");
// fall through
case StackElement::NamespaceTypeEntry:
attributes["java-name"] = QString();
attributes["package"] = m_defaultPackage;
attributes["expense-cost"] = "1";
attributes["expense-limit"] = "none";
attributes["polymorphic-base"] = QString("no");
attributes["generate"] = QString("yes");
attributes["target-type"] = QString();
attributes["generic-class"] = QString("no");
break;
default:
; // nada
};
fetchAttributeValues(tagName, atts, &attributes);
QString name = attributes["name"];
// We need to be able to have duplicate primitive type entries, or it's not possible to
// cover all primitive java types (which we need to do in order to support fake
// meta objects)
if (element->type != StackElement::PrimitiveTypeEntry) {
TypeEntry *tmp = m_database->findType(name);
if (tmp != 0) {
ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name));
}
}
if (name.isEmpty()) {
m_error = "no 'name' attribute specified";
return false;
}
switch (element->type) {
case StackElement::PrimitiveTypeEntry:
{
QString java_name = attributes["java-name"];
QString jni_name = attributes["jni-name"];
QString preferred_conversion = attributes["preferred-conversion"].toLower();
QString preferred_java_type = attributes["preferred-java-type"].toLower();
if (java_name.isEmpty())
java_name = name;
if (jni_name.isEmpty())
jni_name = name;
PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name);
type->setCodeGeneration(m_generate);
type->setTargetLangName(java_name);
type->setJniName(jni_name);
type->setPreferredConversion(convertBoolean(preferred_conversion, "preferred-conversion", true));
type->setPreferredTargetLangType(convertBoolean(preferred_java_type, "preferred-java-type", true));
element->entry = type;
}
break;
case StackElement::EnumTypeEntry: {
QStringList names = name.split(QLatin1String("::"));
if (names.size() == 1) {
m_current_enum = new EnumTypeEntry(QString(), name);
}
else
m_current_enum =
new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"),
names.last());
element->entry = m_current_enum;
m_current_enum->setCodeGeneration(m_generate);
m_current_enum->setTargetLangPackage(m_defaultPackage);
m_current_enum->setUpperBound(attributes["upper-bound"]);
m_current_enum->setLowerBound(attributes["lower-bound"]);
m_current_enum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false));
m_current_enum->setExtensible(convertBoolean(attributes["extensible"], "extensible", false));
// put in the flags parallel...
if (!attributes["flags"].isEmpty() && attributes["flags"].toLower() != "no") {
FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">");
ftype->setOriginator(m_current_enum);
ftype->setOriginalName(attributes["flags"]);
ftype->setCodeGeneration(m_generate);
QString origname = ftype->originalName();
QStringList lst = origname.split("::");
if (QStringList(lst.mid(0, lst.size() - 1)).join("::") != m_current_enum->javaQualifier()) {
ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers")
.arg(m_current_enum->javaQualifier())
.arg(lst.at(0)));
}
ftype->setFlagsName(lst.last());
m_current_enum->setFlags(ftype);
m_database->addFlagsType(ftype);
m_database->addType(ftype);
}
}
break;
case StackElement::InterfaceTypeEntry:
{
ObjectTypeEntry *otype = new ObjectTypeEntry(name);
QString javaName = attributes["java-name"];
if (javaName.isEmpty())
javaName = name;
InterfaceTypeEntry *itype =
new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(javaName));
if (!convertBoolean(attributes["generate"], "generate", true))
itype->setCodeGeneration(TypeEntry::GenerateForSubclass);
else
itype->setCodeGeneration(m_generate);
otype->setDesignatedInterface(itype);
itype->setOrigin(otype);
element->entry = otype;
}
// fall through
case StackElement::NamespaceTypeEntry:
if (element->entry == 0) {
element->entry = new NamespaceTypeEntry(name);
}
// fall through
case StackElement::ObjectTypeEntry:
if (element->entry == 0) {
element->entry = new ObjectTypeEntry(name);
}
// fall through
case StackElement::ValueTypeEntry:
{
if (element->entry == 0) {
element->entry = new ValueTypeEntry(name);
}
ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
ctype->setTargetLangPackage(attributes["package"]);
ctype->setDefaultSuperclass(attributes["default-superclass"]);
ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false));
if (!convertBoolean(attributes["generate"], "generate", true))
element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass);
else
element->entry->setCodeGeneration(m_generate);
QString javaName = attributes["java-name"];
if (!javaName.isEmpty())
ctype->setTargetLangName(javaName);
// The expense policy
QString limit = attributes["expense-limit"];
if (!limit.isEmpty() && limit != "none") {
ExpensePolicy ep;
ep.limit = limit.toInt();
ep.cost = attributes["expense-cost"];
ctype->setExpensePolicy(ep);
}
ctype->setIsPolymorphicBase(convertBoolean(attributes["polymorphic-base"], "polymorphic-base", false));
ctype->setPolymorphicIdValue(attributes["polymorphic-id-expression"]);
if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) {
if (convertBoolean(attributes["force-abstract"], "force-abstract", false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
if (convertBoolean(attributes["deprecated"], "deprecated", false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
}
if (element->type == StackElement::InterfaceTypeEntry ||
element->type == StackElement::ValueTypeEntry ||
element->type == StackElement::ObjectTypeEntry) {
if (convertBoolean(attributes["delete-in-main-thread"], "delete-in-main-thread", false))
ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread);
}
QString targetType = attributes["target-type"];
if (!targetType.isEmpty() && element->entry->isComplex())
static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType);
// ctype->setInclude(Include(Include::IncludePath, ctype->name()));
ctype = ctype->designatedInterface();
if (ctype != 0)
ctype->setTargetLangPackage(attributes["package"]);
}
break;
default:
Q_ASSERT(false);
};
if (element->entry)
m_database->addType(element->entry);
else
ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name));
} else if (element->type != StackElement::None) {
bool topLevel = element->type == StackElement::Root
|| element->type == StackElement::SuppressedWarning
|| element->type == StackElement::Rejection
|| element->type == StackElement::LoadTypesystem
|| element->type == StackElement::InjectCode
|| element->type == StackElement::Template;
if (!topLevel && current->type == StackElement::Root) {
m_error = QString("Tag requires parent: '%1'").arg(tagName);
return false;
}
StackElement topElement = current==0 ? StackElement(0) : *current;
element->entry = topElement.entry;
QHash<QString, QString> attributes;
switch (element->type) {
case StackElement::Root:
attributes["package"] = QString();
attributes["default-superclass"] = QString();
break;
case StackElement::LoadTypesystem:
attributes["name"] = QString();
attributes["generate"] = "yes";
break;
case StackElement::NoNullPointers:
attributes["default-value"] = QString();
break;
case StackElement::SuppressedWarning:
attributes["text"] = QString();
break;
case StackElement::ReplaceDefaultExpression:
attributes["with"] = QString();
break;
case StackElement::DefineOwnership:
attributes["class"] = "java";
attributes["owner"] = "";
break;
case StackElement::ModifyFunction:
attributes["signature"] = QString();
attributes["access"] = QString();
attributes["remove"] = QString();
attributes["rename"] = QString();
attributes["deprecated"] = QString("no");
attributes["associated-to"] = QString();
attributes["virtual-slot"] = QString("no");
break;
case StackElement::ModifyArgument:
attributes["index"] = QString();
attributes["replace-value"] = QString();
attributes["invalidate-after-use"] = QString("no");
break;
case StackElement::ModifyField:
attributes["name"] = QString();
attributes["write"] = "true";
attributes["read"] = "true";
break;
case StackElement::Access:
attributes["modifier"] = QString();
break;
case StackElement::Include:
attributes["file-name"] = QString();
attributes["location"] = QString();
break;
case StackElement::CustomMetaConstructor:
attributes["name"] = topElement.entry->name().toLower() + "_create";
attributes["param-name"] = "copy";
break;
case StackElement::CustomMetaDestructor:
attributes["name"] = topElement.entry->name().toLower() + "_delete";
attributes["param-name"] = "copy";
break;
case StackElement::ReplaceType:
attributes["modified-type"] = QString();
break;
case StackElement::InjectCode:
attributes["class"] = "java";
attributes["position"] = "beginning";
break;
case StackElement::ConversionRule:
attributes["class"] = "";
break;
case StackElement::RejectEnumValue:
attributes["name"] = "";
break;
case StackElement::ArgumentMap:
attributes["index"] = "1";
attributes["meta-name"] = QString();
break;
case StackElement::Rename:
attributes["to"] = QString();
break;
case StackElement::Rejection:
attributes["class"] = "*";
attributes["function-name"] = "*";
attributes["field-name"] = "*";
attributes["enum-name"] = "*";
break;
case StackElement::Removal:
attributes["class"] = "all";
break;
case StackElement::Template:
attributes["name"] = QString();
break;
case StackElement::TemplateInstanceEnum:
attributes["name"] = QString();
break;
case StackElement::Replace:
attributes["from"] = QString();
attributes["to"] = QString();
break;
case StackElement::ReferenceCount:
attributes["action"] = QString();
attributes["variable-name"] = QString();
attributes["thread-safe"] = QString("no");
attributes["declare-variable"] = QString();
attributes["access"] = QString("private");
attributes["conditional"] = QString("");
break;
default:
; // nada
};
if (attributes.count() > 0)
fetchAttributeValues(tagName, atts, &attributes);
switch (element->type) {
case StackElement::Root:
m_defaultPackage = attributes["package"];
m_defaultSuperclass = attributes["default-superclass"];
element->type = StackElement::Root;
element->entry = new TypeSystemTypeEntry(m_defaultPackage);
TypeDatabase::instance()->addType(element->entry);
break;
case StackElement::LoadTypesystem:
{
QString name = attributes["name"];
if (name.isEmpty()) {
m_error = "No typesystem name specified";
return false;
}
if (!m_database->parseFile(name, convertBoolean(attributes["generate"], "generate", true))) {
m_error = QString("Failed to parse: '%1'").arg(name);
return false;
}
}
break;
case StackElement::RejectEnumValue: {
if (!m_current_enum) {
m_error = "<reject-enum-value> node must be used inside a <enum-type> node";
return false;
}
QString name = attributes["name"];
bool added = false;
if (!name.isEmpty()) {
added = true;
m_current_enum->addEnumValueRejection(name);
}
} break;
case StackElement::ReplaceType:
{
if (topElement.type != StackElement::ModifyArgument) {
m_error = "Type replacement can only be specified for argument modifications";
return false;
}
if (attributes["modified-type"].isEmpty()) {
m_error = "Type replacement requires 'modified-type' attribute";
return false;
}
m_function_mods.last().argument_mods.last().modified_type = attributes["modified-type"];
}
break;
case StackElement::ConversionRule:
{
if (topElement.type != StackElement::ModifyArgument) {
m_error = "Conversion rules can only be specified for argument modification";
return false;
}
static QHash<QString, TypeSystem::Language> languageNames;
if (languageNames.isEmpty()) {
languageNames["native"] = TypeSystem::NativeCode;
languageNames["shell"] = TypeSystem::ShellCode;
}
CodeSnip snip;
QString languageAttribute = attributes["class"].toLower();
TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
if (lang == TypeSystem::NoLanguage) {
m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
return false;
}
snip.language = lang;
m_function_mods.last().argument_mods.last().conversion_rules.append(snip);
}
break;
case StackElement::ModifyArgument:
{
if (topElement.type != StackElement::ModifyFunction) {
m_error = QString::fromLatin1("argument modification requires function"
" modification as parent, was %1")
.arg(topElement.type, 0, 16);
return false;
}
QString index = attributes["index"];
if (index == "return")
index = "0";
else if (index == "this")
index = "-1";
bool ok = false;
int idx = index.toInt(&ok);
if (!ok) {
m_error = QString("Cannot convert '%1' to integer").arg(index);
return false;
}
QString replace_value = attributes["replace-value"];
if (!replace_value.isEmpty() && idx != 0) {
m_error = QString("replace-value is only supported for return values (index=0).");
return false;
}
ArgumentModification argumentModification = ArgumentModification(idx);
argumentModification.replace_value = replace_value;
argumentModification.reset_after_use = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false);
m_function_mods.last().argument_mods.append(argumentModification);
}
break;
case StackElement::NoNullPointers:
{
if (topElement.type != StackElement::ModifyArgument) {
m_error = "no-null-pointer requires argument modification as parent";
return false;
}
m_function_mods.last().argument_mods.last().no_null_pointers = true;
if (m_function_mods.last().argument_mods.last().index == 0) {
m_function_mods.last().argument_mods.last().null_pointer_default_value = attributes["default-value"];
} else if (!attributes["default-value"].isEmpty()) {
ReportHandler::warning("default values for null pointer guards are only effective for return values");
}
}
break;
case StackElement::DefineOwnership:
{
if (topElement.type != StackElement::ModifyArgument) {
m_error = "define-ownership requires argument modification as parent";
return false;
}
static QHash<QString, TypeSystem::Language> languageNames;
if (languageNames.isEmpty()) {
languageNames["java"] = TypeSystem::TargetLangCode;
languageNames["shell"] = TypeSystem::ShellCode;
}
QString classAttribute = attributes["class"].toLower();
TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage);
if (lang == TypeSystem::NoLanguage) {
m_error = QString("unsupported class attribute: '%1'").arg(classAttribute);
return false;
}
static QHash<QString, TypeSystem::Ownership> ownershipNames;
if (ownershipNames.isEmpty()) {
ownershipNames["java"] = TypeSystem::TargetLangOwnership;
ownershipNames["c++"] = TypeSystem::CppOwnership;
ownershipNames["default"] = TypeSystem::DefaultOwnership;
}
QString ownershipAttribute = attributes["owner"].toLower();
TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership);
if (owner == TypeSystem::InvalidOwnership) {
m_error = QString("unsupported owner attribute: '%1'").arg(ownershipAttribute);
return false;
}
m_function_mods.last().argument_mods.last().ownerships[lang] = owner;
}
break;
case StackElement::SuppressedWarning:
if (attributes["text"].isEmpty())
ReportHandler::warning("Suppressed warning with no text specified");
else
m_database->addSuppressedWarning(attributes["text"]);
break;
case StackElement::ArgumentMap:
{
if (!(topElement.type & StackElement::CodeSnipMask)) {
m_error = "Argument maps requires code injection as parent";
return false;
}
bool ok;
int pos = attributes["index"].toInt(&ok);
if (!ok) {
m_error = QString("Can't convert position '%1' to integer")
.arg(attributes["position"]);
return false;
}
if (pos <= 0) {
m_error = QString("Argument position %1 must be a positive number").arg(pos);
return false;
}
QString meta_name = attributes["meta-name"];
if (meta_name.isEmpty()) {
ReportHandler::warning("Empty meta name in argument map");
}
if (topElement.type == StackElement::InjectCodeInFunction) {
m_function_mods.last().snips.last().argumentMap[pos] = meta_name;
} else {
ReportHandler::warning("Argument maps are only useful for injection of code "
"into functions.");
}
}
break;
case StackElement::Removal:
{
if (topElement.type != StackElement::ModifyFunction) {
m_error = "Function modification parent required";
return false;
}
static QHash<QString, TypeSystem::Language> languageNames;
if (languageNames.isEmpty()) {
languageNames["java"] = TypeSystem::TargetLangAndNativeCode;
languageNames["all"] = TypeSystem::All;
}
QString languageAttribute = attributes["class"].toLower();
TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
if (lang == TypeSystem::NoLanguage) {
m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
return false;
}
m_function_mods.last().removal = lang;
}
break;
case StackElement::Rename:
case StackElement::Access:
{
if (topElement.type != StackElement::ModifyField
&& topElement.type != StackElement::ModifyFunction) {
m_error = "Function or field modification parent required";
return false;
}
Modification *mod = 0;
if (topElement.type == StackElement::ModifyFunction)
mod = &m_function_mods.last();
else
mod = &m_field_mods.last();
QString modifier;
if (element->type == StackElement::Rename) {
modifier = "rename";
QString renamed_to = attributes["to"];
if (renamed_to.isEmpty()) {
m_error = "Rename modifier requires 'to' attribute";
return false;
}
if (topElement.type == StackElement::ModifyFunction)
mod->setRenamedTo(renamed_to);
else
mod->setRenamedTo(renamed_to);
} else {
modifier = attributes["modifier"].toLower();
}
if (modifier.isEmpty()) {
m_error = "No access modification specified";
return false;
}
static QHash<QString, FunctionModification::Modifiers> modifierNames;
if (modifierNames.isEmpty()) {
modifierNames["private"] = Modification::Private;
modifierNames["public"] = Modification::Public;
modifierNames["protected"] = Modification::Protected;
modifierNames["friendly"] = Modification::Friendly;
modifierNames["rename"] = Modification::Rename;
modifierNames["final"] = Modification::Final;
modifierNames["non-final"] = Modification::NonFinal;
}
if (!modifierNames.contains(modifier)) {
m_error = QString("Unknown access modifier: '%1'").arg(modifier);
return false;
}
mod->modifiers |= modifierNames[modifier];
}
break;
case StackElement::RemoveArgument:
if (topElement.type != StackElement::ModifyArgument) {
m_error = "Removing argument requires argument modification as parent";
return false;
}
m_function_mods.last().argument_mods.last().removed = true;
break;
case StackElement::ModifyField:
{
QString name = attributes["name"];
if (name.isEmpty())
break;
FieldModification fm;
fm.name = name;
fm.modifiers = 0;
QString read = attributes["read"];
QString write = attributes["write"];
if (read == "true") fm.modifiers |= FieldModification::Readable;
if (write == "true") fm.modifiers |= FieldModification::Writable;
m_field_mods << fm;
}
break;
case StackElement::ModifyFunction:
{
if (!(topElement.type & StackElement::ComplexTypeEntryMask)) {
m_error = QString::fromLatin1("Modify function requires complex type as parent"
", was=%1").arg(topElement.type, 0, 16);
return false;
}
QString signature = attributes["signature"];
signature = QMetaObject::normalizedSignature(signature.toLocal8Bit().constData());
if (signature.isEmpty()) {
m_error = "No signature for modified function";
return false;
}
FunctionModification mod;
mod.signature = signature;
QString access = attributes["access"].toLower();
if (!access.isEmpty()) {
if (access == QLatin1String("private"))
mod.modifiers |= Modification::Private;
else if (access == QLatin1String("protected"))
mod.modifiers |= Modification::Protected;
else if (access == QLatin1String("public"))
mod.modifiers |= Modification::Public;
else if (access == QLatin1String("final"))
mod.modifiers |= Modification::Final;
else if (access == QLatin1String("non-final"))
mod.modifiers |= Modification::NonFinal;
else {
m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
return false;
}
}
if (convertBoolean(attributes["deprecated"], "deprecated", false)) {
mod.modifiers |= Modification::Deprecated;
}
QString remove = attributes["remove"].toLower();
if (!remove.isEmpty()) {
if (remove == QLatin1String("all"))
mod.removal = TypeSystem::All;
else if (remove == QLatin1String("java"))
mod.removal = TypeSystem::TargetLangAndNativeCode;
else {
m_error = QString::fromLatin1("Bad removal type '%1'").arg(remove);
return false;
}
}
QString rename = attributes["rename"];
if (!rename.isEmpty()) {
mod.renamedToName = rename;
mod.modifiers |= Modification::Rename;
}
QString association = attributes["associated-to"];
if (!association.isEmpty())
mod.association = association;
mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0);
m_function_mods << mod;
}
break;
case StackElement::ReplaceDefaultExpression:
if (!(topElement.type & StackElement::ModifyArgument)) {
m_error = "Replace default expression only allowed as child of argument modification";
return false;
}
if (attributes["with"].isEmpty()) {
m_error = "Default expression replaced with empty string. Use remove-default-expression instead.";
return false;
}
m_function_mods.last().argument_mods.last().replaced_default_expression = attributes["with"];
break;
case StackElement::RemoveDefaultExpression:
m_function_mods.last().argument_mods.last().removed_default_expression = true;
break;
case StackElement::CustomMetaConstructor:
case StackElement::CustomMetaDestructor:
{
CustomFunction *func = new CustomFunction(attributes["name"]);
func->param_name = attributes["param-name"];
element->value.customFunction = func;
}
break;
case StackElement::ReferenceCount:
{
if (topElement.type != StackElement::ModifyArgument) {
m_error = "reference-count must be child of modify-argument";
return false;
}
ReferenceCount rc;
rc.threadSafe = convertBoolean(attributes["thread-safe"], "thread-safe", false);
static QHash<QString, ReferenceCount::Action> actions;
if (actions.isEmpty()) {
actions["add"] = ReferenceCount::Add;
actions["add-all"] = ReferenceCount::AddAll;
actions["remove"] = ReferenceCount::Remove;
actions["set"] = ReferenceCount::Set;
actions["ignore"] = ReferenceCount::Ignore;
}
rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid);
rc.variableName = attributes["variable-name"];
if (rc.action != ReferenceCount::Ignore && rc.variableName.isEmpty()) {
m_error = "variable-name attribute must be specified";
return false;
}
rc.declareVariable = attributes["declare-variable"];
rc.conditional = attributes["conditional"];
static QHash<QString, int> accessRights;
if (accessRights.isEmpty()) {
accessRights["private"] = ReferenceCount::Private;
accessRights["public"] = ReferenceCount::Public;
accessRights["protected"] = ReferenceCount::Protected;
accessRights["friendly"] = ReferenceCount::Friendly;
}
rc.access = accessRights.value(attributes["access"].toLower(), 0);
if (rc.access == 0) {
m_error = "unrecognized access value: " + attributes["access"];
return false;
}
if (rc.action == ReferenceCount::Invalid) {
m_error = "unrecognized value for action attribute. supported actions:";
foreach (QString action, actions.keys())
m_error += " " + action;
}
m_function_mods.last().argument_mods.last().referenceCounts.append(rc);
}
break;
case StackElement::InjectCode:
{
if (((topElement.type & StackElement::ComplexTypeEntryMask) == 0)
&& (topElement.type != StackElement::ModifyFunction)
&& (topElement.type != StackElement::Root)) {
m_error = "wrong parent type for code injection";
return false;
}
static QHash<QString, TypeSystem::Language> languageNames;
if (languageNames.isEmpty()) {
languageNames["java"] = TypeSystem::TargetLangCode;
languageNames["native"] = TypeSystem::NativeCode;
languageNames["shell"] = TypeSystem::ShellCode;
languageNames["shell-declaration"] = TypeSystem::ShellDeclaration;
languageNames["library-initializer"] = TypeSystem::PackageInitializer;
languageNames["destructor-function"] = TypeSystem::DestructorFunction;
languageNames["constructors"] = TypeSystem::Constructors;
languageNames["interface"] = TypeSystem::Interface;
languageNames["pywrap-cpp"] = TypeSystem::PyWrapperCode;
languageNames["pywrap-h"] = TypeSystem::PyWrapperDeclaration;
}
QString className = attributes["class"].toLower();
if (!languageNames.contains(className)) {
m_error = QString("Invalid class specifier: '%1'").arg(className);
return false;
}
static QHash<QString, CodeSnip::Position> positionNames;
if (positionNames.isEmpty()) {
positionNames["beginning"] = CodeSnip::Beginning;
positionNames["end"] = CodeSnip::End;
// QtScript
positionNames["prototype-initialization"] = CodeSnip::PrototypeInitialization;
positionNames["constructor-initialization"] = CodeSnip::ConstructorInitialization;
positionNames["constructor"] = CodeSnip::Constructor;
}
QString position = attributes["position"].toLower();
if (!positionNames.contains(position)) {
m_error = QString("Invalid position: '%1'").arg(position);
return false;
}
CodeSnip snip;
snip.language = languageNames[className];
snip.position = positionNames[position];
if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) {
m_error = "Interface code injections must be direct child of an interface type entry";
return false;
}
if (topElement.type == StackElement::ModifyFunction) {
FunctionModification mod = m_function_mods.last();
if (snip.language == TypeSystem::ShellDeclaration) {
m_error = "no function implementation in shell declaration in which to inject code";
return false;
}
m_function_mods.last().snips << snip;
element->type = StackElement::InjectCodeInFunction;
} else if (topElement.type == StackElement::Root) {
((TypeSystemTypeEntry *) element->entry)->snips << snip;
} else if (topElement.type != StackElement::Root) {
m_code_snips << snip;
}
}
break;
case StackElement::Include:
{
QString location = attributes["location"].toLower();
static QHash<QString, Include::IncludeType> locationNames;
if (locationNames.isEmpty()) {
locationNames["global"] = Include::IncludePath;
locationNames["local"] = Include::LocalPath;
locationNames["java"] = Include::TargetLangImport;
}
if (!locationNames.contains(location)) {
m_error = QString("Location not recognized: '%1'").arg(location);
return false;
}
Include::IncludeType loc = locationNames[location];
Include inc(loc, attributes["file-name"]);
ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
if (topElement.type & StackElement::ComplexTypeEntryMask) {
ctype->setInclude(inc);
} else if (topElement.type == StackElement::ExtraIncludes) {
ctype->addExtraInclude(inc);
} else {
m_error = "Only supported parents are complex types and extra-includes";
return false;
}
inc = ctype->include();
IncludeList lst = ctype->extraIncludes();
ctype = ctype->designatedInterface();
if (ctype != 0) {
ctype->setExtraIncludes(lst);
ctype->setInclude(inc);
}
}
break;
case StackElement::Rejection:
{
QString cls = attributes["class"];
QString function = attributes["function-name"];
QString field = attributes["field-name"];
QString enum_ = attributes["enum-name"];
if (cls == "*" && function == "*" && field == "*" && enum_ == "*") {
m_error = "bad reject entry, neither 'class', 'function-name' nor "
"'field' specified";
return false;
}
m_database->addRejection(cls, function, field, enum_);
}
break;
case StackElement::Template:
element->value.templateEntry = new TemplateEntry(attributes["name"]);
break;
case StackElement::TemplateInstanceEnum:
if (!(topElement.type & StackElement::CodeSnipMask) &&
(topElement.type != StackElement::Template) &&
(topElement.type != StackElement::CustomMetaConstructor) &&
(topElement.type != StackElement::CustomMetaDestructor) &&
(topElement.type != StackElement::ConversionRule))
{
m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule.";
return false;
}
element->value.templateInstance = new TemplateInstance(attributes["name"]);
break;
case StackElement::Replace:
if (topElement.type != StackElement::TemplateInstanceEnum) {
m_error = "Can only insert replace rules into insert-template.";
return false;
}
element->parent->value.templateInstance->addReplaceRule(attributes["from"],attributes["to"]);
break;
default:
break; // nada
};
}
current = element.release();
return true;
}
TypeDatabase *TypeDatabase::instance()
{
static TypeDatabase *db = new TypeDatabase();
return db;
}
TypeDatabase::TypeDatabase() : m_suppressWarnings(true)
{
addType(new StringTypeEntry("QString"));
StringTypeEntry *e = new StringTypeEntry("QLatin1String");
e->setPreferredConversion(false);
addType(e);
e = new StringTypeEntry("QStringRef");
e->setPreferredConversion(false);
addType(e);
e = new StringTypeEntry("QXmlStreamStringRef");
e->setPreferredConversion(false);
addType(e);
addType(new CharTypeEntry("QChar"));
CharTypeEntry *c = new CharTypeEntry("QLatin1Char");
c->setPreferredConversion(false);
addType(c);
{
VariantTypeEntry *qvariant = new VariantTypeEntry("QVariant");
qvariant->setCodeGeneration(TypeEntry::GenerateNothing);
addType(qvariant);
}
{
JObjectWrapperTypeEntry *wrapper = new JObjectWrapperTypeEntry("JObjectWrapper");
wrapper->setCodeGeneration(TypeEntry::GenerateNothing);
addType(wrapper);
}
addType(new ThreadTypeEntry());
addType(new VoidTypeEntry());
// Predefined containers...
addType(new ContainerTypeEntry("QList", ContainerTypeEntry::ListContainer));
addType(new ContainerTypeEntry("QStringList", ContainerTypeEntry::StringListContainer));
addType(new ContainerTypeEntry("QLinkedList", ContainerTypeEntry::LinkedListContainer));
addType(new ContainerTypeEntry("QVector", ContainerTypeEntry::VectorContainer));
addType(new ContainerTypeEntry("QStack", ContainerTypeEntry::StackContainer));
addType(new ContainerTypeEntry("QSet", ContainerTypeEntry::SetContainer));
addType(new ContainerTypeEntry("QMap", ContainerTypeEntry::MapContainer));
addType(new ContainerTypeEntry("QHash", ContainerTypeEntry::HashContainer));
addType(new ContainerTypeEntry("QPair", ContainerTypeEntry::PairContainer));
addType(new ContainerTypeEntry("QQueue", ContainerTypeEntry::QueueContainer));
addType(new ContainerTypeEntry("QMultiMap", ContainerTypeEntry::MultiMapContainer));
// Custom types...
// ### QtScript: no custom handling of QModelIndex for now
// addType(new QModelIndexTypeEntry());
addRemoveFunctionToTemplates(this);
}
bool TypeDatabase::parseFile(const QString &filename, bool generate)
{
QFile file(filename);
Q_ASSERT(file.exists());
QXmlInputSource source(&file);
int count = m_entries.size();
QXmlSimpleReader reader;
Handler handler(this, generate);
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);
bool ok = reader.parse(&source, false);
int newCount = m_entries.size();
ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries")
.arg(filename)
.arg(newCount - count));
return ok;
}
QString PrimitiveTypeEntry::javaObjectName() const
{
static QHash<QString, QString> table;
if (table.isEmpty()) {
table["boolean"] = "Boolean";
table["byte"] = "Byte";
table["char"] = "Character";
table["short"] = "Short";
table["int"] = "Integer";
table["long"] = "Long";
table["float"] = "Float";
table["double"] = "Double";
}
Q_ASSERT(table.contains(targetLangName()));
return table[targetLangName()];
}
ContainerTypeEntry *TypeDatabase::findContainerType(const QString &name)
{
QString template_name = name;
int pos = name.indexOf('<');
if (pos > 0)
template_name = name.left(pos);
TypeEntry *type_entry = findType(template_name);
if (type_entry && type_entry->isContainer())
return static_cast<ContainerTypeEntry *>(type_entry);
return 0;
}
PrimitiveTypeEntry *TypeDatabase::findTargetLangPrimitiveType(const QString &java_name)
{
foreach (QList<TypeEntry *> entries, m_entries.values()) {
foreach (TypeEntry *e, entries) {
if (e && e->isPrimitive()) {
PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry *>(e);
if (pe->targetLangName() == java_name && pe->preferredConversion())
return pe;
}
}
}
return 0;
}
IncludeList TypeDatabase::extraIncludes(const QString &className)
{
ComplexTypeEntry *typeEntry = findComplexType(className);
if (typeEntry != 0)
return typeEntry->extraIncludes();
else
return IncludeList();
}
QString Include::toString() const
{
if (type == IncludePath)
return "#include <" + name + '>';
else if (type == LocalPath)
return "#include \"" + name + "\"";
else
return "import " + name + ";";
}
QString Modification::accessModifierString() const
{
if (isPrivate()) return "private";
if (isProtected()) return "protected";
if (isPublic()) return "public";
if (isFriendly()) return "friendly";
return QString();
}
FunctionModificationList ComplexTypeEntry::functionModifications(const QString &signature) const
{
FunctionModificationList lst;
for (int i=0; i<m_function_mods.count(); ++i) {
FunctionModification mod = m_function_mods.at(i);
if (mod.signature == signature) {
lst << mod;
}
}
return lst;
}
FieldModification ComplexTypeEntry::fieldModification(const QString &name) const
{
for (int i=0; i<m_field_mods.size(); ++i)
if (m_field_mods.at(i).name == name)
return m_field_mods.at(i);
FieldModification mod;
mod.name = name;
mod.modifiers = FieldModification::Readable | FieldModification::Writable;
return mod;
}
QString ContainerTypeEntry::javaPackage() const
{
if (m_type == PairContainer)
return "com.trolltech.qt";
return "java.util";
}
QString ContainerTypeEntry::targetLangName() const
{
switch (m_type) {
case StringListContainer: return "List";
case ListContainer: return "List";
case LinkedListContainer: return "LinkedList";
case VectorContainer: return "List";
case StackContainer: return "Stack";
case QueueContainer: return "Queue";
case SetContainer: return "Set";
case MapContainer: return "SortedMap";
case MultiMapContainer: return "SortedMap";
case HashContainer: return "HashMap";
// case MultiHashCollectio: return "MultiHash";
case PairContainer: return "QPair";
default:
qWarning("bad type... %d", m_type);
break;
}
return QString();
}
QString ContainerTypeEntry::qualifiedCppName() const
{
if (m_type == StringListContainer)
return "QStringList";
return ComplexTypeEntry::qualifiedCppName();
}
QString EnumTypeEntry::javaQualifier() const
{
TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier);
if (te != 0)
return te->targetLangName();
else
return m_qualifier;
}
QString EnumTypeEntry::jniName() const
{
return "jint";
}
QString FlagsTypeEntry::jniName() const
{
return "jint";
}
void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue)
{
m_enum_redirections << EnumValueRedirection(rejected, usedValue);
}
QString EnumTypeEntry::enumValueRedirection(const QString &value) const
{
for (int i=0; i<m_enum_redirections.size(); ++i)
if (m_enum_redirections.at(i).rejected == value)
return m_enum_redirections.at(i).used;
return QString();
}
QString FlagsTypeEntry::qualifiedTargetLangName() const
{
return javaPackage() + "." + m_enum->javaQualifier() + "." + targetLangName();
}
void TypeDatabase::addRejection(const QString &class_name, const QString &function_name,
const QString &field_name, const QString &enum_name)
{
TypeRejection r;
r.class_name = class_name;
r.function_name = function_name;
r.field_name = field_name;
r.enum_name = enum_name;
m_rejections << r;
}
bool TypeDatabase::isClassRejected(const QString &class_name)
{
if (!m_rebuild_classes.isEmpty())
return !m_rebuild_classes.contains(class_name);
foreach (const TypeRejection &r, m_rejections)
if (r.class_name == class_name && r.function_name == "*" && r.field_name == "*" && r.enum_name == "*") {
return true;
}
return false;
}
bool TypeDatabase::isEnumRejected(const QString &class_name, const QString &enum_name)
{
foreach (const TypeRejection &r, m_rejections) {
if (r.enum_name == enum_name
&& (r.class_name == class_name || r.class_name == "*")) {
return true;
}
}
return false;
}
bool TypeDatabase::isFunctionRejected(const QString &class_name, const QString &function_name)
{
foreach (const TypeRejection &r, m_rejections)
if (r.function_name == function_name &&
(r.class_name == class_name || r.class_name == "*"))
return true;
return false;
}
bool TypeDatabase::isFieldRejected(const QString &class_name, const QString &field_name)
{
foreach (const TypeRejection &r, m_rejections)
if (r.field_name == field_name &&
(r.class_name == class_name || r.class_name == "*"))
return true;
return false;
}
FlagsTypeEntry *TypeDatabase::findFlagsType(const QString &name) const
{
FlagsTypeEntry *fte = (FlagsTypeEntry *) findType(name);
return fte ? fte : (FlagsTypeEntry *) m_flags_entries.value(name);
}
QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/) {
return QLatin1String("Global");
}
/*!
* The Visual Studio 2002 compiler doesn't support these symbols,
* which our typedefs unforntuatly expand to.
*/
QString fixCppTypeName(const QString &name)
{
if (name == "long long") return "qint64";
else if (name == "unsigned long long") return "quint64";
return name;
}
QString formattedCodeHelper(QTextStream &s, Indentor &indentor, QStringList &lines) {
bool multilineComment = false;
bool lastEmpty = true;
QString lastLine;
while (!lines.isEmpty()) {
const QString line = lines.takeFirst().trimmed();
if (line.isEmpty()) {
if (!lastEmpty)
s << endl;
lastEmpty = true;
continue;
} else {
lastEmpty = false;
}
if (line.startsWith("/*"))
multilineComment = true;
if (multilineComment) {
s << indentor;
if (line.startsWith("*"))
s << " ";
s << line << endl;
if (line.endsWith("*/"))
multilineComment = false;
} else if (line.startsWith("}")) {
return line;
} else if (line.endsWith("}")) {
s << indentor << line << endl;
return 0;
} else if(line.endsWith("{")) {
s << indentor << line << endl;
QString tmp;
{
Indentation indent(indentor);
tmp = formattedCodeHelper(s, indentor, lines);
}
if (!tmp.isNull()) {
s << indentor << tmp << endl;
}
lastLine = tmp;
continue;
} else {
s << indentor;
if (!lastLine.isEmpty() &&
!lastLine.endsWith(";") &&
!line.startsWith("@") &&
!line.startsWith("//") &&
!lastLine.startsWith("//") &&
!lastLine.endsWith("}") &&
!line.startsWith("{"))
s << " ";
s << line << endl;
}
lastLine = line;
}
return 0;
}
QTextStream &CodeSnip::formattedCode(QTextStream &s, Indentor &indentor) const
{
QStringList lst(code().split("\n"));
while (!lst.isEmpty()) {
QString tmp = formattedCodeHelper(s, indentor, lst);
if (!tmp.isNull()) {
s << indentor << tmp << endl;
}
}
s.flush();
return s;
}
QString TemplateInstance::expandCode() const{
TemplateEntry *templateEntry = TypeDatabase::instance()->findTemplate(m_name);
if(templateEntry){
QString res = templateEntry->code();
foreach(QString key, replaceRules.keys()){
res.replace(key, replaceRules[key]);
}
return "// TEMPLATE - " + m_name + " - START" + res + "// TEMPLATE - " + m_name + " - END";
}
else{
ReportHandler::warning("insert-template referring to non-existing template '" + m_name + "'");
}
return QString();
}
QString CodeSnipAbstract::code() const{
QString res;
foreach(CodeSnipFragment *codeFrag, codeList){
res.append(codeFrag->code());
}
return res;
}
QString CodeSnipFragment::code() const{
if(m_instance)
return m_instance->expandCode();
else
return m_code;
}
QString FunctionModification::toString() const
{
QString str = signature + QLatin1String("->");
if (modifiers & AccessModifierMask) {
switch (modifiers & AccessModifierMask) {
case Private: str += QLatin1String("private"); break;
case Protected: str += QLatin1String("protected"); break;
case Public: str += QLatin1String("public"); break;
case Friendly: str += QLatin1String("friendly"); break;
}
}
if (modifiers & Final) str += QLatin1String("final");
if (modifiers & NonFinal) str += QLatin1String("non-final");
if (modifiers & Readable) str += QLatin1String("readable");
if (modifiers & Writable) str += QLatin1String("writable");
if (modifiers & CodeInjection) {
foreach (CodeSnip s, snips) {
str += QLatin1String("\n//code injection:\n");
str += s.code();
}
}
if (modifiers & Rename) str += QLatin1String("renamed:") + renamedToName;
if (modifiers & Deprecated) str += QLatin1String("deprecate");
if (modifiers & ReplaceExpression) str += QLatin1String("replace-expression");
return str;
}
static void removeFunction(ComplexTypeEntry *e, const char *signature)
{
FunctionModification mod;
mod.signature = QMetaObject::normalizedSignature(signature);
mod.removal = TypeSystem::All;
e->addFunctionModification(mod);
}
static void injectCode(ComplexTypeEntry *e,
const char *signature,
const QByteArray &code,
const ArgumentMap &args)
{
CodeSnip snip;
snip.language = TypeSystem::NativeCode;
snip.position = CodeSnip::Beginning;
snip.addCode(QString::fromLatin1(code));
snip.argumentMap = args;
FunctionModification mod;
mod.signature = QMetaObject::normalizedSignature(signature);
mod.snips << snip;
mod.modifiers = Modification::CodeInjection;
e->addFunctionModification(mod);
}
static void addRemoveFunctionToTemplates(TypeDatabase *db)
{
ContainerTypeEntry *qvector = db->findContainerType(QLatin1String("QVector"));
removeFunction(qvector, "constData() const");
removeFunction(qvector, "data() const");
removeFunction(qvector, "data()");
removeFunction(qvector, "first()");
removeFunction(qvector, "last()");
removeFunction(qvector, "operator[](int)");
removeFunction(qvector, "operator[](int) const");
removeFunction(qvector, "operator=(QVector<T>)");
ContainerTypeEntry *qlist = db->findContainerType(QLatin1String("QList"));
removeFunction(qlist, "constData() const");
removeFunction(qlist, "data() const");
removeFunction(qlist, "data()");
removeFunction(qlist, "back()");
removeFunction(qlist, "front()");
removeFunction(qlist, "first()");
removeFunction(qlist, "last()");
removeFunction(qlist, "operator[](int)");
removeFunction(qlist, "operator[](int) const");
removeFunction(qlist, "operator=(QList<T>)");
ContainerTypeEntry *qqueue = db->findContainerType(QLatin1String("QQueue"));
removeFunction(qqueue, "head() const");
// QtScript: The next part is Java-specific, skip it for now...
return;
ArgumentMap args1;
args1[1] = QLatin1String("$1");
ArgumentMap args2 = args1;
args2[2] = QLatin1String("$2");
QByteArray code =
"\nif ($1 >= __qt_this->size() || $1 < 0) {"
"\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
"\n QString::fromLatin1(\"Accessing container of size %3 at %4\")"
"\n .arg(__qt_this->size()).arg($1).toLatin1());"
"\n return;"
"\n}";
QByteArray code_with_return = QByteArray(code).replace("return;", "return 0;");
QByteArray code_index_length =
"\nif ($1 < 0 || $2 < 0 || ($1 + $2) >= __qt_this->size()) {"
"\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
"\n QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
"\n .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
"\n return;"
"\n}";
QByteArray code_non_empty =
"\nif (__qt_this->isEmpty()) {"
"\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
"\n QString::fromLatin1(\"Accessing empty container...\").toLatin1());"
"\n return;"
"\n}";
QByteArray code_two_indices =
"\nif ($1 < 0 || $2 < 0 || $1 >= __qt_this->size() || $2 >= __qt_this->size()) {"
"\n __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
"\n QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
"\n .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
"\n return;"
"\n}";
{ // QVector safty...
injectCode(qvector, "at(int) const", code_with_return, args1);
injectCode(qvector, "replace(int,T)", code, args1);
injectCode(qvector, "remove(int)", code, args1);
injectCode(qvector, "remove(int, int)", code_index_length, args2);
injectCode(qvector, "pop_back()", code_non_empty, ArgumentMap());
injectCode(qvector, "pop_front()", code_non_empty, ArgumentMap());
}
{ // QList safty...
injectCode(qlist, "at(int) const", code_with_return, args1);
injectCode(qlist, "replace(int, T)", code, args1);
injectCode(qlist, "pop_back()", code_non_empty, ArgumentMap());
injectCode(qlist, "pop_front()", code_non_empty, ArgumentMap());
injectCode(qlist, "swap(int, int)", code_two_indices, args2);
injectCode(qlist, "move(int, int)", code_two_indices, args2);
injectCode(qlist, "removeAt(int)", code, args1);
injectCode(qlist, "takeAt(int)", code_with_return, args1);
}
}