From 9732e0c744e45a67094fc6ce08bdadb1f9a08d4a Mon Sep 17 00:00:00 2001 From: Hugo Lima Date: Mon, 17 Aug 2009 17:32:08 -0300 Subject: The genesis... --- typesystem.cpp | 2085 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2085 insertions(+) create mode 100644 typesystem.cpp (limited to 'typesystem.cpp') diff --git a/typesystem.cpp b/typesystem.cpp new file mode 100644 index 000000000..2a17d703b --- /dev/null +++ b/typesystem.cpp @@ -0,0 +1,2085 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "typesystem.h" +#include "generator.h" + +#include + +#include + +QString strings_Object = QLatin1String("Object"); +QString strings_String = QLatin1String("String"); +QString strings_Thread = QLatin1String("Thread"); +QString strings_char = QLatin1String("char"); +QString stringsJavaLang = QLatin1String("java.lang"); +QString strings_jchar = QLatin1String("jchar"); +QString strings_jobject = QLatin1String("jobject"); + +class StackElement +{ +public: + enum ElementType { + None = 0x0, + + // Type tags (0x1, ... , 0xff) + ObjectTypeEntry = 0x1, + ValueTypeEntry = 0x2, + InterfaceTypeEntry = 0x3, + NamespaceTypeEntry = 0x4, + ComplexTypeEntryMask = 0x7, + + // Non-complex type tags (0x8, 0x9, ... , 0xf) + PrimitiveTypeEntry = 0x8, + EnumTypeEntry = 0x9, + ContainerTypeEntry = 0xa, + TypeEntryMask = 0xf, + + // Documentation tags + InjectDocumentation = 0x10, + ModifyDocumentation = 0x20, + DocumentationMask = 0xf0, + + // 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, + Thread = 0x100000, + FunctionModifiers = 0xff0000, + + // Argument modifier tags (0x01000000 ... 0xf0000000) + ConversionRule = 0x01000000, + ReplaceType = 0x02000000, + ReplaceDefaultExpression = 0x04000000, + RemoveArgument = 0x08000000, + DefineOwnership = 0x10000000, + RemoveDefaultExpression = 0x20000000, + NoNullPointers = 0x40000000, +#if 0 + ReferenceCount = 0x80000000, +#endif + ParentOwner = 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_currentEnum = 0; + current = 0; + + tagNames["rejection"] = StackElement::Rejection; + tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry; + tagNames["container-type"] = StackElement::ContainerTypeEntry; + 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; +#if 0 + tagNames["reference-count"] = StackElement::ReferenceCount; +#endif + tagNames["parent"] = StackElement::ParentOwner; + tagNames["inject-documentation"] = StackElement::InjectDocumentation; + tagNames["modify-documentation"] = StackElement::ModifyDocumentation; + } + + 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_currentEnum; + + CodeSnipList m_codeSnips; + FunctionModificationList m_functionMods; + FieldModificationList m_fieldMods; + DocModificationList m_docModifications; + + QHash tagNames; + QString m_currentSignature; +}; + +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); + + 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(current->entry); + centry->setFunctionModifications(m_functionMods); + centry->setFieldModifications(m_fieldMods); + centry->setCodeSnips(m_codeSnips); + centry->setDocModification(m_docModifications); + + if (centry->designatedInterface()) { + centry->designatedInterface()->setCodeSnips(m_codeSnips); + centry->designatedInterface()->setFunctionModifications(m_functionMods); + } + m_codeSnips = CodeSnipList(); + m_functionMods = FunctionModificationList(); + m_fieldMods = FieldModificationList(); + m_docModifications = DocModificationList(); + } + 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: + current->entry->setDocModification(m_docModifications); + m_docModifications = DocModificationList(); + m_currentEnum = 0; + break; + case StackElement::Template: + m_database->addTemplate(current->value.templateEntry); + break; + case StackElement::TemplateInstanceEnum: + if (current->parent->type == StackElement::InjectCode) + m_codeSnips.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_functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(current->value.templateInstance); + else if (current->parent->type == StackElement::InjectCodeInFunction) + m_functionMods.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 + && current->parent->type == StackElement::ModifyArgument) { + m_functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch); + return true; + } + + if (current->parent) { + if ((current->type & StackElement::CodeSnipMask)) { + switch (current->parent->type) { + case StackElement::Root: + ((TypeSystemTypeEntry *) current->parent->entry)->codeSnips().last().addCode(ch); + break; + case StackElement::ModifyFunction: + m_functionMods.last().snips.last().addCode(ch); + m_functionMods.last().modifiers |= FunctionModification::CodeInjection; + break; + case StackElement::NamespaceTypeEntry: + case StackElement::ObjectTypeEntry: + case StackElement::ValueTypeEntry: + case StackElement::InterfaceTypeEntry: + m_codeSnips.last().addCode(ch); + break; + default: + Q_ASSERT(false); + }; + return true; + } + } + + if (current->type & StackElement::DocumentationMask) + m_docModifications.last().setCode(ch); + + 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 attributes; + attributes["name"] = QString(); + + switch (element->type) { + case StackElement::PrimitiveTypeEntry: + attributes["target-lang-name"] = QString(); + attributes["target-lang-api-name"] = QString(); + attributes["preferred-conversion"] = "yes"; + attributes["preferred-target-lang-type"] = "yes"; + break; + case StackElement::ContainerTypeEntry: + attributes["type"] = QString(); + 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["hash-function"] = QString(""); + // fall throooough + case StackElement::InterfaceTypeEntry: + attributes["default-superclass"] = m_defaultSuperclass; + attributes["polymorphic-id-expression"] = QString(); + attributes["delete-in-main-thread"] = QString("no"); + attributes["held-type"] = QString(); + attributes["copyable"] = QString(); + // fall through + case StackElement::NamespaceTypeEntry: + attributes["target-lang-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 target language + // 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) + 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 targetLangName = attributes["target-lang-name"]; + QString targetLangApiName = attributes["target-lang-api-name"]; + QString preferredConversion = attributes["preferred-conversion"].toLower(); + QString preferredTargetLangType = attributes["preferred-target-lang-type"].toLower(); + + if (targetLangName.isEmpty()) + targetLangName = name; + if (targetLangApiName.isEmpty()) + targetLangApiName = name; + + PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name); + type->setCodeGeneration(m_generate); + type->setTargetLangName(targetLangName); + type->setTargetLangApiName(targetLangApiName); + + bool preferred; + preferred = convertBoolean(preferredConversion, "preferred-conversion", true); + type->setPreferredConversion(preferred); + preferred = convertBoolean(preferredTargetLangType, + "preferred-target-lang-type", true); + type->setPreferredTargetLangType(preferred); + + element->entry = type; + } + break; + case StackElement::ContainerTypeEntry: + { + QString typeName = attributes["type"]; + ContainerTypeEntry::Type containerType = + ContainerTypeEntry::containerTypeFromString(typeName); + if (typeName.isEmpty()) { + m_error = "no 'type' attribute specified"; + return false; + } else if (containerType == ContainerTypeEntry::NoContainer) { + m_error = "there is no container of type " + containerType; + return false; + } + + ContainerTypeEntry *type = new ContainerTypeEntry(name, containerType); + type->setCodeGeneration(m_generate); + element->entry = type; + } + break; + case StackElement::EnumTypeEntry: { + QStringList names = name.split(QLatin1String("::")); + + if (names.size() == 1) + m_currentEnum = new EnumTypeEntry(QString(), name); + else + m_currentEnum = + new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"), + names.last()); + element->entry = m_currentEnum; + m_currentEnum->setCodeGeneration(m_generate); + m_currentEnum->setTargetLangPackage(m_defaultPackage); + m_currentEnum->setUpperBound(attributes["upper-bound"]); + m_currentEnum->setLowerBound(attributes["lower-bound"]); + m_currentEnum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false)); + m_currentEnum->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_currentEnum); + 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_currentEnum->targetLangQualifier()) { + ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers") + .arg(m_currentEnum->targetLangQualifier()) + .arg(lst.at(0))); + } + + ftype->setFlagsName(lst.last()); + m_currentEnum->setFlags(ftype); + + m_database->addFlagsType(ftype); + m_database->addType(ftype); + } + } + break; + + case StackElement::InterfaceTypeEntry: { + ObjectTypeEntry *otype = new ObjectTypeEntry(name); + QString targetLangName = attributes["target-lang-name"]; + if (targetLangName.isEmpty()) + targetLangName = name; + InterfaceTypeEntry *itype = + new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName)); + + 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) + element->entry = new NamespaceTypeEntry(name); + + // fall through + case StackElement::ObjectTypeEntry: + if (!element->entry) + element->entry = new ObjectTypeEntry(name); + + // fall through + case StackElement::ValueTypeEntry: { + if (!element->entry) + 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 targetLangName = attributes["target-lang-name"]; + if (!targetLangName.isEmpty()) + ctype->setTargetLangName(targetLangName); + + // 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"]); + //Copyable + if (attributes["copyable"].isEmpty()) + ctype->setCopyable(ComplexTypeEntry::Unknown); + else { + if (convertBoolean(attributes["copyable"], "copyable", false)) + ctype->setCopyable(ComplexTypeEntry::CopyableSet); + else + ctype->setCopyable(ComplexTypeEntry::NonCopyableSet); + + } + + if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) + ctype->setHashFunction(attributes["hash-function"]); + + + ctype->setHeldType(attributes["held-type"]); + + 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) + 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::InjectDocumentation) { + // check the XML tag attributes + QHash attributes; + attributes["mode"] = "replace"; + attributes["format"] = "native"; + + fetchAttributeValues(tagName, atts, &attributes); + + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (current->parent && current->parent->type & validParent) { + QString modeName = attributes["mode"]; + DocModification::Mode mode; + if (modeName == "append") { + mode = DocModification::Append; + } else if (modeName == "prepend") { + mode = DocModification::Prepend; + } else if (modeName == "replace") { + mode = DocModification::Replace; + } else { + m_error = "Unknow documentation injection mode: " + modeName; + return false; + } + + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; + languageNames["native"] = TypeSystem::NativeCode; + } + + QString format = attributes["format"].toLower(); + TypeSystem::Language lang = languageNames.value(format, TypeSystem::NoLanguage); + if (lang == TypeSystem::NoLanguage) { + m_error = QString("unsupported class attribute: '%1'").arg(format); + return false; + } + + QString signature = current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature; + DocModification mod(mode, signature); + mod.format = lang; + m_docModifications << mod; + } else { + m_error = "inject-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"; + return false; + } + } else if (element->type == StackElement::ModifyDocumentation) { + // check the XML tag attributes + QHash attributes; + attributes["xpath"] = QString(); + fetchAttributeValues(tagName, atts, &attributes); + const int validParent = StackElement::TypeEntryMask + | StackElement::ModifyFunction + | StackElement::ModifyField; + if (current->parent && current->parent->type & validParent) + m_docModifications << DocModification(attributes["xpath"], m_currentSignature); + else { + m_error = "modify-documentation must be inside modify-function, " + "modify-field or other tags that creates a type"; + return false; + } + } 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::ConversionRule + || element->type == StackElement::Template; + + if (!topLevel && current->type == StackElement::Root) { + m_error = QString("Tag requires parent: '%1'").arg(tagName); + return false; + } + + StackElement topElement = !current ? 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"] = "target"; + 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"); + attributes["thread"] = QString("no"); + attributes["allow-thread"] = 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"] = "target"; + attributes["position"] = "beginning"; + attributes["file"] = QString(); + break; + case StackElement::ConversionRule: + attributes["class"] = ""; + attributes["file"] = ""; + 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; +#if 0 + case StackElement::ReferenceCount: + attributes["action"] = QString(); + break; +#endif + case StackElement::ParentOwner: + attributes["index"] = QString(); + attributes["action"] = QString(); + 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); + + if ((m_generate == TypeEntry::GenerateForSubclass || + m_generate == TypeEntry::GenerateNothing) && m_defaultPackage != "") + TypeDatabase::instance()->addRequiredTargetImport(m_defaultPackage); + + + if (!element->entry->qualifiedCppName().isEmpty()) + m_database->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_currentEnum) { + m_error = " node must be used inside a node"; + return false; + } + QString name = attributes["name"]; + + bool added = false; + if (!name.isEmpty()) { + added = true; + m_currentEnum->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_functionMods.last().argument_mods.last().modified_type = attributes["modified-type"]; + } + break; + case StackElement::ConversionRule: { + if (topElement.type != StackElement::ModifyArgument + && topElement.type != StackElement::PrimitiveTypeEntry + && topElement.type != StackElement::ContainerTypeEntry) { + m_error = "Conversion rules can only be specified for argument modification" + " and to primitive or container types conversion."; + return false; + } + + if (topElement.type == StackElement::ModifyArgument) { + static QHash languageNames; + if (languageNames.isEmpty()) { + languageNames["target"] = TypeSystem::TargetLangCode; + languageNames["native"] = TypeSystem::NativeCode; + } + + 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(lang); + return false; + } + + CodeSnip snip; + snip.language = lang; + m_functionMods.last().argument_mods.last().conversion_rules.append(snip); + } else { + QString sourceFile = attributes["file"].toLower(); + if (sourceFile.isEmpty()) { + m_error = QString("'file' attribute required; the source file containing the" + " containing the conversion functions must be provided"); + return false; + } + + //Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass + && m_generate != TypeEntry::GenerateNothing) { + if (QFile::exists(sourceFile)) { + QFile conversionSource(sourceFile); + if (conversionSource.open(QIODevice::ReadOnly)) { + CodeSnip snip; + snip.addCode(conversionSource.readAll()); + topElement.entry->addCodeSnip(snip); + } + } else { + ReportHandler::warning("File containing conversion code for " + + topElement.entry->name() + + " type does not exist: " + + sourceFile); + } + } + + } + } + + 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) { + 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.resetAfterUse = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false); + m_functionMods.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_functionMods.last().argument_mods.last().noNullPointers = true; + if (!m_functionMods.last().argument_mods.last().index) + m_functionMods.last().argument_mods.last().nullPointerDefaultValue = 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["target"] = 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["target"] = 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_functionMods.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_functionMods.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["target"] = 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_functionMods.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_functionMods.last(); + else + mod = &m_fieldMods.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_functionMods.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_fieldMods << 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; + m_currentSignature = 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("target")) + 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.setIsThread(convertBoolean(attributes["thread"], "thread", false)); + mod.setAllowThread(convertBoolean(attributes["allow-thread"], "allow-thread", false)); + + mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0); + + m_functionMods << 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_functionMods.last().argument_mods.last().replacedDefaultExpression = attributes["with"]; + break; + case StackElement::RemoveDefaultExpression: + m_functionMods.last().argument_mods.last().removedDefaultExpression = true; + break; + case StackElement::CustomMetaConstructor: + case StackElement::CustomMetaDestructor: { + CustomFunction *func = new CustomFunction(attributes["name"]); + func->paramName = attributes["param-name"]; + element->value.customFunction = func; + } + break; +#if 0 + case StackElement::ReferenceCount: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "reference-count must be child of modify-argument"; + return false; + } + + ReferenceCount rc; + + 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); + + if (rc.action == ReferenceCount::Invalid) { + m_error = "unrecognized value for action attribute. supported actions:"; + foreach (QString action, actions.keys()) + m_error += " " + action; + } + + m_functionMods.last().argument_mods.last().referenceCounts.append(rc); + } + break; +#endif + + case StackElement::ParentOwner: { + if (topElement.type != StackElement::ModifyArgument) { + m_error = "parent-policy must be child of modify-argument"; + return false; + } + + ArgumentOwner ao; + + 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; + } + + static QHash actions; + if (actions.isEmpty()) { + actions["add"] = ArgumentOwner::Add; + actions["remove"] = ArgumentOwner::Remove; + } + + ao.action = actions.value(attributes["action"].toLower(), ArgumentOwner::Invalid); + if (!ao.action) { + m_error = QString("Invalid parent actionr"); + return false; + } + ao.index = idx; + m_functionMods.last().argument_mods.last().owner = ao; + } + break; + + + case StackElement::InjectCode: { + if (((!topElement.type & StackElement::ComplexTypeEntryMask)) + && (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["target"] = TypeSystem::TargetLangCode; // em algum lugar do cpp + languageNames["native"] = TypeSystem::NativeCode; // em algum lugar do cpp + languageNames["shell"] = TypeSystem::ShellCode; // coloca no header, mas antes da declaracao da classe + languageNames["shell-declaration"] = TypeSystem::ShellDeclaration; // coloca no header, dentro da declaracao da classe + 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 positionNames; + if (positionNames.isEmpty()) { + positionNames["beginning"] = CodeSnip::Beginning; + positionNames["end"] = CodeSnip::End; + // QtScript + positionNames["declaration"] = CodeSnip::Declaration; + 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]; + bool in_file = false; + + QString file_name = attributes["file"]; + + //Handler constructor.... + if (m_generate != TypeEntry::GenerateForSubclass && + m_generate != TypeEntry::GenerateNothing && + !file_name.isEmpty()) { + if (QFile::exists(file_name)) { + QFile code_file(file_name); + if (code_file.open(QIODevice::ReadOnly)) { + QString content; + content = code_file.readAll(); + content.prepend("// ========================================================================\n" + "// START of custom code block [file: " + file_name + "]\n"); + content.append("\n// END of custom code block [file: " + file_name + "]\n" + "// ========================================================================\n"); + snip.addCode(content); + in_file = true; + } + } else + ReportHandler::warning("File for inject code not exist: " + file_name); + + } + + 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_functionMods.last(); + if (snip.language == TypeSystem::ShellDeclaration) { + m_error = "no function implementation in shell declaration in which to inject code"; + return false; + } + + m_functionMods.last().snips << snip; + if (in_file) + m_functionMods.last().modifiers |= FunctionModification::CodeInjection; + element->type = StackElement::InjectCodeInFunction; + + } else if (topElement.type == StackElement::Root) { + ((TypeSystemTypeEntry *) element->entry)->addCodeSnip(snip); + + } else if (topElement.type != StackElement::Root) + m_codeSnips << 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["target"] = 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) { + 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) +{ + StringTypeEntry* e = new StringTypeEntry("QXmlStreamStringRef"); + e->setPreferredConversion(false); + addType(e); + + addType(new VoidTypeEntry()); +} + +QString TypeDatabase::modifiedTypesystemFilepath(const QString &ts_file) +{ + if (!QFile::exists(ts_file)) { + foreach (const QString &path, m_typesystemPaths) { + QString filepath(path + '/' + ts_file); + if (QFile::exists(filepath)) + return filepath; + } + } + return ts_file; +} + +bool TypeDatabase::parseFile(const QString &filename, bool generate) +{ + QString filepath; + if (filename[0] != '/') + filepath = modifiedTypesystemFilepath(filename); + else + filepath = filename; + + if (m_parsedTypesystemFiles.contains(filepath)) + return m_parsedTypesystemFiles[filepath]; + + QFile file(filepath); + Q_ASSERT_X(file.exists(), __FUNCTION__, ("Can't find " + filename).toLocal8Bit().data()); + + int count = m_entries.size(); + bool ok = parseFile(&file, generate); + m_parsedTypesystemFiles[filepath] = ok; + int newCount = m_entries.size(); + + ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries") + .arg(filename) + .arg(newCount - count)); + return ok; +} + +bool TypeDatabase::parseFile(QIODevice* device, bool generate) +{ + QXmlInputSource source(device); + QXmlSimpleReader reader; + Handler handler(this, generate); + + reader.setContentHandler(&handler); + reader.setErrorHandler(&handler); + + return reader.parse(&source, false); +} + +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()]; +} + +PrimitiveTypeEntry* PrimitiveTypeEntry::basicAliasedTypeEntry() const +{ + if (!m_aliasedTypeEntry) + return 0; + + PrimitiveTypeEntry* baseAliasTypeEntry = m_aliasedTypeEntry->basicAliasedTypeEntry(); + if (baseAliasTypeEntry) + return baseAliasTypeEntry; + else + return m_aliasedTypeEntry; +} + +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 &targetLangName) +{ + foreach (QList entries, m_entries.values()) { + foreach (TypeEntry *e, entries) { + if (e && e->isPrimitive()) { + PrimitiveTypeEntry *pe = static_cast(e); + if (pe->targetLangName() == targetLangName && pe->preferredConversion()) + return pe; + } + } + } + + return 0; +} + +IncludeList TypeDatabase::extraIncludes(const QString &className) +{ + ComplexTypeEntry *typeEntry = findComplexType(className); + if (typeEntry) + 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_functionMods.count(); ++i) { + const FunctionModification &mod = m_functionMods.at(i); + if (mod.signature == signature) + lst << mod; + + } + + return lst; +} + +FieldModification ComplexTypeEntry::fieldModification(const QString &name) const +{ + for (int i = 0; i < m_fieldMods.size(); ++i) + if (m_fieldMods.at(i).name == name) + return m_fieldMods.at(i); + FieldModification mod; + mod.name = name; + mod.modifiers = FieldModification::Readable | FieldModification::Writable; + return mod; +} + +QString ContainerTypeEntry::targetLangPackage() const +{ + if (m_type == PairContainer) + return "com.trolltech.qt"; + return "java.util"; +} + +QString ContainerTypeEntry::targetLangName() const +{ + + switch (m_type) { + case StringListContainer: return "QStringList"; + case ListContainer: return "QList"; + case LinkedListContainer: return "LinkedList"; + case VectorContainer: return "QVector"; + case StackContainer: return "QStack"; + case QueueContainer: return "QQueue"; + case SetContainer: return "QSet"; + case MapContainer: return "QMap"; + case MultiMapContainer: return "QMultiMap"; + case HashContainer: return "QHashMap"; + //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::targetLangQualifier() const +{ + TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier); + if (te) + return te->targetLangName(); + else + return m_qualifier; +} + +QString EnumTypeEntry::targetLangApiName() const +{ + return "jint"; +} + +QString FlagsTypeEntry::targetLangApiName() const +{ + return "jint"; +} + +void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue) +{ + m_enumRedirections << EnumValueRedirection(rejected, usedValue); +} + +QString EnumTypeEntry::enumValueRedirection(const QString &value) const +{ + for (int i = 0; i < m_enumRedirections.size(); ++i) + if (m_enumRedirections.at(i).rejected == value) + return m_enumRedirections.at(i).used; + return QString(); +} + +QString FlagsTypeEntry::qualifiedTargetLangName() const +{ + return targetLangPackage() + "." + m_enum->targetLangQualifier() + "." + 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_rebuildClasses.isEmpty()) + return !m_rebuildClasses.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_flagsEntries.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 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); +} +*/ + -- cgit v1.2.3