Mercurial > projects > qtd
diff generator/typesystem.cpp @ 1:e78566595089
initial import
author | mandel |
---|---|
date | Mon, 11 May 2009 16:01:50 +0000 |
parents | |
children | 0a29ce1ae854 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/generator/typesystem.cpp Mon May 11 16:01:50 2009 +0000 @@ -0,0 +1,2047 @@ +/**************************************************************************** +** +** Copyright (C) 1992-2008 Nokia. All rights reserved. +** +** This file is part of Qt Jambi. +** +** * Commercial Usage +* Licensees holding valid Qt Commercial licenses may use this file in +* accordance with the Qt Commercial License Agreement provided with the +* Software or, alternatively, in accordance with the terms contained in +* a written agreement between you and Nokia. +* +* +* GNU General Public License Usage +* Alternatively, this file may be used under the terms of the GNU +* General Public License versions 2.0 or 3.0 as published by the Free +* Software Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. In addition, as a special +* exception, Nokia gives you certain additional rights. These rights +* are described in the Nokia Qt GPL Exception version 1.2, included in +* the file GPL_EXCEPTION.txt in this package. +* +* Qt for Windows(R) Licensees +* As a special exception, Nokia, as the sole copyright holder for Qt +* Designer, grants users of the Qt/Eclipse Integration plug-in the +* right for the Qt/Eclipse Integration to link to functionality +* provided by Qt Designer and its related libraries. +* +* +* If you are unsure which license is appropriate for your use, please +* contact the sales department at qt-sales@nokia.com. + +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "typesystem.h" +#include "generator.h" + +#include "customtypes.h" + +#include <reporthandler.h> + +#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, + // qtd stuff + AddClass = 0x1100, + + // 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, + StoreResult = 0x110000, + + // 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; + // qtd + tagNames["add-class"] = StackElement::AddClass; + tagNames["store-result"] = StackElement::StoreResult; + } + + 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); + } + + 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"); + attributes["wrap"] = QString(); + // 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 n = ftype->originalName(); + + QStringList lst = n.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) { + if(name == "QVariant") + element->entry = new VariantTypeEntry(name); + else + 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)); + + // qtd + QString wrap = attributes["wrap"]; + ctype->setStructInD(wrap == "struct" ? true : 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; + // qtd + case StackElement::AddClass: + attributes["name"] = 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::StoreResult: + { + if (topElement.type != StackElement::ModifyFunction) { + m_error = QString::fromLatin1("result storage requires function" + " modification as parent, was %1") + .arg(topElement.type, 0, 16); + return false; + } + + m_function_mods.last().store_result = true; + } + 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::AddClass: // qtd - fully :) + { + if (!(topElement.type & StackElement::ComplexTypeEntryMask)) { + m_error = QString::fromLatin1("Add class to module requires complex type as parent" + ", was=%1").arg(topElement.type, 0, 16); + return false; + } + QString class_name = attributes["name"]; + ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(topElement.entry); + if(!class_name.isEmpty()) + ctype->includedClasses << class_name; + } + 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; + } + + 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; + } + + 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; + return true; +} + +TypeDatabase *TypeDatabase::instance() +{ + static TypeDatabase *db = new TypeDatabase(); + return db; +} + +TypeDatabase::TypeDatabase() : m_suppressWarnings(true), m_includeEclipseWarnings(false) +{ + 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::GenerateAll); + 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... +// 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 "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 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, + TypeSystem::Language lang = TypeSystem::NativeCode) +{ + CodeSnip snip; + snip.language = lang; + 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"); + + + 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}"; +/* qtd2 + { // QVector safty... + injectCode(qvector, "at(int) const", code_with_return, args1, TypeSystem::TargetLangCode); + injectCode(qvector, "replace(int,T)", code, args1, TypeSystem::TargetLangCode); + injectCode(qvector, "remove(int)", code, args1, TypeSystem::TargetLangCode); + injectCode(qvector, "remove(int, int)", code_index_length, args2, TypeSystem::TargetLangCode); + injectCode(qvector, "pop_back()", code_non_empty, ArgumentMap(), TypeSystem::TargetLangCode); + injectCode(qvector, "pop_front()", code_non_empty, ArgumentMap(), TypeSystem::TargetLangCode); + } + + { // 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); + } +*/ +}