aboutsummaryrefslogtreecommitdiffstats
path: root/ApiExtractor/typesystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ApiExtractor/typesystem.cpp')
-rw-r--r--ApiExtractor/typesystem.cpp2482
1 files changed, 2482 insertions, 0 deletions
diff --git a/ApiExtractor/typesystem.cpp b/ApiExtractor/typesystem.cpp
new file mode 100644
index 000000000..a08c2df2e
--- /dev/null
+++ b/ApiExtractor/typesystem.cpp
@@ -0,0 +1,2482 @@
+/*
+ * This file is part of the API Extractor project.
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: PySide team <contact@pyside.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "typesystem.h"
+#include "typesystem_p.h"
+#include "typedatabase.h"
+#include "reporthandler.h"
+#include <QtXml>
+
+static QString strings_Object = QLatin1String("Object");
+static QString strings_String = QLatin1String("String");
+static QString strings_char = QLatin1String("char");
+static QString strings_jchar = QLatin1String("jchar");
+static QString strings_jobject = QLatin1String("jobject");
+
+static QList<CustomConversion*> customConversionsForReview = QList<CustomConversion*>();
+
+Handler::Handler(TypeDatabase* database, bool generate)
+ : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass)
+{
+ m_currentEnum = 0;
+ m_current = 0;
+ m_currentDroppedEntry = 0;
+ m_currentDroppedEntryDepth = 0;
+ m_ignoreDepth = 0;
+
+ tagNames["rejection"] = StackElement::Rejection;
+ tagNames["custom-type"] = StackElement::CustomTypeEntry;
+ 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["function"] = StackElement::FunctionTypeEntry;
+ 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["native-to-target"] = StackElement::NativeToTarget;
+ tagNames["target-to-native"] = StackElement::TargetToNative;
+ tagNames["add-conversion"] = StackElement::AddConversion;
+ 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;
+ tagNames["parent"] = StackElement::ParentOwner;
+ tagNames["inject-documentation"] = StackElement::InjectDocumentation;
+ tagNames["modify-documentation"] = StackElement::ModifyDocumentation;
+ tagNames["add-function"] = StackElement::AddFunction;
+}
+
+bool Handler::error(const QXmlParseException &e)
+{
+ qWarning("Error: line=%d, column=%d, message=%s\n",
+ e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
+ return false;
+}
+
+bool Handler::fatalError(const QXmlParseException &e)
+{
+ qWarning("Fatal error: line=%d, column=%d, message=%s\n",
+ e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
+
+ return false;
+}
+
+bool Handler::warning(const QXmlParseException &e)
+{
+ qWarning("Warning: line=%d, column=%d, message=%s\n",
+ e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
+
+ return false;
+}
+
+void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
+ QHash<QString, QString> *acceptedAttributes)
+{
+ Q_ASSERT(acceptedAttributes);
+
+ 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 &)
+{
+ if (m_ignoreDepth) {
+ --m_ignoreDepth;
+ return true;
+ }
+
+ if (m_currentDroppedEntry) {
+ if (m_currentDroppedEntryDepth == 1) {
+ m_current = m_currentDroppedEntry->parent;
+ delete m_currentDroppedEntry;
+ m_currentDroppedEntry = 0;
+ m_currentDroppedEntryDepth = 0;
+ } else {
+ ++m_currentDroppedEntryDepth;
+ }
+ return true;
+ }
+
+ QString tagName = localName.toLower();
+ if (tagName == "import-file")
+ return true;
+
+ if (!m_current)
+ return true;
+
+ switch (m_current->type) {
+ case StackElement::Root:
+ if (m_generate == TypeEntry::GenerateAll) {
+ TypeDatabase::instance()->addGlobalUserFunctions(m_contextStack.top()->addedFunctions);
+ TypeDatabase::instance()->addGlobalUserFunctionModifications(m_contextStack.top()->functionMods);
+ foreach (CustomConversion* customConversion, customConversionsForReview) {
+ foreach (CustomConversion::TargetToNativeConversion* toNative, customConversion->targetToNativeConversions())
+ toNative->setSourceType(m_database->findType(toNative->sourceTypeName()));
+ }
+ }
+ break;
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ case StackElement::NamespaceTypeEntry: {
+ ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(m_current->entry);
+ centry->setAddedFunctions(m_contextStack.top()->addedFunctions);
+ centry->setFunctionModifications(m_contextStack.top()->functionMods);
+ centry->setFieldModifications(m_contextStack.top()->fieldMods);
+ centry->setCodeSnips(m_contextStack.top()->codeSnips);
+ centry->setDocModification(m_contextStack.top()->docModifications);
+
+ if (centry->designatedInterface()) {
+ centry->designatedInterface()->setCodeSnips(m_contextStack.top()->codeSnips);
+ centry->designatedInterface()->setFunctionModifications(m_contextStack.top()->functionMods);
+ }
+ }
+ break;
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion: {
+ CustomConversion* customConversion = static_cast<TypeEntry*>(m_current->entry)->customConversion();
+ if (!customConversion) {
+ m_error = "CustomConversion object is missing.";
+ return false;
+ }
+
+ QString code = m_contextStack.top()->codeSnips.takeLast().code();
+ if (m_current->type == StackElement::AddConversion) {
+ if (customConversion->targetToNativeConversions().isEmpty()) {
+ m_error = "CustomConversion's target to native conversions missing.";
+ return false;
+ }
+ customConversion->targetToNativeConversions().last()->setConversion(code);
+ } else {
+ customConversion->setNativeToTargetConversion(code);
+ }
+ }
+ break;
+ case StackElement::CustomMetaConstructor: {
+ m_current->entry->setCustomConstructor(*m_current->value.customFunction);
+ delete m_current->value.customFunction;
+ }
+ break;
+ case StackElement::CustomMetaDestructor: {
+ m_current->entry->setCustomDestructor(*m_current->value.customFunction);
+ delete m_current->value.customFunction;
+ }
+ break;
+ case StackElement::EnumTypeEntry:
+ m_current->entry->setDocModification(m_contextStack.top()->docModifications);
+ m_contextStack.top()->docModifications = DocModificationList();
+ m_currentEnum = 0;
+ break;
+ case StackElement::Template:
+ m_database->addTemplate(m_current->value.templateEntry);
+ break;
+ case StackElement::TemplateInstanceEnum:
+ switch (m_current->parent->type) {
+ case StackElement::InjectCode:
+ if (m_current->parent->parent->type == StackElement::Root) {
+ CodeSnipList snips = m_current->parent->entry->codeSnips();
+ CodeSnip snip = snips.takeLast();
+ snip.addTemplateInstance(m_current->value.templateInstance);
+ snips.append(snip);
+ m_current->parent->entry->setCodeSnips(snips);
+ break;
+ }
+ case StackElement::NativeToTarget:
+ case StackElement::AddConversion:
+ m_contextStack.top()->codeSnips.last().addTemplateInstance(m_current->value.templateInstance);
+ break;
+ case StackElement::Template:
+ m_current->parent->value.templateEntry->addTemplateInstance(m_current->value.templateInstance);
+ break;
+ case StackElement::CustomMetaConstructor:
+ case StackElement::CustomMetaDestructor:
+ m_current->parent->value.customFunction->addTemplateInstance(m_current->value.templateInstance);
+ break;
+ case StackElement::ConversionRule:
+ m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(m_current->value.templateInstance);
+ break;
+ case StackElement::InjectCodeInFunction:
+ m_contextStack.top()->functionMods.last().snips.last().addTemplateInstance(m_current->value.templateInstance);
+ break;
+ default:
+ break; // nada
+ };
+ break;
+ default:
+ break;
+ }
+
+ if (m_current->type == StackElement::Root
+ || m_current->type == StackElement::NamespaceTypeEntry
+ || m_current->type == StackElement::InterfaceTypeEntry
+ || m_current->type == StackElement::ObjectTypeEntry
+ || m_current->type == StackElement::ValueTypeEntry
+ || m_current->type == StackElement::PrimitiveTypeEntry) {
+ StackElementContext* context = m_contextStack.pop();
+ delete context;
+ }
+
+ StackElement *child = m_current;
+ m_current = m_current->parent;
+ delete(child);
+
+ return true;
+}
+
+bool Handler::characters(const QString &ch)
+{
+ if (m_currentDroppedEntry || m_ignoreDepth)
+ return true;
+
+ if (m_current->type == StackElement::Template) {
+ m_current->value.templateEntry->addCode(ch);
+ return true;
+ }
+
+ if (m_current->type == StackElement::CustomMetaConstructor || m_current->type == StackElement::CustomMetaDestructor) {
+ m_current->value.customFunction->addCode(ch);
+ return true;
+ }
+
+ if (m_current->type == StackElement::ConversionRule
+ && m_current->parent->type == StackElement::ModifyArgument) {
+ m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.last().addCode(ch);
+ return true;
+ }
+
+ if (m_current->type == StackElement::NativeToTarget || m_current->type == StackElement::AddConversion) {
+ m_contextStack.top()->codeSnips.last().addCode(ch);
+ return true;
+ }
+
+ if (m_current->parent) {
+ if ((m_current->type & StackElement::CodeSnipMask)) {
+ CodeSnipList snips;
+ switch (m_current->parent->type) {
+ case StackElement::Root:
+ snips = m_current->parent->entry->codeSnips();
+ snips.last().addCode(ch);
+ m_current->parent->entry->setCodeSnips(snips);
+ break;
+ case StackElement::ModifyFunction:
+ case StackElement::AddFunction:
+ m_contextStack.top()->functionMods.last().snips.last().addCode(ch);
+ m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection;
+ break;
+ case StackElement::NamespaceTypeEntry:
+ case StackElement::ObjectTypeEntry:
+ case StackElement::ValueTypeEntry:
+ case StackElement::InterfaceTypeEntry:
+ m_contextStack.top()->codeSnips.last().addCode(ch);
+ break;
+ default:
+ Q_ASSERT(false);
+ };
+ return true;
+ }
+ }
+
+ if (m_current->type & StackElement::DocumentationMask)
+ m_contextStack.top()->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;
+ }
+}
+
+static bool convertRemovalAttribute(const QString& removalAttribute, Modification& mod, QString& errorMsg)
+{
+ QString remove = removalAttribute.toLower();
+ if (!remove.isEmpty()) {
+ if (remove == QLatin1String("all")) {
+ mod.removal = TypeSystem::All;
+ } else if (remove == QLatin1String("target")) {
+ mod.removal = TypeSystem::TargetLangAndNativeCode;
+ } else {
+ errorMsg = QString::fromLatin1("Bad removal type '%1'").arg(remove);
+ return false;
+ }
+ }
+ return true;
+}
+
+static void getNamePrefixRecursive(StackElement* element, QStringList& names)
+{
+ if (!element->parent || !element->parent->entry)
+ return;
+ getNamePrefixRecursive(element->parent, names);
+ names << element->parent->entry->name();
+}
+
+static QString getNamePrefix(StackElement* element)
+{
+ QStringList names;
+ getNamePrefixRecursive(element, names);
+ return names.join(".");
+}
+
+// Returns empty string if there's no error.
+static QString checkSignatureError(const QString& signature, const QString& tag)
+{
+ QString funcName = signature.left(signature.indexOf('(')).trimmed();
+ static QRegExp whiteSpace("\\s");
+ if (!funcName.startsWith("operator ") && funcName.contains(whiteSpace)) {
+ return QString("Error in <%1> tag signature attribute '%2'.\n"
+ "White spaces aren't allowed in function names, "
+ "and return types should not be part of the signature.")
+ .arg(tag)
+ .arg(signature);
+ }
+ return QString();
+}
+
+bool Handler::startElement(const QString &, const QString &n,
+ const QString &, const QXmlAttributes &atts)
+{
+ if (m_ignoreDepth) {
+ ++m_ignoreDepth;
+ return true;
+ }
+
+ if (!m_defaultPackage.isEmpty() && atts.index("since") != -1) {
+ TypeDatabase* td = TypeDatabase::instance();
+ if (!td->checkApiVersion(m_defaultPackage, atts.value("since").toAscii())) {
+ ++m_ignoreDepth;
+ return true;
+ }
+ }
+
+
+ QString tagName = n.toLower();
+ if (tagName == "import-file")
+ return importFileElement(atts);
+
+ if (!tagNames.contains(tagName)) {
+ m_error = QString("Unknown tag name: '%1'").arg(tagName);
+ return false;
+ }
+
+ if (m_currentDroppedEntry) {
+ ++m_currentDroppedEntryDepth;
+ return true;
+ }
+
+ StackElement* element = new StackElement(m_current);
+ element->type = tagNames[tagName];
+
+ if (element->type == StackElement::Root && m_generate == TypeEntry::GenerateAll)
+ customConversionsForReview.clear();
+
+ if (element->type == StackElement::Root
+ || element->type == StackElement::NamespaceTypeEntry
+ || element->type == StackElement::InterfaceTypeEntry
+ || element->type == StackElement::ObjectTypeEntry
+ || element->type == StackElement::ValueTypeEntry
+ || element->type == StackElement::PrimitiveTypeEntry) {
+ m_contextStack.push(new StackElementContext());
+ }
+
+ if (element->type & StackElement::TypeEntryMask) {
+ QHash<QString, QString> attributes;
+ attributes["name"] = QString();
+ attributes["since"] = QString("0");
+ attributes["revision"] = QString("0");
+
+ 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";
+ attributes["default-constructor"] = QString();
+ break;
+ case StackElement::ContainerTypeEntry:
+ attributes["type"] = QString();
+ break;
+ case StackElement::EnumTypeEntry:
+ attributes["flags"] = QString();
+ attributes["flags-revision"] = QString();
+ attributes["upper-bound"] = QString();
+ attributes["lower-bound"] = QString();
+ attributes["force-integer"] = "no";
+ attributes["extensible"] = "no";
+ attributes["identified-by-value"] = QString();
+ break;
+ case StackElement::ValueTypeEntry:
+ attributes["default-constructor"] = QString();
+ // fall throooough
+ case StackElement::ObjectTypeEntry:
+ attributes["force-abstract"] = QString("no");
+ attributes["deprecated"] = QString("no");
+ attributes["hash-function"] = QString("");
+ attributes["stream"] = QString("no");
+ // 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;
+ case StackElement::FunctionTypeEntry:
+ attributes["signature"] = QString();
+ attributes["rename"] = QString();
+ break;
+ default:
+ { } // nada
+ };
+
+ fetchAttributeValues(tagName, atts, &attributes);
+ QString name = attributes["name"];
+ double since = attributes["since"].toDouble();
+
+ if (m_database->hasDroppedTypeEntries()) {
+ QString identifier = getNamePrefix(element) + '.';
+ identifier += (element->type == StackElement::FunctionTypeEntry ? attributes["signature"] : name);
+ if (m_database->shouldDropTypeEntry(identifier)) {
+ m_currentDroppedEntry = element;
+ m_currentDroppedEntryDepth = 1;
+ ReportHandler::debugSparse(QString("Type system entry '%1' was intentionally dropped from generation.").arg(identifier));
+ return true;
+ }
+ }
+
+ // The top level tag 'function' has only the 'signature' tag
+ // and we should extract the 'name' value from it.
+ if (element->type == StackElement::FunctionTypeEntry) {
+ QString signature = attributes["signature"];
+ name = signature.left(signature.indexOf('(')).trimmed();
+ QString errorString = checkSignatureError(signature, "function");
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+ QString rename = attributes["rename"];
+ if (!rename.isEmpty()) {
+ static QRegExp functionNameRegExp("^[a-zA-Z_][a-zA-Z0-9_]*$");
+ if (!functionNameRegExp.exactMatch(rename)) {
+ m_error = "can not rename '" + signature + "', '" + rename + "' is not a valid function name";
+ return false;
+ }
+ FunctionModification mod(since);
+ mod.signature = signature;
+ mod.renamedToName = attributes["rename"];
+ mod.modifiers |= Modification::Rename;
+ m_contextStack.top()->functionMods << mod;
+ }
+ }
+
+ // 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
+ && element->type != StackElement::FunctionTypeEntry) {
+ TypeEntry *tmp = m_database->findType(name);
+ if (tmp)
+ ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name));
+ }
+
+ if (element->type == StackElement::EnumTypeEntry) {
+ if (name.isEmpty()) {
+ name = attributes["identified-by-value"];
+ } else if (!attributes["identified-by-value"].isEmpty()) {
+ m_error = "can't specify both 'name' and 'identified-by-value' attributes";
+ return false;
+ }
+ }
+
+ // Fix type entry name using nesting information.
+ if (element->type & StackElement::TypeEntryMask
+ && element->parent && element->parent->type != StackElement::Root) {
+ name = element->parent->entry->name() + "::" + name;
+ }
+
+
+ if (name.isEmpty()) {
+ m_error = "no 'name' attribute specified";
+ return false;
+ }
+
+ switch (element->type) {
+ case StackElement::CustomTypeEntry:
+ element->entry = new TypeEntry(name, TypeEntry::CustomType, since);
+ break;
+ 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();
+ QString defaultConstructor = attributes["default-constructor"];
+
+ if (targetLangName.isEmpty())
+ targetLangName = name;
+ if (targetLangApiName.isEmpty())
+ targetLangApiName = name;
+
+ PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name, since);
+ type->setCodeGeneration(m_generate);
+ type->setTargetLangName(targetLangName);
+ type->setTargetLangApiName(targetLangApiName);
+ type->setTargetLangPackage(m_defaultPackage);
+ type->setDefaultConstructor(defaultConstructor);
+
+ 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, since);
+ 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, since);
+ else
+ m_currentEnum =
+ new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"),
+ names.last(), since);
+ m_currentEnum->setAnonymous(!attributes["identified-by-value"].isEmpty());
+ 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...
+ QString flagName = attributes["flags"];
+ if (!flagName.isEmpty()) {
+ FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">", since);
+ ftype->setOriginator(m_currentEnum);
+ // Try to get the guess the qualified flag name
+ if (!flagName.contains("::") && names.count() > 1) {
+ QStringList cpy(names);
+ cpy.removeLast();
+ cpy.append(flagName);
+ flagName = cpy.join("::");
+ }
+
+ ftype->setOriginalName(flagName);
+ 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);
+
+ QString revision = attributes["flags-revision"].isEmpty() ? attributes["revision"] : attributes["flags-revision"];
+ setTypeRevision(ftype, revision.toInt());
+ }
+ }
+ break;
+
+ case StackElement::InterfaceTypeEntry: {
+ ObjectTypeEntry *otype = new ObjectTypeEntry(name, since);
+ QString targetLangName = attributes["target-lang-name"];
+ if (targetLangName.isEmpty())
+ targetLangName = name;
+ InterfaceTypeEntry *itype =
+ new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(targetLangName), since);
+
+ 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::ValueTypeEntry: {
+ if (!element->entry) {
+ ValueTypeEntry* typeEntry = new ValueTypeEntry(name, since);
+ QString defaultConstructor = attributes["default-constructor"];
+ if (!defaultConstructor.isEmpty())
+ typeEntry->setDefaultConstructor(defaultConstructor);
+ element->entry = typeEntry;
+ }
+
+ // fall through
+ case StackElement::NamespaceTypeEntry:
+ if (!element->entry)
+ element->entry = new NamespaceTypeEntry(name, since);
+
+ // fall through
+ case StackElement::ObjectTypeEntry:
+ if (!element->entry)
+ element->entry = new ObjectTypeEntry(name, since);
+
+ element->entry->setStream(attributes["stream"] == QString("yes"));
+
+ ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
+ ctype->setTargetLangPackage(attributes["package"]);
+ ctype->setDefaultSuperclass(attributes["default-superclass"]);
+ ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false));
+
+ if (!convertBoolean(attributes["generate"], "generate", true))
+ element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass);
+ else
+ element->entry->setCodeGeneration(m_generate);
+
+ QString 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<ComplexTypeEntry *>(element->entry)->setTargetType(targetType);
+
+ // ctype->setInclude(Include(Include::IncludePath, ctype->name()));
+ ctype = ctype->designatedInterface();
+ if (ctype)
+ ctype->setTargetLangPackage(attributes["package"]);
+
+ }
+ break;
+ case StackElement::FunctionTypeEntry: {
+ QString signature = attributes["signature"];
+ signature = TypeDatabase::normalizedSignature(signature.toLatin1().constData());
+ element->entry = m_database->findType(name);
+ if (element->entry) {
+ if (element->entry->type() == TypeEntry::FunctionType) {
+ reinterpret_cast<FunctionTypeEntry*>(element->entry)->addSignature(signature);
+ } else {
+ m_error = QString("%1 expected to be a function, but isn't! Maybe it was already declared as a class or something else.").arg(name);
+ return false;
+ }
+ } else {
+ element->entry = new FunctionTypeEntry(name, signature, since);
+ element->entry->setCodeGeneration(m_generate);
+ }
+ }
+ break;
+ default:
+ Q_ASSERT(false);
+ };
+
+ if (element->entry) {
+ m_database->addType(element->entry);
+ setTypeRevision(element->entry, attributes["revision"].toInt());
+ } else {
+ ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name));
+ }
+
+ } else if (element->type == StackElement::InjectDocumentation) {
+ // check the XML tag attributes
+ QHash<QString, QString> attributes;
+ attributes["mode"] = "replace";
+ attributes["format"] = "native";
+ attributes["since"] = QString("0");
+
+ fetchAttributeValues(tagName, atts, &attributes);
+ double since = attributes["since"].toDouble();
+
+ const int validParent = StackElement::TypeEntryMask
+ | StackElement::ModifyFunction
+ | StackElement::ModifyField;
+ if (m_current->parent && m_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<QString, TypeSystem::Language> 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 = m_current->type & StackElement::TypeEntryMask ? QString() : m_currentSignature;
+ DocModification mod(mode, signature, since);
+ mod.format = lang;
+ m_contextStack.top()->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<QString, QString> attributes;
+ attributes["xpath"] = QString();
+ attributes["since"] = QString("0");
+ fetchAttributeValues(tagName, atts, &attributes);
+ double since = attributes["since"].toDouble();
+
+ const int validParent = StackElement::TypeEntryMask
+ | StackElement::ModifyFunction
+ | StackElement::ModifyField;
+ if (m_current->parent && m_current->parent->type & validParent) {
+ QString signature = (m_current->type & StackElement::TypeEntryMask) ? QString() : m_currentSignature;
+ m_contextStack.top()->docModifications << DocModification(attributes["xpath"], signature, since);
+ } 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::ExtraIncludes
+ || element->type == StackElement::ConversionRule
+ || element->type == StackElement::AddFunction
+ || element->type == StackElement::Template;
+
+ if (!topLevel && m_current->type == StackElement::Root) {
+ m_error = QString("Tag requires parent: '%1'").arg(tagName);
+ return false;
+ }
+
+ StackElement topElement = !m_current ? StackElement(0) : *m_current;
+ element->entry = topElement.entry;
+
+ QHash<QString, QString> attributes;
+ attributes["since"] = QString("0");
+ 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::AddFunction:
+ attributes["signature"] = QString();
+ attributes["return-type"] = QString("void");
+ attributes["access"] = QString("public");
+ attributes["static"] = QString("no");
+ 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";
+ attributes["remove"] = QString();
+ 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"] = QString();
+ attributes["file"] = QString();
+ break;
+ case StackElement::TargetToNative:
+ attributes["replace"] = QString("yes");
+ break;
+ case StackElement::AddConversion:
+ attributes["type"] = QString();
+ attributes["check"] = QString();
+ 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();
+ break;
+ case StackElement::ParentOwner:
+ attributes["index"] = QString();
+ attributes["action"] = QString();
+ default:
+ { };
+ };
+
+ double since = 0;
+ if (attributes.count() > 0) {
+ fetchAttributeValues(tagName, atts, &attributes);
+ since = attributes["since"].toDouble();
+ }
+
+ switch (element->type) {
+ case StackElement::Root:
+ m_defaultPackage = attributes["package"];
+ m_defaultSuperclass = attributes["default-superclass"];
+ element->type = StackElement::Root;
+ {
+ TypeSystemTypeEntry* moduleEntry = reinterpret_cast<TypeSystemTypeEntry*>(
+ m_database->findType(m_defaultPackage));
+ element->entry = moduleEntry ? moduleEntry : new TypeSystemTypeEntry(m_defaultPackage, since);
+ element->entry->setCodeGeneration(m_generate);
+ }
+
+ 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;
+ }
+ bool generateChild = (convertBoolean(attributes["generate"], "generate", true) && (m_generate == TypeEntry::GenerateAll));
+ if (!m_database->parseFile(name, generateChild)) {
+ m_error = QString("Failed to parse: '%1'").arg(name);
+ return false;
+ }
+ }
+ break;
+ case StackElement::RejectEnumValue: {
+ if (!m_currentEnum) {
+ m_error = "<reject-enum-value> node must be used inside a <enum-type> node";
+ return false;
+ }
+ QString name = attributes["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_contextStack.top()->functionMods.last().argument_mods.last().modified_type = attributes["modified-type"];
+ }
+ break;
+ case StackElement::ConversionRule: {
+ if (topElement.type != StackElement::ModifyArgument
+ && topElement.type != StackElement::ValueTypeEntry
+ && topElement.type != StackElement::PrimitiveTypeEntry
+ && topElement.type != StackElement::ContainerTypeEntry) {
+ m_error = "Conversion rules can only be specified for argument modification, "
+ "value-type, primitive-type or container-type conversion.";
+ return false;
+ }
+
+ static QHash<QString, TypeSystem::Language> 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 (topElement.type == StackElement::ModifyArgument) {
+ if (lang == TypeSystem::NoLanguage) {
+ m_error = QString("unsupported class attribute: '%1'").arg(lang);
+ return false;
+ }
+
+ CodeSnip snip(since);
+ snip.language = lang;
+ m_contextStack.top()->functionMods.last().argument_mods.last().conversion_rules.append(snip);
+ } else {
+ if (topElement.entry->hasConversionRule() || topElement.entry->hasCustomConversion()) {
+ m_error = "Types can have only one conversion rule";
+ return false;
+ }
+
+ // The old conversion rule tag that uses a file containing the conversion
+ // will be kept temporarily for compatibility reasons.
+ QString sourceFile = attributes["file"];
+ if (!sourceFile.isEmpty()) {
+ if (m_generate != TypeEntry::GenerateForSubclass
+ && m_generate != TypeEntry::GenerateNothing) {
+
+ const char* conversionFlag = NATIVE_CONVERSION_RULE_FLAG;
+ if (lang == TypeSystem::TargetLangCode)
+ conversionFlag = TARGET_CONVERSION_RULE_FLAG;
+
+ QFile conversionSource(sourceFile);
+ if (conversionSource.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ topElement.entry->setConversionRule(conversionFlag + QString::fromUtf8(conversionSource.readAll()));
+ } else {
+ ReportHandler::warning("File containing conversion code for "
+ + topElement.entry->name()
+ + " type does not exist or is not readable: "
+ + sourceFile);
+ }
+ }
+ }
+
+ CustomConversion* customConversion = new CustomConversion(static_cast<TypeEntry*>(m_current->entry));
+ customConversionsForReview.append(customConversion);
+ }
+ }
+ break;
+ case StackElement::NativeToTarget: {
+ if (topElement.type != StackElement::ConversionRule) {
+ m_error = "Native to Target conversion code can only be specified for custom conversion rules.";
+ return false;
+ }
+ m_contextStack.top()->codeSnips << CodeSnip(0);
+ }
+ break;
+ case StackElement::TargetToNative: {
+ if (topElement.type != StackElement::ConversionRule) {
+ m_error = "Target to Native conversions can only be specified for custom conversion rules.";
+ return false;
+ }
+ bool replace = attributes["replace"] == "yes";
+ static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace);
+ }
+ break;
+ case StackElement::AddConversion: {
+ if (topElement.type != StackElement::TargetToNative) {
+ m_error = "Target to Native conversions can only be added inside 'target-to-native' tags.";
+ return false;
+ }
+ QString sourceTypeName = attributes["type"];
+ if (sourceTypeName.isEmpty()) {
+ m_error = "Target to Native conversions must specify the input type with the 'type' attribute.";
+ return false;
+ }
+ QString typeCheck = attributes["check"];
+ static_cast<TypeEntry*>(m_current->entry)->customConversion()->addTargetToNativeConversion(sourceTypeName, typeCheck);
+ m_contextStack.top()->codeSnips << CodeSnip(0);
+ }
+ break;
+ case StackElement::ModifyArgument: {
+ if (topElement.type != StackElement::ModifyFunction
+ && topElement.type != StackElement::AddFunction) {
+ 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, since);
+ argumentModification.replace_value = replace_value;
+ argumentModification.resetAfterUse = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false);
+ m_contextStack.top()->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_contextStack.top()->functionMods.last().argument_mods.last().noNullPointers = true;
+ if (!m_contextStack.top()->functionMods.last().argument_mods.last().index)
+ m_contextStack.top()->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<QString, TypeSystem::Language> languageNames;
+ if (languageNames.isEmpty()) {
+ languageNames["target"] = TypeSystem::TargetLangCode;
+ languageNames["native"] = TypeSystem::NativeCode;
+ }
+
+ QString classAttribute = attributes["class"].toLower();
+ TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage);
+ if (lang == TypeSystem::NoLanguage) {
+ m_error = QString("unsupported class attribute: '%1'").arg(classAttribute);
+ return false;
+ }
+
+ static QHash<QString, TypeSystem::Ownership> ownershipNames;
+ if (ownershipNames.isEmpty()) {
+ ownershipNames["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_contextStack.top()->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_contextStack.top()->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<QString, TypeSystem::Language> 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_contextStack.top()->functionMods.last().removal = lang;
+ }
+ break;
+ case StackElement::Rename:
+ case StackElement::Access: {
+ if (topElement.type != StackElement::ModifyField
+ && topElement.type != StackElement::ModifyFunction
+ && topElement.type != StackElement::ModifyArgument) {
+ m_error = "Function, field or argument modification parent required";
+ return false;
+ }
+
+ Modification *mod = 0;
+ if (topElement.type == StackElement::ModifyFunction)
+ mod = &m_contextStack.top()->functionMods.last();
+ else if (topElement.type == StackElement::ModifyField)
+ mod = &m_contextStack.top()->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 if (topElement.type == StackElement::ModifyField)
+ mod->setRenamedTo(renamed_to);
+ else
+ m_contextStack.top()->functionMods.last().argument_mods.last().renamed_to = renamed_to;
+ } else
+ modifier = attributes["modifier"].toLower();
+
+
+ if (modifier.isEmpty()) {
+ m_error = "No access modification specified";
+ return false;
+ }
+
+ static QHash<QString, FunctionModification::Modifiers> modifierNames;
+ if (modifierNames.isEmpty()) {
+ modifierNames["private"] = Modification::Private;
+ modifierNames["public"] = Modification::Public;
+ modifierNames["protected"] = Modification::Protected;
+ modifierNames["friendly"] = Modification::Friendly;
+ modifierNames["rename"] = Modification::Rename;
+ modifierNames["final"] = Modification::Final;
+ modifierNames["non-final"] = Modification::NonFinal;
+ }
+
+ if (!modifierNames.contains(modifier)) {
+ m_error = QString("Unknown access modifier: '%1'").arg(modifier);
+ return false;
+ }
+
+ if (mod)
+ 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_contextStack.top()->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;
+
+ if (!convertRemovalAttribute(attributes["remove"], fm, m_error))
+ return false;
+
+ QString read = attributes["read"];
+ QString write = attributes["write"];
+
+ if (read == "true") fm.modifiers |= FieldModification::Readable;
+ if (write == "true") fm.modifiers |= FieldModification::Writable;
+
+ m_contextStack.top()->fieldMods << fm;
+ }
+ break;
+ case StackElement::AddFunction: {
+ if (!(topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::Root))) {
+ m_error = QString::fromLatin1("Add function requires a complex type or a root tag as parent"
+ ", was=%1").arg(topElement.type, 0, 16);
+ return false;
+ }
+ QString signature = attributes["signature"];
+
+ signature = TypeDatabase::normalizedSignature(signature.toLocal8Bit().constData());
+ if (signature.isEmpty()) {
+ m_error = "No signature for the added function";
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, "add-function");
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ AddedFunction func(signature, attributes["return-type"], since);
+ func.setStatic(attributes["static"] == "yes");
+ if (!signature.contains("("))
+ signature += "()";
+ m_currentSignature = signature;
+
+ QString access = attributes["access"].toLower();
+ if (!access.isEmpty()) {
+ if (access == QLatin1String("protected")) {
+ func.setAccess(AddedFunction::Protected);
+ } else if (access == QLatin1String("public")) {
+ func.setAccess(AddedFunction::Public);
+ } else {
+ m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
+ return false;
+ }
+ }
+
+ m_contextStack.top()->addedFunctions << func;
+
+ FunctionModification mod(since);
+ mod.signature = m_currentSignature;
+ m_contextStack.top()->functionMods << mod;
+ }
+ 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 = TypeDatabase::normalizedSignature(signature.toLocal8Bit().constData());
+ if (signature.isEmpty()) {
+ m_error = "No signature for modified function";
+ return false;
+ }
+
+ QString errorString = checkSignatureError(signature, "modify-function");
+ if (!errorString.isEmpty()) {
+ m_error = errorString;
+ return false;
+ }
+
+ FunctionModification mod(since);
+ 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;
+
+ if (!convertRemovalAttribute(attributes["remove"], mod, m_error))
+ 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_contextStack.top()->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_contextStack.top()->functionMods.last().argument_mods.last().replacedDefaultExpression = attributes["with"];
+ break;
+ case StackElement::RemoveDefaultExpression:
+ m_contextStack.top()->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;
+ case StackElement::ReferenceCount: {
+ if (topElement.type != StackElement::ModifyArgument) {
+ m_error = "reference-count must be child of modify-argument";
+ return false;
+ }
+
+ ReferenceCount rc;
+
+ static QHash<QString, ReferenceCount::Action> actions;
+ if (actions.isEmpty()) {
+ actions["add"] = ReferenceCount::Add;
+ actions["add-all"] = ReferenceCount::AddAll;
+ actions["remove"] = ReferenceCount::Remove;
+ actions["set"] = ReferenceCount::Set;
+ actions["ignore"] = ReferenceCount::Ignore;
+ }
+ rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid);
+ rc.varName = attributes["variable-name"];
+
+ if (rc.action == ReferenceCount::Invalid) {
+ m_error = "unrecognized value for action attribute. supported actions:";
+ foreach (QString action, actions.keys())
+ m_error += " " + action;
+ }
+
+ m_contextStack.top()->functionMods.last().argument_mods.last().referenceCounts.append(rc);
+ }
+ break;
+
+ 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<QString, ArgumentOwner::Action> 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_contextStack.top()->functionMods.last().argument_mods.last().owner = ao;
+ }
+ break;
+
+
+ case StackElement::InjectCode: {
+ if (!(topElement.type & StackElement::ComplexTypeEntryMask)
+ && (topElement.type != StackElement::AddFunction)
+ && (topElement.type != StackElement::ModifyFunction)
+ && (topElement.type != StackElement::Root)) {
+ m_error = "wrong parent type for code injection";
+ return false;
+ }
+
+ static QHash<QString, TypeSystem::Language> languageNames;
+ if (languageNames.isEmpty()) {
+ languageNames["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<QString, CodeSnip::Position> 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(since);
+ 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 codeFile(file_name);
+ if (codeFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
+ QString content = QString::fromUtf8(codeFile.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 || topElement.type == StackElement::AddFunction) {
+ FunctionModification mod = m_contextStack.top()->functionMods.last();
+ if (snip.language == TypeSystem::ShellDeclaration) {
+ m_error = "no function implementation in shell declaration in which to inject code";
+ return false;
+ }
+
+ m_contextStack.top()->functionMods.last().snips << snip;
+ if (in_file)
+ m_contextStack.top()->functionMods.last().modifiers |= FunctionModification::CodeInjection;
+ element->type = StackElement::InjectCodeInFunction;
+ } else if (topElement.type == StackElement::Root) {
+ element->entry->addCodeSnip(snip);
+ } else if (topElement.type != StackElement::Root) {
+ m_contextStack.top()->codeSnips << snip;
+ }
+
+ }
+ break;
+ case StackElement::Include: {
+ QString location = attributes["location"].toLower();
+
+ static QHash<QString, Include::IncludeType> locationNames;
+ if (locationNames.isEmpty()) {
+ locationNames["global"] = Include::IncludePath;
+ locationNames["local"] = Include::LocalPath;
+ locationNames["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<ComplexTypeEntry *>(element->entry);
+ if (topElement.type & (StackElement::ComplexTypeEntryMask | StackElement::PrimitiveTypeEntry)) {
+ element->entry->setInclude(inc);
+ } else if (topElement.type == StackElement::ExtraIncludes) {
+ element->entry->addExtraInclude(inc);
+ } else {
+ m_error = "Only supported parent tags are primitive-type, complex types or 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"], since);
+ break;
+ case StackElement::TemplateInstanceEnum:
+ if (!(topElement.type & StackElement::CodeSnipMask) &&
+ (topElement.type != StackElement::Template) &&
+ (topElement.type != StackElement::CustomMetaConstructor) &&
+ (topElement.type != StackElement::CustomMetaDestructor) &&
+ (topElement.type != StackElement::NativeToTarget) &&
+ (topElement.type != StackElement::AddConversion) &&
+ (topElement.type != StackElement::ConversionRule)) {
+ m_error = "Can only insert templates into code snippets, templates, custom-constructors, "\
+ "custom-destructors, conversion-rule, native-to-target or add-conversion tags.";
+ return false;
+ }
+ element->value.templateInstance = new TemplateInstance(attributes["name"], since);
+ 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
+ };
+ }
+
+ m_current = element;
+ return true;
+}
+
+PrimitiveTypeEntry* PrimitiveTypeEntry::basicAliasedTypeEntry() const
+{
+ if (!m_aliasedTypeEntry)
+ return 0;
+
+ PrimitiveTypeEntry* baseAliasTypeEntry = m_aliasedTypeEntry->basicAliasedTypeEntry();
+ if (baseAliasTypeEntry)
+ return baseAliasTypeEntry;
+ else
+ return m_aliasedTypeEntry;
+}
+
+typedef QHash<const PrimitiveTypeEntry*, QString> PrimitiveTypeEntryTargetLangPackageMap;
+Q_GLOBAL_STATIC(PrimitiveTypeEntryTargetLangPackageMap, primitiveTypeEntryTargetLangPackages);
+
+void PrimitiveTypeEntry::setTargetLangPackage(const QString& package)
+{
+ primitiveTypeEntryTargetLangPackages()->insert(this, package);
+}
+QString PrimitiveTypeEntry::targetLangPackage() const
+{
+ if (!primitiveTypeEntryTargetLangPackages()->contains(this))
+ return this->::TypeEntry::targetLangPackage();
+ return primitiveTypeEntryTargetLangPackages()->value(this);
+}
+
+CodeSnipList TypeEntry::codeSnips() const
+{
+ return m_codeSnips;
+}
+
+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;
+}
+
+// The things we do not to break the ABI...
+typedef QHash<const ComplexTypeEntry*, QString> ComplexTypeEntryDefaultConstructorMap;
+Q_GLOBAL_STATIC(ComplexTypeEntryDefaultConstructorMap, complexTypeEntryDefaultConstructors);
+
+void ComplexTypeEntry::setDefaultConstructor(const QString& defaultConstructor)
+{
+ if (!defaultConstructor.isEmpty())
+ complexTypeEntryDefaultConstructors()->insert(this, defaultConstructor);
+}
+QString ComplexTypeEntry::defaultConstructor() const
+{
+ if (!complexTypeEntryDefaultConstructors()->contains(this))
+ return QString();
+ return complexTypeEntryDefaultConstructors()->value(this);
+}
+bool ComplexTypeEntry::hasDefaultConstructor() const
+{
+ return complexTypeEntryDefaultConstructors()->contains(this);
+}
+
+QString ContainerTypeEntry::targetLangPackage() const
+{
+ return QString();
+}
+
+QString ContainerTypeEntry::targetLangName() const
+{
+
+ switch (m_type) {
+ case StringListContainer: return "QStringList";
+ case ListContainer: return "QList";
+ case LinkedListContainer: return "QLinkedList";
+ 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 "QHash";
+ case MultiHashContainer: return "QMultiHash";
+ 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();
+}
+
+/*!
+ * 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 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;
+}
+
+bool FunctionModification::operator!=(const FunctionModification& other) const
+{
+ return !(*this == other);
+}
+
+bool FunctionModification::operator==(const FunctionModification& other) const
+{
+ if (signature != other.signature)
+ return false;
+
+ if (association != other.association)
+ return false;
+
+ if (modifiers != other.modifiers)
+ return false;
+
+ if (removal != other.removal)
+ return false;
+
+ if (m_thread != other.m_thread)
+ return false;
+
+ if (m_allowThread != other.m_allowThread)
+ return false;
+
+ if (m_version != other.m_version)
+ return false;
+
+ return true;
+}
+
+static AddedFunction::TypeInfo parseType(const QString& signature, int startPos = 0, int* endPos = 0)
+{
+ AddedFunction::TypeInfo result;
+ QRegExp regex("\\w");
+ int length = signature.length();
+ int start = signature.indexOf(regex, startPos);
+ if (start == -1) {
+ if (signature.mid(startPos + 1, 3) == "...") { // varargs
+ if (endPos)
+ *endPos = startPos + 4;
+ result.name = "...";
+ } else { // error
+ if (endPos)
+ *endPos = length;
+ }
+ return result;
+ }
+
+ int cantStop = 0;
+ QString paramString;
+ QChar c;
+ int i = start;
+ for (; i < length; ++i) {
+ c = signature[i];
+ if (c == '<')
+ cantStop++;
+ if (c == '>')
+ cantStop--;
+ if (cantStop < 0)
+ break; // FIXME: report error?
+ if ((c == ')' || c == ',') && !cantStop)
+ break;
+ paramString += signature[i];
+ }
+ if (endPos)
+ *endPos = i;
+
+ // Check default value
+ if (paramString.contains('=')) {
+ QStringList lst = paramString.split('=');
+ paramString = lst[0].trimmed();
+ result.defaultValue = lst[1].trimmed();
+ }
+
+ // check constness
+ if (paramString.startsWith("const ")) {
+ result.isConstant = true;
+ paramString.remove(0, sizeof("const")/sizeof(char));
+ paramString = paramString.trimmed();
+ }
+ // check reference
+ if (paramString.endsWith("&")) {
+ result.isReference = true;
+ paramString.chop(1);
+ paramString = paramString.trimmed();
+ }
+ // check Indirections
+ while (paramString.endsWith("*")) {
+ result.indirections++;
+ paramString.chop(1);
+ paramString = paramString.trimmed();
+ }
+ result.name = paramString;
+
+ return result;
+}
+
+AddedFunction::AddedFunction(QString signature, QString returnType, double vr) : m_access(Public), m_version(vr)
+{
+ Q_ASSERT(!returnType.isEmpty());
+ m_returnType = parseType(returnType);
+ signature = signature.trimmed();
+ int endPos = signature.indexOf('(');
+ if (endPos < 0) {
+ m_isConst = false;
+ m_name = signature;
+ } else {
+ m_name = signature.left(endPos).trimmed();
+ int signatureLength = signature.length();
+ while (endPos < signatureLength) {
+ TypeInfo arg = parseType(signature, endPos, &endPos);
+ if (!arg.name.isEmpty())
+ m_arguments.append(arg);
+ // end of parameters...
+ if (signature[endPos] == ')')
+ break;
+ }
+ // is const?
+ m_isConst = signature.right(signatureLength - endPos).contains("const");
+ }
+}
+
+QString ComplexTypeEntry::targetLangApiName() const
+{
+ return strings_jobject;
+}
+QString StringTypeEntry::targetLangApiName() const
+{
+ return strings_jobject;
+}
+QString StringTypeEntry::targetLangName() const
+{
+ return strings_String;
+}
+QString StringTypeEntry::targetLangPackage() const
+{
+ return QString();
+}
+QString CharTypeEntry::targetLangApiName() const
+{
+ return strings_jchar;
+}
+QString CharTypeEntry::targetLangName() const
+{
+ return strings_char;
+}
+QString VariantTypeEntry::targetLangApiName() const
+{
+ return strings_jobject;
+}
+QString VariantTypeEntry::targetLangName() const
+{
+ return strings_Object;
+}
+QString VariantTypeEntry::targetLangPackage() const
+{
+ return QString();
+}
+
+QString ContainerTypeEntry::typeName() const
+{
+ switch(m_type) {
+ case LinkedListContainer:
+ return "linked-list";
+ case ListContainer:
+ return "list";
+ case StringListContainer:
+ return "string-list";
+ case VectorContainer:
+ return "vector";
+ case StackContainer:
+ return "stack";
+ case QueueContainer:
+ return "queue";
+ case SetContainer:
+ return "set";
+ case MapContainer:
+ return "map";
+ case MultiMapContainer:
+ return "multi-map";
+ case HashContainer:
+ return "hash";
+ case MultiHashContainer:
+ return "multi-hash";
+ case PairContainer:
+ return "pair";
+ case NoContainer:
+ default:
+ return "?";
+ }
+}
+
+static bool strLess(const char* a, const char* b)
+{
+ return ::strcmp(a, b) < 0;
+}
+
+bool TypeEntry::isCppPrimitive() const
+{
+ if (!isPrimitive())
+ return false;
+
+ PrimitiveTypeEntry* aliasedType = ((PrimitiveTypeEntry*)this)->basicAliasedTypeEntry();
+ QByteArray typeName = (aliasedType ? aliasedType->name() : m_name).toAscii();
+
+ if (typeName.contains(' ') || m_type == VoidType)
+ return true;
+ // Keep this sorted!!
+ static const char* cppTypes[] = { "bool", "char", "double", "float", "int", "long", "long long", "short", "wchar_t" };
+ const int N = sizeof(cppTypes)/sizeof(char*);
+
+ const char** res = qBinaryFind(&cppTypes[0], &cppTypes[N], typeName.constData(), strLess);
+
+ return res != &cppTypes[N];
+}
+
+// Again, stuff to avoid ABI breakage.
+typedef QHash<const TypeEntry*, CustomConversion*> TypeEntryCustomConversionMap;
+Q_GLOBAL_STATIC(TypeEntryCustomConversionMap, typeEntryCustomConversionMap);
+
+TypeEntry::~TypeEntry()
+{
+ if (typeEntryCustomConversionMap()->contains(this)) {
+ CustomConversion* customConversion = typeEntryCustomConversionMap()->value(this);
+ typeEntryCustomConversionMap()->remove(this);
+ delete customConversion;
+ }
+}
+
+bool TypeEntry::hasCustomConversion() const
+{
+ return typeEntryCustomConversionMap()->contains(this);
+}
+void TypeEntry::setCustomConversion(CustomConversion* customConversion)
+{
+ if (customConversion)
+ typeEntryCustomConversionMap()->insert(this, customConversion);
+ else if (typeEntryCustomConversionMap()->contains(this))
+ typeEntryCustomConversionMap()->remove(this);
+}
+CustomConversion* TypeEntry::customConversion() const
+{
+ if (typeEntryCustomConversionMap()->contains(this))
+ return typeEntryCustomConversionMap()->value(this);
+ return 0;
+}
+
+/*
+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;
+}
+*/
+
+struct CustomConversion::CustomConversionPrivate
+{
+ CustomConversionPrivate(const TypeEntry* ownerType)
+ : ownerType(ownerType), replaceOriginalTargetToNativeConversions(false)
+ {
+ }
+ const TypeEntry* ownerType;
+ QString nativeToTargetConversion;
+ bool replaceOriginalTargetToNativeConversions;
+ TargetToNativeConversions targetToNativeConversions;
+};
+
+struct CustomConversion::TargetToNativeConversion::TargetToNativeConversionPrivate
+{
+ TargetToNativeConversionPrivate()
+ : sourceType(0)
+ {
+ }
+ const TypeEntry* sourceType;
+ QString sourceTypeName;
+ QString sourceTypeCheck;
+ QString conversion;
+};
+
+CustomConversion::CustomConversion(TypeEntry* ownerType)
+{
+ m_d = new CustomConversionPrivate(ownerType);
+ if (ownerType)
+ ownerType->setCustomConversion(this);
+}
+
+CustomConversion::~CustomConversion()
+{
+ foreach (TargetToNativeConversion* targetToNativeConversion, m_d->targetToNativeConversions)
+ delete targetToNativeConversion;
+ m_d->targetToNativeConversions.clear();
+ delete m_d;
+}
+
+const TypeEntry* CustomConversion::ownerType() const
+{
+ return m_d->ownerType;
+}
+
+QString CustomConversion::nativeToTargetConversion() const
+{
+ return m_d->nativeToTargetConversion;
+}
+
+void CustomConversion::setNativeToTargetConversion(const QString& nativeToTargetConversion)
+{
+ m_d->nativeToTargetConversion = nativeToTargetConversion;
+}
+
+bool CustomConversion::replaceOriginalTargetToNativeConversions() const
+{
+ return m_d->replaceOriginalTargetToNativeConversions;
+}
+
+void CustomConversion::setReplaceOriginalTargetToNativeConversions(bool replaceOriginalTargetToNativeConversions)
+{
+ m_d->replaceOriginalTargetToNativeConversions = replaceOriginalTargetToNativeConversions;
+}
+
+bool CustomConversion::hasTargetToNativeConversions() const
+{
+ return !(m_d->targetToNativeConversions.isEmpty());
+}
+
+CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions()
+{
+ return m_d->targetToNativeConversions;
+}
+
+const CustomConversion::TargetToNativeConversions& CustomConversion::targetToNativeConversions() const
+{
+ return m_d->targetToNativeConversions;
+}
+
+void CustomConversion::addTargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion)
+{
+ m_d->targetToNativeConversions.append(new TargetToNativeConversion(sourceTypeName, sourceTypeCheck, conversion));
+}
+
+CustomConversion::TargetToNativeConversion::TargetToNativeConversion(const QString& sourceTypeName,
+ const QString& sourceTypeCheck,
+ const QString& conversion)
+{
+ m_d = new TargetToNativeConversionPrivate;
+ m_d->sourceTypeName = sourceTypeName;
+ m_d->sourceTypeCheck = sourceTypeCheck;
+ m_d->conversion = conversion;
+}
+
+CustomConversion::TargetToNativeConversion::~TargetToNativeConversion()
+{
+ delete m_d;
+}
+
+const TypeEntry* CustomConversion::TargetToNativeConversion::sourceType() const
+{
+ return m_d->sourceType;
+}
+
+void CustomConversion::TargetToNativeConversion::setSourceType(const TypeEntry* sourceType)
+{
+ m_d->sourceType = sourceType;
+}
+
+bool CustomConversion::TargetToNativeConversion::isCustomType() const
+{
+ return !(m_d->sourceType);
+}
+
+QString CustomConversion::TargetToNativeConversion::sourceTypeName() const
+{
+ return m_d->sourceTypeName;
+}
+
+QString CustomConversion::TargetToNativeConversion::sourceTypeCheck() const
+{
+ return m_d->sourceTypeCheck;
+}
+
+QString CustomConversion::TargetToNativeConversion::conversion() const
+{
+ return m_d->conversion;
+}
+
+void CustomConversion::TargetToNativeConversion::setConversion(const QString& conversion)
+{
+ m_d->conversion = conversion;
+}