/*
 * This file is part of the API Extractor project.
 *
 * Copyright (C) 2013 Digia Plc 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");
    }
}

AddedFunction::TypeInfo AddedFunction::TypeInfo::fromSignature(const QString& signature)
{
    return parseType(signature);
}

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;
}
