/**************************************************************************** ** ** 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 #include #include 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 *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 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 *acceptedAttributes) { Q_ASSERT(acceptedAttributes != 0); for (int i=0; icontains(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(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 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 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["create-shell"] = QString("yes"); attributes["create-promoter"] = QString("yes"); 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(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->setCreateShell(convertBoolean(attributes["create-shell"], "create-shell", false)); ctype->setCreatePromoter(convertBoolean(attributes["create-promoter"], "create-promoter", false)); 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(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 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 = " node must be used inside a 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 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 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 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 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 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 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 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 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; languageNames["pywrap-operators"] = TypeSystem::PyWrapperOperators; languageNames["pyshell-h"] = TypeSystem::PyShellDeclaration; languageNames["pyinheritshell-h"] = TypeSystem::PyInheritShellDeclaration; languageNames["pyinheritshell-constructor-code"] = TypeSystem::PyInheritShellConstructorCode; languageNames["pyinit-cpp"] = TypeSystem::PyInitSource; languageNames["pysetwrapperfunc"] = TypeSystem::PySetWrapperFunc; } QString className = attributes["class"].toLower(); if (!languageNames.contains(className)) { m_error = QString("Invalid class specifier: '%1'").arg(className); return false; } static QHash 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 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(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)); 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 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(type_entry); return 0; } PrimitiveTypeEntry *TypeDatabase::findTargetLangPrimitiveType(const QString &java_name) { foreach (QList entries, m_entries.values()) { foreach (TypeEntry *e, entries) { if (e && e->isPrimitive()) { PrimitiveTypeEntry *pe = static_cast(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; ifindType(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; ijavaQualifier() + "." + 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)"); 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)"); 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); } }