view generator/typesystem.cpp @ 282:256ab6cb8e85

Signals look-up andNew syntax for connect. The old one will not work from now on. This will allow for the signals overload. Although changes are done for both D1 and D2 versions, D1 won't work because of compiler bugs. I am tired of waiting for fixes.
author eldar
date Fri, 16 Oct 2009 02:43:59 +0000
parents ae34188ddd84
children bc783e20da2b
line wrap: on
line source

/****************************************************************************
**
** Copyright (C) 1992-2008 Nokia. All rights reserved.
**
** This file is part of Qt Jambi.
**
** * Commercial Usage
* Licensees holding valid Qt Commercial licenses may use this file in
* accordance with the Qt Commercial License Agreement provided with the
* Software or, alternatively, in accordance with the terms contained in
* a written agreement between you and Nokia.
*
*
* GNU General Public License Usage
* Alternatively, this file may be used under the terms of the GNU
* General Public License versions 2.0 or 3.0 as published by the Free
* Software Foundation and appearing in the file LICENSE.GPL included in
* the packaging of this file.  Please review the following information
* to ensure GNU General Public Licensing requirements will be met:
* http://www.fsf.org/licensing/licenses/info/GPLv2.html and
* http://www.gnu.org/copyleft/gpl.html.  In addition, as a special
* exception, Nokia gives you certain additional rights. These rights
* are described in the Nokia Qt GPL Exception version 1.2, included in
* the file GPL_EXCEPTION.txt in this package.
* 
* Qt for Windows(R) Licensees
* As a special exception, Nokia, as the sole copyright holder for Qt
* Designer, grants users of the Qt/Eclipse Integration plug-in the
* right for the Qt/Eclipse Integration to link to functionality
* provided by Qt Designer and its related libraries.
*
*
* If you are unsure which license is appropriate for your use, please
* contact the sales department at qt-sales@nokia.com.

**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/

#include "typesystem.h"
#include "generator.h"

#include "customtypes.h"

#include <reporthandler.h>

#include <QtXml>

QString strings_Object = QLatin1String("Object");
QString strings_String = QLatin1String("string");
QString strings_Thread = QLatin1String("Thread");
QString strings_char = QLatin1String("char");
QString strings_java_lang = QLatin1String("java.lang");
QString strings_jchar = QLatin1String("jchar");
QString strings_jobject = QLatin1String("jobject");

static void addRemoveFunctionToTemplates(TypeDatabase *db);

class StackElement
{
    public:
    enum ElementType {
        None = 0x0,

        // Type tags (0x1, ... , 0xff)
        ObjectTypeEntry      = 0x1,
        ValueTypeEntry       = 0x2,
        InterfaceTypeEntry   = 0x3,
        NamespaceTypeEntry   = 0x4,
        ComplexTypeEntryMask = 0xf,

        // Non-complex type tags (0x10, 0x20, ... , 0xf0)
        PrimitiveTypeEntry   = 0x10,
        EnumTypeEntry        = 0x20,
        TypeEntryMask        = 0xff,

        // Simple tags (0x100, 0x200, ... , 0xf00)
        ExtraIncludes               = 0x100,
        Include                     = 0x200,
        ModifyFunction              = 0x300,
        ModifyField                 = 0x400,
        Root                        = 0x500,
        CustomMetaConstructor       = 0x600,
        CustomMetaDestructor        = 0x700,
        ArgumentMap                 = 0x800,
        SuppressedWarning           = 0x900,
        Rejection                   = 0xa00,
        LoadTypesystem              = 0xb00,
        RejectEnumValue             = 0xc00,
        Template                    = 0xd00,
        TemplateInstanceEnum        = 0xe00,
        Replace                     = 0xf00,
        SimpleMask                  = 0xf00,
        // qtd stuff
        AddClass                    = 0x1100,
        PackageDepend               = 0x1200,

        // Code snip tags (0x1000, 0x2000, ... , 0xf000)
        InjectCode =           0x1000,
        InjectCodeInFunction = 0x2000,
        CodeSnipMask =         0xf000,

        // Function modifier tags (0x010000, 0x020000, ... , 0xf00000)
        Access                   = 0x010000,
        Removal                  = 0x020000,
        Rename                   = 0x040000,
        ModifyArgument           = 0x080000,
        PrivateSignal            = 0x100000,
        FunctionModifiers        = 0xff0000,
        StoreResult              = 0x110000,

        // Argument modifier tags (0x01000000 ... 0xf0000000)
        ConversionRule           = 0x01000000,
        ReplaceType              = 0x02000000,
        ReplaceDefaultExpression = 0x04000000,
        RemoveArgument           = 0x08000000,
        DefineOwnership          = 0x10000000,
        RemoveDefaultExpression  = 0x20000000,
        NoNullPointers           = 0x40000000,
        ReferenceCount           = 0x80000000,
        ArgumentModifiers        = 0xff000000

    };

    StackElement(StackElement *p) : entry(0), type(None), parent(p){ }

    TypeEntry *entry;
    ElementType type;
    StackElement *parent;

    union {
        TemplateInstance *templateInstance;
        TemplateEntry *templateEntry;
        CustomFunction *customFunction;
    } value;
};

class Handler : public QXmlDefaultHandler
{
public:
    Handler(TypeDatabase *database, const QString &source_dir, bool generate)
        : m_database(database), m_generate(generate ? TypeEntry::GenerateAll : TypeEntry::GenerateForSubclass), m_source_dir(source_dir)
    {
        m_current_enum = 0;
        current = 0;

        tagNames["rejection"] = StackElement::Rejection;
        tagNames["primitive-type"] = StackElement::PrimitiveTypeEntry;
        tagNames["object-type"] = StackElement::ObjectTypeEntry;
        tagNames["value-type"] = StackElement::ValueTypeEntry;
        tagNames["interface-type"] = StackElement::InterfaceTypeEntry;
        tagNames["namespace-type"] = StackElement::NamespaceTypeEntry;
        tagNames["enum-type"] = StackElement::EnumTypeEntry;
        tagNames["extra-includes"] = StackElement::ExtraIncludes;
        tagNames["include"] = StackElement::Include;
        tagNames["inject-code"] = StackElement::InjectCode;
        tagNames["modify-function"] = StackElement::ModifyFunction;
        tagNames["modify-field"] = StackElement::ModifyField;
        tagNames["access"] = StackElement::Access;
        tagNames["remove"] = StackElement::Removal;
        tagNames["rename"] = StackElement::Rename;
        tagNames["typesystem"] = StackElement::Root;
        tagNames["custom-constructor"] = StackElement::CustomMetaConstructor;
        tagNames["custom-destructor"] = StackElement::CustomMetaDestructor;
        tagNames["argument-map"] = StackElement::ArgumentMap;
        tagNames["suppress-warning"] = StackElement::SuppressedWarning;
        tagNames["load-typesystem"] = StackElement::LoadTypesystem;
        tagNames["define-ownership"] = StackElement::DefineOwnership;
        tagNames["replace-default-expression"] = StackElement::ReplaceDefaultExpression;
        tagNames["reject-enum-value"] = StackElement::RejectEnumValue;
        tagNames["replace-type"] = StackElement::ReplaceType;
        tagNames["conversion-rule"] = StackElement::ConversionRule;
        tagNames["modify-argument"] = StackElement::ModifyArgument;
        tagNames["remove-argument"] = StackElement::RemoveArgument;
        tagNames["remove-default-expression"] = StackElement::RemoveDefaultExpression;
        tagNames["template"] = StackElement::Template;
        tagNames["insert-template"] = StackElement::TemplateInstanceEnum;
        tagNames["replace"] = StackElement::Replace;
        tagNames["no-null-pointer"] = StackElement::NoNullPointers;
        tagNames["reference-count"] = StackElement::ReferenceCount;
        // qtd
        tagNames["add-class"] = StackElement::AddClass;
        tagNames["store-result"] = StackElement::StoreResult;

        tagNames["depend"] = StackElement::PackageDepend;
    }

    bool startElement(const QString &namespaceURI, const QString &localName,
                      const QString &qName, const QXmlAttributes &atts);
    bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);

    QString errorString() const { return m_error; }
    bool error(const QXmlParseException &exception);
    bool fatalError(const QXmlParseException &exception);
    bool warning(const QXmlParseException &exception);

    bool characters(const QString &ch);

private:
    void fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
                              QHash<QString, QString> *acceptedAttributes);

    bool importFileElement(const QXmlAttributes &atts);
    bool convertBoolean(const QString &, const QString &, bool);

    TypeDatabase *m_database;
    StackElement* current;
    QString m_defaultPackage;
    QString m_defaultSuperclass;
    QString m_error;
    TypeEntry::CodeGeneration m_generate;

    EnumTypeEntry *m_current_enum;

    CodeSnipList m_code_snips;
    FunctionModificationList m_function_mods;
    FieldModificationList m_field_mods;

    QHash<QString, StackElement::ElementType> tagNames;

    QString m_source_dir;
    QStringList m_defaultDepends;
};

bool Handler::error(const QXmlParseException &e)
{
    qWarning("Error: line=%d, column=%d, message=%s\n",
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));
    return false;
}

bool Handler::fatalError(const QXmlParseException &e)
{
    qWarning("Fatal error: line=%d, column=%d, message=%s\n",
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));

    return false;
}

bool Handler::warning(const QXmlParseException &e)
{
    qWarning("Warning: line=%d, column=%d, message=%s\n",
             e.lineNumber(), e.columnNumber(), qPrintable(e.message()));

    return false;
}

void Handler::fetchAttributeValues(const QString &name, const QXmlAttributes &atts,
                                   QHash<QString, QString> *acceptedAttributes)
{
    Q_ASSERT(acceptedAttributes != 0);

    for (int i=0; i<atts.length(); ++i) {
        QString key = atts.localName(i).toLower();
        QString val = atts.value(i);

        if (!acceptedAttributes->contains(key)) {
            ReportHandler::warning(QString("Unknown attribute for '%1': '%2'").arg(name).arg(key));
        } else {
            (*acceptedAttributes)[key] = val;
        }
    }
}

bool Handler::endElement(const QString &, const QString &localName, const QString &)
{
    QString tagName = localName.toLower();
    if(tagName == "import-file")
        return true;

    if (!current)
        return true;

    switch (current->type) {
    case StackElement::ObjectTypeEntry:
    case StackElement::ValueTypeEntry:
    case StackElement::InterfaceTypeEntry:
    case StackElement::NamespaceTypeEntry:
        {
            ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(current->entry);
            centry->setFunctionModifications(m_function_mods);
            centry->setFieldModifications(m_field_mods);
            centry->setCodeSnips(m_code_snips);

            if (centry->designatedInterface()) {
                centry->designatedInterface()->setCodeSnips(m_code_snips);
                centry->designatedInterface()->setFunctionModifications(m_function_mods);
            }
            m_code_snips = CodeSnipList();
            m_function_mods = FunctionModificationList();
            m_field_mods = FieldModificationList();
        }
        break;
    case StackElement::CustomMetaConstructor:
        {
            current->entry->setCustomConstructor(*current->value.customFunction);
            delete current->value.customFunction;
        }
        break;
    case StackElement::CustomMetaDestructor:
        {
            current->entry->setCustomDestructor(*current->value.customFunction);
            delete current->value.customFunction;
        }
        break;
    case StackElement::EnumTypeEntry:
        m_current_enum = 0;
        break;
    case StackElement::Template:
        m_database->addTemplate(current->value.templateEntry);
        break;
    case StackElement::TemplateInstanceEnum:
        if(current->parent->type == StackElement::InjectCode){
            m_code_snips.last().addTemplateInstance(current->value.templateInstance);
        }else if(current->parent->type == StackElement::Template){
            current->parent->value.templateEntry->addTemplateInstance(current->value.templateInstance);
        }else if(current->parent->type == StackElement::CustomMetaConstructor || current->parent->type == StackElement::CustomMetaConstructor){
            current->parent->value.customFunction->addTemplateInstance(current->value.templateInstance);
        }else if(current->parent->type == StackElement::ConversionRule){
            m_function_mods.last().argument_mods.last().conversion_rules.last().addTemplateInstance(current->value.templateInstance);
        }else if(current->parent->type == StackElement::InjectCodeInFunction){
            m_function_mods.last().snips.last().addTemplateInstance(current->value.templateInstance);
        }
        break;
    default:
        break;
    }

    StackElement *child = current;
    current=current->parent;
    delete(child);

    return true;
}

bool Handler::characters(const QString &ch)
{
    if(current->type == StackElement::Template){
        current->value.templateEntry->addCode(ch);
        return true;
    }

    if (current->type == StackElement::CustomMetaConstructor || current->type == StackElement::CustomMetaDestructor){
        current->value.customFunction->addCode(ch);
        return true;
    }

    if (current->type == StackElement::ConversionRule){
        m_function_mods.last().argument_mods.last().conversion_rules.last().addCode(ch);
        return true;
    }

    if  (current->parent){
        if ((current->type & StackElement::CodeSnipMask) != 0) {
            switch (current->parent->type) {
                case StackElement::Root:
                    ((TypeSystemTypeEntry *) current->parent->entry)->snips.last().addCode(ch);
                    break;
                case StackElement::ModifyFunction:
                    m_function_mods.last().snips.last().addCode(ch);
                    break;
                case StackElement::NamespaceTypeEntry:
                case StackElement::ObjectTypeEntry:
                case StackElement::ValueTypeEntry:
                case StackElement::InterfaceTypeEntry:
                    m_code_snips.last().addCode(ch);
                    break;
                default:
                    Q_ASSERT(false);
            };
            return true;
        }
    }

    return true;
}

bool Handler::importFileElement(const QXmlAttributes &atts)
{
    QString fileName = atts.value("name");
    if(fileName.isEmpty()){
        m_error = "Required attribute 'name' missing for include-file tag.";
        return false;
    }

    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        file.setFileName(":/trolltech/generator/" + fileName);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            m_error = QString("Could not open file: '%1'").arg(fileName);
            return false;
        }
    }

    QString quoteFrom = atts.value("quote-after-line");
    bool foundFromOk = quoteFrom.isEmpty();
    bool from = quoteFrom.isEmpty();

    QString quoteTo = atts.value("quote-before-line");
    bool foundToOk = quoteTo.isEmpty();
    bool to = true;

    QTextStream in(&file);
    while (!in.atEnd()) {
        QString line = in.readLine();
        if(from && to && line.contains(quoteTo)) {
            to = false;
            foundToOk = true;
            break;
        }
        if(from && to)
            characters(line + "\n");
        if(!from && line.contains(quoteFrom)) {
            from = true;
            foundFromOk = true;
        }
    }
    if(!foundFromOk || !foundToOk){
        QString fromError = QString("Could not find quote-after-line='%1' in file '%2'.").arg(quoteFrom).arg(fileName);
        QString toError = QString("Could not find quote-before-line='%1' in file '%2'.").arg(quoteTo).arg(fileName);

        if(!foundToOk)
            m_error = toError;
        if(!foundFromOk)
            m_error = fromError;
        if(!foundFromOk && !foundToOk)
            m_error = fromError + " " + toError;
        return false;
    }

    return true;
}

bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue)
{
    QString value = _value.toLower();
    if (value == "true" || value == "yes") {
        return true;
    } else if (value == "false" || value == "no") {
        return false;
    } else {
        QString warn = QString("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
            .arg(value).arg(attributeName).arg(defaultValue ? "yes" : "no");

        ReportHandler::warning(warn);
        return defaultValue;
    }
}

bool Handler::startElement(const QString &, const QString &n,
                           const QString &, const QXmlAttributes &atts)
{
    QString tagName = n.toLower();
    if(tagName == "import-file"){
        return importFileElement(atts);
    }

    StackElement *element = new StackElement(current);

    if (!tagNames.contains(tagName)) {
        m_error = QString("Unknown tag name: '%1'").arg(tagName);
        return false;
    }

    element->type = tagNames[tagName];
    if (element->type & StackElement::TypeEntryMask) {
        if (current->type != StackElement::Root) {
            m_error = "Nested types not supported";
            return false;
        }

        QHash<QString, QString> attributes;
        attributes["name"] = QString();

        switch (element->type) {
        case StackElement::PrimitiveTypeEntry:
            attributes["java-name"] = QString();
            attributes["jni-name"] = QString();
            attributes["preferred-conversion"] = "yes";
            attributes["preferred-java-type"] = "yes";
            break;
        case StackElement::EnumTypeEntry:
            attributes["flags"] = "no";
            attributes["upper-bound"] = QString();
            attributes["lower-bound"] = QString();
            attributes["force-integer"] = "no";
            attributes["extensible"] = "no";

            break;

        case StackElement::ObjectTypeEntry:
        case StackElement::ValueTypeEntry:
            attributes["force-abstract"] = QString("no");
            attributes["deprecated"] = QString("no");
            attributes["wrap"] = QString();
            // fall throooough
        case StackElement::InterfaceTypeEntry:
            attributes["default-superclass"] = m_defaultSuperclass;
            attributes["polymorphic-id-expression"] = QString();
            attributes["delete-in-main-thread"] = QString("no");
            // fall through
        case StackElement::NamespaceTypeEntry:
            attributes["java-name"] = QString();
            attributes["package"] = m_defaultPackage;
            attributes["expense-cost"] = "1";
            attributes["expense-limit"] = "none";
            attributes["polymorphic-base"] = QString("no");
            attributes["generate"] = QString("yes");
            attributes["target-type"] = QString();
            attributes["generic-class"] = QString("no");
            break;
        default:
            ; // nada
        };

        fetchAttributeValues(tagName, atts, &attributes);

        QString name = attributes["name"];

        // We need to be able to have duplicate primitive type entries, or it's not possible to
        // cover all primitive java types (which we need to do in order to support fake
        // meta objects)
        if (element->type != StackElement::PrimitiveTypeEntry) {
            TypeEntry *tmp = m_database->findType(name);
            if (tmp != 0) {
                ReportHandler::warning(QString("Duplicate type entry: '%1'").arg(name));
            }
        }

        if (name.isEmpty()) {
            m_error = "no 'name' attribute specified";
            return false;
        }
        switch (element->type) {
        case StackElement::PrimitiveTypeEntry:
            {
                QString java_name = attributes["java-name"];
                QString jni_name = attributes["jni-name"];
                QString preferred_conversion = attributes["preferred-conversion"].toLower();
                QString preferred_java_type = attributes["preferred-java-type"].toLower();

                if (java_name.isEmpty())
                    java_name = name;
                if (jni_name.isEmpty())
                    jni_name = name;

                PrimitiveTypeEntry *type = new PrimitiveTypeEntry(name);
                type->setCodeGeneration(m_generate);
                type->setTargetLangName(java_name);
                type->setJniName(jni_name);

                type->setPreferredConversion(convertBoolean(preferred_conversion, "preferred-conversion", true));
                type->setPreferredTargetLangType(convertBoolean(preferred_java_type, "preferred-java-type", true));

                element->entry = type;
            }
            break;
        case StackElement::EnumTypeEntry: {
            QStringList names = name.split(QLatin1String("::"));

            if (names.size() == 1) {
                m_current_enum = new EnumTypeEntry(QString(), name);
            }
            else
                m_current_enum =
                    new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join("::"),
                                      names.last());
            element->entry = m_current_enum;
            m_current_enum->setCodeGeneration(m_generate);
            m_current_enum->setTargetLangPackage(m_defaultPackage);
            //m_current_enum->setDepends(m_defaultDepends); // For enums its not needed?
            m_current_enum->setUpperBound(attributes["upper-bound"]);
            m_current_enum->setLowerBound(attributes["lower-bound"]);
            m_current_enum->setForceInteger(convertBoolean(attributes["force-integer"], "force-integer", false));
            m_current_enum->setExtensible(convertBoolean(attributes["extensible"], "extensible", false));

            // put in the flags parallel...
            if (!attributes["flags"].isEmpty() && attributes["flags"].toLower() != "no") {
                FlagsTypeEntry *ftype = new FlagsTypeEntry("QFlags<" + name + ">");
                ftype->setOriginator(m_current_enum);
                ftype->setOriginalName(attributes["flags"]);
                ftype->setCodeGeneration(m_generate);
                QString n = ftype->originalName();

                QStringList lst = n.split("::");
                if (QStringList(lst.mid(0, lst.size() - 1)).join("::") != m_current_enum->javaQualifier()) {
                    ReportHandler::warning(QString("enum %1 and flags %2 differ in qualifiers")
                                           .arg(m_current_enum->javaQualifier())
                                           .arg(lst.at(0)));
                }

                ftype->setFlagsName(lst.last());
                m_current_enum->setFlags(ftype);

                m_database->addFlagsType(ftype);
                m_database->addType(ftype);
            }
            }
            break;

        case StackElement::InterfaceTypeEntry:
            {
                ObjectTypeEntry *otype = new ObjectTypeEntry(name);
                QString javaName = attributes["java-name"];
                if (javaName.isEmpty())
                    javaName = name;
                InterfaceTypeEntry *itype =
                    new InterfaceTypeEntry(InterfaceTypeEntry::interfaceName(javaName));

                if (!convertBoolean(attributes["generate"], "generate", true))
                    itype->setCodeGeneration(TypeEntry::GenerateForSubclass);
                else
                    itype->setCodeGeneration(m_generate);
                otype->setDesignatedInterface(itype);
                itype->setOrigin(otype);
                element->entry = otype;
            }
            // fall through
        case StackElement::NamespaceTypeEntry:
            if (element->entry == 0) {
                element->entry = new NamespaceTypeEntry(name);
            }
            // fall through
        case StackElement::ObjectTypeEntry:
            if (element->entry == 0) {
                element->entry = new ObjectTypeEntry(name);
            }
            // fall through
        case StackElement::ValueTypeEntry:
            {
                if (element->entry == 0) {
                    if(name == "QVariant")
                        element->entry = new VariantTypeEntry(name);
                    else
                        element->entry = new ValueTypeEntry(name);
                }

                ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
                ctype->setTargetLangPackage(attributes["package"]);
                //ctype->setDepends(m_defaultDepends);
                ctype->setDefaultSuperclass(attributes["default-superclass"]);
                ctype->setGenericClass(convertBoolean(attributes["generic-class"], "generic-class", false));

                // qtd
                QString wrap = attributes["wrap"];
                ctype->setStructInD(wrap == "struct" ? true : false);
                //

                if (!convertBoolean(attributes["generate"], "generate", true))
                    element->entry->setCodeGeneration(TypeEntry::GenerateForSubclass);
                else
                    element->entry->setCodeGeneration(m_generate);

                QString javaName = attributes["java-name"];
                if (!javaName.isEmpty())
                    ctype->setTargetLangName(javaName);

                // The expense policy
                QString limit = attributes["expense-limit"];
                if (!limit.isEmpty() && limit != "none") {
                    ExpensePolicy ep;
                    ep.limit = limit.toInt();
                    ep.cost = attributes["expense-cost"];
                    ctype->setExpensePolicy(ep);
                }

                ctype->setIsPolymorphicBase(convertBoolean(attributes["polymorphic-base"], "polymorphic-base", false));
                ctype->setPolymorphicIdValue(attributes["polymorphic-id-expression"]);

                if (element->type == StackElement::ObjectTypeEntry || element->type == StackElement::ValueTypeEntry) {
                    if (convertBoolean(attributes["force-abstract"], "force-abstract", false))
                        ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::ForceAbstract);
                    if (convertBoolean(attributes["deprecated"], "deprecated", false))
                        ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::Deprecated);
                }

                if (element->type == StackElement::InterfaceTypeEntry ||
                    element->type == StackElement::ValueTypeEntry ||
                    element->type == StackElement::ObjectTypeEntry) {
                    if (convertBoolean(attributes["delete-in-main-thread"], "delete-in-main-thread", false))
                        ctype->setTypeFlags(ctype->typeFlags() | ComplexTypeEntry::DeleteInMainThread);
                }

                QString targetType = attributes["target-type"];
                if (!targetType.isEmpty() && element->entry->isComplex())
                    static_cast<ComplexTypeEntry *>(element->entry)->setTargetType(targetType);

                // ctype->setInclude(Include(Include::IncludePath, ctype->name()));
                ctype = ctype->designatedInterface();
                if (ctype != 0)
                {
                    ctype->setTargetLangPackage(attributes["package"]);
                    ctype->setDepends(m_defaultDepends);
                }
            }
            break;
        default:
            Q_ASSERT(false);
        };

        if (element->entry)
            m_database->addType(element->entry);
        else
            ReportHandler::warning(QString("Type: %1 was rejected by typesystem").arg(name));

    } else if (element->type != StackElement::None) {
        bool topLevel = element->type == StackElement::Root
      || element->type == StackElement::SuppressedWarning
      || element->type == StackElement::Rejection
      || element->type == StackElement::LoadTypesystem
      || element->type == StackElement::InjectCode
      || element->type == StackElement::Template
      || element->type == StackElement::PackageDepend;

        if (!topLevel && current->type == StackElement::Root) {
            m_error = QString("Tag requires parent: '%1'").arg(tagName);
            return false;
        }

        StackElement topElement = current==0 ? StackElement(0) : *current;
        element->entry = topElement.entry;

        QHash<QString, QString> attributes;
        switch (element->type) {
        case StackElement::Root:
            attributes["package"] = QString();
            attributes["default-superclass"] = QString();
            break;
        case StackElement::LoadTypesystem:
            attributes["name"] = QString();
            attributes["generate"] = "yes";
            break;
        case StackElement::NoNullPointers:
            attributes["default-value"] = QString();
            break;
        case StackElement::SuppressedWarning:
            attributes["text"] = QString();
            break;
        case StackElement::ReplaceDefaultExpression:
            attributes["with"] = QString();
            break;
        case StackElement::DefineOwnership:
            attributes["class"] = "java";
            attributes["owner"] = "";
            break;
        case StackElement::ModifyFunction:
            attributes["signature"] = QString();
            attributes["access"] = QString();
            attributes["remove"] = QString();
            attributes["rename"] = QString();
            attributes["deprecated"] = QString("no");
            attributes["associated-to"] = QString();
            attributes["virtual-slot"] = QString("no");
            attributes["allow-as-slot"] = QString("no");
            attributes["private-signal"] = QString("no");
            break;
        case StackElement::ModifyArgument:
            attributes["index"] = QString();
            attributes["replace-value"] = QString();
            attributes["invalidate-after-use"] = QString("no");
            break;
        case StackElement::ModifyField:
            attributes["name"] = QString();
            attributes["write"] = "true";
            attributes["read"] = "true";
            break;
        case StackElement::Access:
            attributes["modifier"] = QString();
            break;
        case StackElement::Include:
            attributes["file-name"] = QString();
            attributes["location"] = QString();
            break;
        case StackElement::CustomMetaConstructor:
            attributes["name"] = topElement.entry->name().toLower() + "_create";
            attributes["param-name"] = "copy";
            break;
        case StackElement::CustomMetaDestructor:
            attributes["name"] = topElement.entry->name().toLower() + "_delete";
            attributes["param-name"] = "copy";
            break;
        case StackElement::ReplaceType:
            attributes["modified-type"] = QString();
            break;
        case StackElement::InjectCode:
            attributes["class"] = "java";
            attributes["position"] = "beginning";
            break;
        case StackElement::ConversionRule:
            attributes["class"] = "";
            break;
        case StackElement::RejectEnumValue:
            attributes["name"] = "";
            break;
        case StackElement::ArgumentMap:
            attributes["index"] = "1";
            attributes["meta-name"] = QString();
            break;
        case StackElement::Rename:
            attributes["to"] = QString();
            break;
        case StackElement::Rejection:
            attributes["class"] = "*";
            attributes["function-name"] = "*";
            attributes["field-name"] = "*";
            attributes["enum-name"] = "*";
            break;
        case StackElement::Removal:
            attributes["class"] = "all";
            break;
        case StackElement::Template:
            attributes["name"] = QString();
            break;
        case StackElement::TemplateInstanceEnum:
            attributes["name"] = QString();
            break;
        case StackElement::Replace:
            attributes["from"] = QString();
            attributes["to"] = QString();
            break;
        case StackElement::ReferenceCount:
            attributes["action"] = QString();
            attributes["variable-name"] = QString();
            attributes["thread-safe"] = QString("no");
            attributes["declare-variable"] = QString();
            attributes["access"] = QString("private");
            attributes["conditional"] = QString("");
            break;
            // qtd
        case StackElement::AddClass:
            attributes["name"] = QString();
            break;
        case StackElement::PackageDepend:
            attributes["package"] = QString();
            break;
        default:
            ; // nada
        };

        if (attributes.count() > 0)
            fetchAttributeValues(tagName, atts, &attributes);

        switch (element->type) {
        case StackElement::Root:
            m_defaultPackage = attributes["package"];
            m_defaultSuperclass = attributes["default-superclass"];
            element->type = StackElement::Root;
            element->entry = new TypeSystemTypeEntry(m_defaultPackage);
            TypeDatabase::instance()->addType(element->entry);
            break;
        case StackElement::LoadTypesystem:
            {
                QString name = attributes["name"];
                if (name.isEmpty()) {
                    m_error = "No typesystem name specified";
                    return false;
                }

                if (!m_database->parseFile(name, m_source_dir, convertBoolean(attributes["generate"], "generate", true))) {
                    m_error = QString("Failed to parse: '%1'").arg(name);
                    return false;
                }
            }
            break;
        case StackElement::RejectEnumValue: {
            if (!m_current_enum) {
                m_error = "<reject-enum-value> node must be used inside a <enum-type> node";
                return false;
            }
            QString name = attributes["name"];

            bool added = false;
            if (!name.isEmpty()) {
                added = true;
                m_current_enum->addEnumValueRejection(name);
            }

            } break;
        case StackElement::ReplaceType:
            {
                if (topElement.type != StackElement::ModifyArgument) {
                    m_error = "Type replacement can only be specified for argument modifications";
                    return false;
                }

                if (attributes["modified-type"].isEmpty()) {
                    m_error = "Type replacement requires 'modified-type' attribute";
                    return false;
                }

                m_function_mods.last().argument_mods.last().modified_type = attributes["modified-type"];
            }
            break;
        case StackElement::ConversionRule:
            {
                if (topElement.type != StackElement::ModifyArgument) {
                    m_error = "Conversion rules can only be specified for argument modification";
                    return false;
                }

                static QHash<QString, TypeSystem::Language> languageNames;
                if (languageNames.isEmpty()) {
                    languageNames["native"] = TypeSystem::NativeCode;
                    languageNames["shell"] = TypeSystem::ShellCode;
                }

                CodeSnip snip;
                QString languageAttribute = attributes["class"].toLower();
                TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
                if (lang == TypeSystem::NoLanguage) {
                    m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
                    return false;
                }

                snip.language = lang;
                m_function_mods.last().argument_mods.last().conversion_rules.append(snip);
            }

            break;
        case StackElement::StoreResult:
            {
                if (topElement.type != StackElement::ModifyFunction) {
                    m_error = QString::fromLatin1("result storage requires function"
                                                  " modification as parent, was %1")
                              .arg(topElement.type, 0, 16);
                    return false;
                }

                m_function_mods.last().store_result = true;
            }
            break;
        case StackElement::ModifyArgument:
            {
                if (topElement.type != StackElement::ModifyFunction) {
                    m_error = QString::fromLatin1("argument modification requires function"
                                                  " modification as parent, was %1")
                              .arg(topElement.type, 0, 16);
                    return false;
                }

                QString index = attributes["index"];
                if (index == "return")
                    index = "0";
                else if (index == "this")
                    index = "-1";

                bool ok = false;
                int idx = index.toInt(&ok);
                if (!ok) {
                    m_error = QString("Cannot convert '%1' to integer").arg(index);
                    return false;
                }

                QString replace_value = attributes["replace-value"];

                if (!replace_value.isEmpty() && idx != 0) {
                    m_error = QString("replace-value is only supported for return values (index=0).");
                    return false;
                }

                ArgumentModification argumentModification = ArgumentModification(idx);
                argumentModification.replace_value = replace_value;
                argumentModification.reset_after_use = convertBoolean(attributes["invalidate-after-use"], "invalidate-after-use", false);
                m_function_mods.last().argument_mods.append(argumentModification);
            }
            break;
        case StackElement::NoNullPointers:
            {
                if (topElement.type != StackElement::ModifyArgument) {
                    m_error = "no-null-pointer requires argument modification as parent";
                    return false;
                }

                m_function_mods.last().argument_mods.last().no_null_pointers = true;
                if (m_function_mods.last().argument_mods.last().index == 0) {
                    m_function_mods.last().argument_mods.last().null_pointer_default_value = attributes["default-value"];
                } else if (!attributes["default-value"].isEmpty()) {
                    ReportHandler::warning("default values for null pointer guards are only effective for return values");
                }
            }
            break;
        case StackElement::DefineOwnership:
            {
                if (topElement.type != StackElement::ModifyArgument) {
                    m_error = "define-ownership requires argument modification as parent";
                    return false;
                }

                static QHash<QString, TypeSystem::Language> languageNames;
                if (languageNames.isEmpty()) {
                    languageNames["java"] = TypeSystem::TargetLangCode;
                    languageNames["shell"] = TypeSystem::ShellCode;
                }

                QString classAttribute = attributes["class"].toLower();
                TypeSystem::Language lang = languageNames.value(classAttribute, TypeSystem::NoLanguage);
                if (lang == TypeSystem::NoLanguage) {
                    m_error = QString("unsupported class attribute: '%1'").arg(classAttribute);
                    return false;
                }

                static QHash<QString, TypeSystem::Ownership> ownershipNames;
                if (ownershipNames.isEmpty()) {
                    ownershipNames["java"] = TypeSystem::TargetLangOwnership;
                    ownershipNames["c++"] = TypeSystem::CppOwnership;
                    ownershipNames["default"] = TypeSystem::DefaultOwnership;
                }

                QString ownershipAttribute = attributes["owner"].toLower();
                TypeSystem::Ownership owner = ownershipNames.value(ownershipAttribute, TypeSystem::InvalidOwnership);
                if (owner == TypeSystem::InvalidOwnership) {
                    m_error = QString("unsupported owner attribute: '%1'").arg(ownershipAttribute);
                    return false;
                }

                m_function_mods.last().argument_mods.last().ownerships[lang] = owner;
            }
            break;
        case StackElement::SuppressedWarning:
            if (attributes["text"].isEmpty())
                ReportHandler::warning("Suppressed warning with no text specified");
            else
                m_database->addSuppressedWarning(attributes["text"]);
            break;
        case StackElement::ArgumentMap:
            {
                if (!(topElement.type & StackElement::CodeSnipMask)) {
                    m_error = "Argument maps requires code injection as parent";
                    return false;
                }

                bool ok;
                int pos = attributes["index"].toInt(&ok);
                if (!ok) {
                    m_error = QString("Can't convert position '%1' to integer")
                              .arg(attributes["position"]);
                    return false;
                }

                if (pos <= 0) {
                    m_error = QString("Argument position %1 must be a positive number").arg(pos);
                    return false;
                }

                QString meta_name = attributes["meta-name"];
                if (meta_name.isEmpty()) {
                    ReportHandler::warning("Empty meta name in argument map");
                }

                if (topElement.type == StackElement::InjectCodeInFunction) {
                    m_function_mods.last().snips.last().argumentMap[pos] = meta_name;
                } else {
                    ReportHandler::warning("Argument maps are only useful for injection of code "
                                           "into functions.");
                }
            }
            break;
        case StackElement::Removal:
            {
                if (topElement.type != StackElement::ModifyFunction) {
                    m_error = "Function modification parent required";
                    return false;
                }

                static QHash<QString, TypeSystem::Language> languageNames;
                if (languageNames.isEmpty()) {
                    languageNames["java"] = TypeSystem::TargetLangAndNativeCode;
                    languageNames["all"] = TypeSystem::All;
                }

                QString languageAttribute = attributes["class"].toLower();
                TypeSystem::Language lang = languageNames.value(languageAttribute, TypeSystem::NoLanguage);
                if (lang == TypeSystem::NoLanguage) {
                    m_error = QString("unsupported class attribute: '%1'").arg(languageAttribute);
                    return false;
                }

                m_function_mods.last().removal = lang;
            }
            break;
        case StackElement::Rename:
        case StackElement::Access:
            {
                if (topElement.type != StackElement::ModifyField
                    && topElement.type != StackElement::ModifyFunction) {
                    m_error = "Function or field modification parent required";
                    return false;
                }

                Modification *mod = 0;
                if (topElement.type == StackElement::ModifyFunction)
                    mod = &m_function_mods.last();
                else
                    mod = &m_field_mods.last();

                QString modifier;
                if (element->type == StackElement::Rename) {
                    modifier = "rename";
                    QString renamed_to = attributes["to"];
                    if (renamed_to.isEmpty()) {
                        m_error = "Rename modifier requires 'to' attribute";
                        return false;
                    }

                    if (topElement.type == StackElement::ModifyFunction)
                        mod->setRenamedTo(renamed_to);
                    else
                        mod->setRenamedTo(renamed_to);
                } else {
                    modifier = attributes["modifier"].toLower();
                }

                if (modifier.isEmpty()) {
                    m_error = "No access modification specified";
                    return false;
                }

                static QHash<QString, FunctionModification::Modifiers> modifierNames;
                if (modifierNames.isEmpty()) {
                    modifierNames["private"] = Modification::Private;
                    modifierNames["public"] = Modification::Public;
                    modifierNames["protected"] = Modification::Protected;
                    modifierNames["friendly"] = Modification::Friendly;
                    modifierNames["rename"] = Modification::Rename;
                    modifierNames["final"] = Modification::Final;
                    modifierNames["non-final"] = Modification::NonFinal;
                }

                if (!modifierNames.contains(modifier)) {
                    m_error = QString("Unknown access modifier: '%1'").arg(modifier);
                    return false;
                }

                mod->modifiers |= modifierNames[modifier];
            }
            break;
        case StackElement::RemoveArgument:
            if (topElement.type != StackElement::ModifyArgument) {
                m_error = "Removing argument requires argument modification as parent";
                return false;
            }

            m_function_mods.last().argument_mods.last().removed = true;

            break;

        case StackElement::ModifyField:
            {
                QString name = attributes["name"];
                if (name.isEmpty())
                    break;
                FieldModification fm;
                fm.name = name;
                fm.modifiers = 0;

                QString read = attributes["read"];
                QString write = attributes["write"];

                if (read == "true") fm.modifiers |= FieldModification::Readable;
                if (write == "true") fm.modifiers |= FieldModification::Writable;

                m_field_mods << fm;
            }
            break;
        case StackElement::AddClass: // qtd - fully :)
            {
                if (!(topElement.type & StackElement::ComplexTypeEntryMask)) {
                    m_error = QString::fromLatin1("Add class to module requires complex type as parent"
                                                  ", was=%1").arg(topElement.type, 0, 16);
                    return false;
                }
                QString class_name = attributes["name"];
                ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(topElement.entry);
                if(!class_name.isEmpty())
                    ctype->includedClasses << class_name;
            }
            break;
        case StackElement::ModifyFunction:
            {
                if (!(topElement.type & StackElement::ComplexTypeEntryMask)) {
                    m_error = QString::fromLatin1("Modify function requires complex type as parent"
                                                  ", was=%1").arg(topElement.type, 0, 16);
                    return false;
                }
                QString signature = attributes["signature"];

                signature = QMetaObject::normalizedSignature(signature.toLocal8Bit().constData());
                if (signature.isEmpty()) {
                    m_error = "No signature for modified function";
                    return false;
                }

                FunctionModification mod;
                mod.signature = signature;

                QString access = attributes["access"].toLower();
                if (!access.isEmpty()) {
                    if (access == QLatin1String("private"))
                        mod.modifiers |= Modification::Private;
                    else if (access == QLatin1String("protected"))
                        mod.modifiers |= Modification::Protected;
                    else if (access == QLatin1String("public"))
                        mod.modifiers |= Modification::Public;
                    else if (access == QLatin1String("final"))
                        mod.modifiers |= Modification::Final;
                    else if (access == QLatin1String("non-final"))
                        mod.modifiers |= Modification::NonFinal;
                    else {
                        m_error = QString::fromLatin1("Bad access type '%1'").arg(access);
                        return false;
                    }
                }

                if (convertBoolean(attributes["deprecated"], "deprecated", false)) {
                    mod.modifiers |= Modification::Deprecated;
                }

                QString remove = attributes["remove"].toLower();
                if (!remove.isEmpty()) {
                    if (remove == QLatin1String("all"))
                        mod.removal = TypeSystem::All;
                    else if (remove == QLatin1String("java"))
                        mod.removal = TypeSystem::TargetLangAndNativeCode;
                    else {
                        m_error = QString::fromLatin1("Bad removal type '%1'").arg(remove);
                        return false;
                    }
                }

                QString rename = attributes["rename"];
                if (!rename.isEmpty()) {
                    mod.renamedToName = rename;
                    mod.modifiers |= Modification::Rename;
                }

                QString association = attributes["associated-to"];
                if (!association.isEmpty())
                    mod.association = association;

                mod.modifiers |= (convertBoolean(attributes["virtual-slot"], "virtual-slot", false) ? Modification::VirtualSlot : 0);
                mod.modifiers |= (convertBoolean(attributes["allow-as-slot"], "allow-as-slot", false) ? Modification::AllowAsSlot : 0);
                mod.modifiers |= (convertBoolean(attributes["private-signal"], "private-signal", false) ? Modification::PrivateSignal : 0);

                m_function_mods << mod;
            }
            break;
        case StackElement::ReplaceDefaultExpression:
            if (!(topElement.type & StackElement::ModifyArgument)) {
                m_error = "Replace default expression only allowed as child of argument modification";
                return false;
            }

            if (attributes["with"].isEmpty()) {
               m_error = "Default expression replaced with empty string. Use remove-default-expression instead.";
               return false;
            }

            m_function_mods.last().argument_mods.last().replaced_default_expression = attributes["with"];
            break;
        case StackElement::RemoveDefaultExpression:
            m_function_mods.last().argument_mods.last().removed_default_expression = true;
            break;
        case StackElement::CustomMetaConstructor:
        case StackElement::CustomMetaDestructor:
            {
                CustomFunction *func = new CustomFunction(attributes["name"]);
                func->param_name = attributes["param-name"];
                element->value.customFunction = func;
            }
            break;
        case StackElement::ReferenceCount:
            {
                if (topElement.type != StackElement::ModifyArgument) {
                    m_error = "reference-count must be child of modify-argument";
                    return false;
                }

                ReferenceCount rc;
                rc.threadSafe = convertBoolean(attributes["thread-safe"], "thread-safe", false);

                static QHash<QString, ReferenceCount::Action> actions;
                if (actions.isEmpty()) {
                    actions["add"] = ReferenceCount::Add;
                    actions["add-all"] = ReferenceCount::AddAll;
                    actions["remove"] = ReferenceCount::Remove;
                    actions["set"] = ReferenceCount::Set;
                    actions["ignore"] = ReferenceCount::Ignore;
                }
                rc.action = actions.value(attributes["action"].toLower(), ReferenceCount::Invalid);

                rc.variableName = attributes["variable-name"];
                if (rc.action != ReferenceCount::Ignore && rc.variableName.isEmpty()) {
                    m_error = "variable-name attribute must be specified";
                    return false;
                }

                rc.declareVariable = attributes["declare-variable"];
                rc.conditional = attributes["conditional"];

                static QHash<QString, int> accessRights;
                if (accessRights.isEmpty()) {
                    accessRights["private"] = ReferenceCount::Private;
                    accessRights["public"] = ReferenceCount::Public;
                    accessRights["protected"] = ReferenceCount::Protected;
                    accessRights["friendly"] = ReferenceCount::Friendly;
                }
                rc.access = accessRights.value(attributes["access"].toLower(), 0);
                if (rc.access == 0) {
                    m_error = "unrecognized access value: " + attributes["access"];
                    return false;
                }

                if (rc.action == ReferenceCount::Invalid) {
                    m_error = "unrecognized value for action attribute. supported actions:";
                    foreach (QString action, actions.keys())
                        m_error += " " + action;
                }

                m_function_mods.last().argument_mods.last().referenceCounts.append(rc);
            }
            break;
        case StackElement::InjectCode:
            {
                if (((topElement.type & StackElement::ComplexTypeEntryMask) == 0)
                    && (topElement.type != StackElement::ModifyFunction)
                    && (topElement.type != StackElement::Root)) {
                    m_error = "wrong parent type for code injection";
                    return false;
                }

                static QHash<QString, TypeSystem::Language> languageNames;
                if (languageNames.isEmpty()) {
                    languageNames["java"] = TypeSystem::TargetLangCode;
                    languageNames["java-free"] = TypeSystem::TargetLangFreeCode;
                    languageNames["native"] = TypeSystem::NativeCode;
                    languageNames["shell"] = TypeSystem::ShellCode;
                    languageNames["shell-declaration"] = TypeSystem::ShellDeclaration;
                    languageNames["library-initializer"] = TypeSystem::PackageInitializer;
                    languageNames["destructor-function"] = TypeSystem::DestructorFunction;
                    languageNames["constructors"] = TypeSystem::Constructors;
                    languageNames["interface"] = TypeSystem::Interface;
                }

                QString className = attributes["class"].toLower();
                if (!languageNames.contains(className)) {
                    m_error = QString("Invalid class specifier: '%1'").arg(className);
                    return false;
                }


                static QHash<QString, CodeSnip::Position> positionNames;
                if (positionNames.isEmpty()) {
                    positionNames["beginning"] = CodeSnip::Beginning;
                    positionNames["end"] = CodeSnip::End;
                }

                QString position = attributes["position"].toLower();
                if (!positionNames.contains(position)) {
                    m_error = QString("Invalid position: '%1'").arg(position);
                    return false;
                }

                CodeSnip snip;
                snip.language = languageNames[className];
                snip.position = positionNames[position];

                if (snip.language == TypeSystem::Interface && topElement.type != StackElement::InterfaceTypeEntry) {
                    m_error = "Interface code injections must be direct child of an interface type entry";
                    return false;
                }

                if (topElement.type == StackElement::ModifyFunction) {
                    FunctionModification mod = m_function_mods.last();
                    if (snip.language == TypeSystem::ShellDeclaration) {
                        m_error = "no function implementation in shell declaration in which to inject code";
                        return false;
                    }

                    m_function_mods.last().snips << snip;
                    element->type = StackElement::InjectCodeInFunction;
                } else if (topElement.type == StackElement::Root) {
                    ((TypeSystemTypeEntry *) element->entry)->snips << snip;

                } else if (topElement.type != StackElement::Root) {
                    m_code_snips << snip;
                }
            }
            break;
        case StackElement::Include:
            {
                QString location = attributes["location"].toLower();

                static QHash<QString, Include::IncludeType> locationNames;
                if (locationNames.isEmpty()) {
                    locationNames["global"] = Include::IncludePath;
                    locationNames["local"] = Include::LocalPath;
                    locationNames["java"] = Include::TargetLangImport;
                }

                if (!locationNames.contains(location)) {
                    m_error = QString("Location not recognized: '%1'").arg(location);
                    return false;
                }

                Include::IncludeType loc = locationNames[location];
                Include inc(loc, attributes["file-name"]);

                ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
                if (topElement.type & StackElement::ComplexTypeEntryMask) {
                    ctype->setInclude(inc);
                } else if (topElement.type == StackElement::ExtraIncludes) {
                    ctype->addExtraInclude(inc);
                } else {
                    m_error = "Only supported parents are complex types and extra-includes";
                    return false;
                }

                inc = ctype->include();
                IncludeList lst = ctype->extraIncludes();
                ctype = ctype->designatedInterface();
                if (ctype != 0) {
                    ctype->setExtraIncludes(lst);
                    ctype->setInclude(inc);
                }
            }
            break;
        case StackElement::Rejection:
            {
                QString cls = attributes["class"];
                QString function = attributes["function-name"];
                QString field = attributes["field-name"];
                QString enum_ = attributes["enum-name"];
                if (cls == "*" && function == "*" && field == "*" && enum_ == "*") {
                    m_error = "bad reject entry, neither 'class', 'function-name' nor "
                              "'field' specified";
                    return false;
                }
                m_database->addRejection(cls, function, field, enum_);
            }
            break;
        case StackElement::Template:
            element->value.templateEntry = new TemplateEntry(attributes["name"]);
            break;
        case StackElement::TemplateInstanceEnum:
            if (!(topElement.type & StackElement::CodeSnipMask) &&
                  (topElement.type != StackElement::Template) &&
                  (topElement.type != StackElement::CustomMetaConstructor) &&
                  (topElement.type != StackElement::CustomMetaDestructor) &&
                  (topElement.type != StackElement::ConversionRule))
            {
                m_error = "Can only insert templates into code snippets, templates, custom-constructors, custom-destructors or conversion-rule.";
                return false;
            }
            element->value.templateInstance = new TemplateInstance(attributes["name"]);
            break;
        case StackElement::Replace:
            if (topElement.type != StackElement::TemplateInstanceEnum) {
                m_error = "Can only insert replace rules into insert-template.";
                return false;
            }
            element->parent->value.templateInstance->addReplaceRule(attributes["from"],attributes["to"]);
            break;
        case StackElement::PackageDepend:
            {
                QString package = attributes["package"];
                if (package.isEmpty()) {
                    m_error = "bad package depend entry";
                    return false;
                }
                m_defaultDepends << package;
            }
            break;
        default:
            break; // nada
        };
    }

    current = element;
    return true;
}

TypeDatabase *TypeDatabase::instance()
{
    static TypeDatabase *db = new TypeDatabase();
    return db;
}

TypeDatabase::TypeDatabase() : m_suppressWarnings(true), m_includeEclipseWarnings(false)
{
    addType(new StringTypeEntry("QString"));

    StringTypeEntry *e = new StringTypeEntry("QLatin1String");
    e->setPreferredConversion(false);
    addType(e);

    e = new StringTypeEntry("QStringRef");
    e->setPreferredConversion(false);
    addType(e);

    e = new StringTypeEntry("QXmlStreamStringRef");
    e->setPreferredConversion(false);
    addType(e);

    addType(new CharTypeEntry("QChar"));

    CharTypeEntry *c = new CharTypeEntry("QLatin1Char");
    c->setPreferredConversion(false);
    addType(c);

    {
        VariantTypeEntry *qvariant = new VariantTypeEntry("QVariant");
        qvariant->setCodeGeneration(TypeEntry::GenerateAll);
        addType(qvariant);
    }

    {
        JObjectWrapperTypeEntry *wrapper = new JObjectWrapperTypeEntry("JObjectWrapper");
        wrapper->setCodeGeneration(TypeEntry::GenerateNothing);
        addType(wrapper);
    }

    addType(new ThreadTypeEntry());
    addType(new VoidTypeEntry());

    // Predefined containers...
    addType(new ContainerTypeEntry("QList", ContainerTypeEntry::ListContainer));
    addType(new ContainerTypeEntry("QStringList", ContainerTypeEntry::StringListContainer));
    addType(new ContainerTypeEntry("QLinkedList", ContainerTypeEntry::LinkedListContainer));
    addType(new ContainerTypeEntry("QVector", ContainerTypeEntry::VectorContainer));
    addType(new ContainerTypeEntry("QStack", ContainerTypeEntry::StackContainer));
    addType(new ContainerTypeEntry("QSet", ContainerTypeEntry::SetContainer));
    addType(new ContainerTypeEntry("QMap", ContainerTypeEntry::MapContainer));
    addType(new ContainerTypeEntry("QHash", ContainerTypeEntry::HashContainer));
    addType(new ContainerTypeEntry("QPair", ContainerTypeEntry::PairContainer));
    addType(new ContainerTypeEntry("QQueue", ContainerTypeEntry::QueueContainer));
    addType(new ContainerTypeEntry("QMultiMap", ContainerTypeEntry::MultiMapContainer));

    // Custom types...
//    addType(new QModelIndexTypeEntry());

    addRemoveFunctionToTemplates(this);
}

bool TypeDatabase::parseFile(const QString &filename, const QString &source_dir, bool generate)
{
    QString name(filename);
    QFileInfo info(name);
    if(!info.exists())
    {
        Q_ASSERT(!source_dir.isEmpty());
        name = source_dir + "/" + filename;
    }
    QFile file(name);
    Q_ASSERT(file.exists());
    QXmlInputSource source(&file);

    int count = m_entries.size();

    QXmlSimpleReader reader;
    Handler handler(this, source_dir, generate);

    reader.setContentHandler(&handler);
    reader.setErrorHandler(&handler);

    bool ok = reader.parse(&source, false);

    int newCount = m_entries.size();

    ReportHandler::debugSparse(QString::fromLatin1("Parsed: '%1', %2 new entries")
                               .arg(filename)
                               .arg(newCount - count));

    return ok;
}

QString PrimitiveTypeEntry::javaObjectName() const
{
    static QHash<QString, QString> table;
    if (table.isEmpty()) {
        table["boolean"] = "Boolean";
        table["byte"] = "Byte";
        table["char"] = "Character";
        table["short"] = "Short";
        table["int"] = "Integer";
        table["long"] = "Long";
        table["float"] = "Float";
        table["double"] = "Double";
    }
    Q_ASSERT(table.contains(targetLangName()));
    return table[targetLangName()];
}

ContainerTypeEntry *TypeDatabase::findContainerType(const QString &name)
{
    QString template_name = name;

    int pos = name.indexOf('<');
    if (pos > 0)
        template_name = name.left(pos);

    TypeEntry *type_entry = findType(template_name);
    if (type_entry && type_entry->isContainer())
        return static_cast<ContainerTypeEntry *>(type_entry);
    return 0;
}

PrimitiveTypeEntry *TypeDatabase::findTargetLangPrimitiveType(const QString &java_name)
{
    foreach (QList<TypeEntry *> entries, m_entries.values()) {
        foreach (TypeEntry *e, entries) {
            if (e && e->isPrimitive()) {
                PrimitiveTypeEntry *pe = static_cast<PrimitiveTypeEntry *>(e);
                if (pe->targetLangName() == java_name && pe->preferredConversion())
                    return pe;
            }
        }
    }

    return 0;
}

IncludeList TypeDatabase::extraIncludes(const QString &className)
{
    ComplexTypeEntry *typeEntry = findComplexType(className);
    if (typeEntry != 0)
        return typeEntry->extraIncludes();
    else
        return IncludeList();
}



QString Include::toString() const
{
    if (type == IncludePath)
        return "#include <" + name + '>';
    else if (type == LocalPath)
        return "#include \"" + name + "\"";
    else
        return "import " + name + ";";
}

QString Modification::accessModifierString() const
{
    if (isPrivate()) return "private";
    if (isProtected()) return "protected";
    if (isPublic()) return "public";
    if (isFriendly()) return "friendly";
    return QString();
}

FunctionModificationList ComplexTypeEntry::functionModifications(const QString &signature) const
{
    FunctionModificationList lst;
    for (int i=0; i<m_function_mods.count(); ++i) {
        FunctionModification mod = m_function_mods.at(i);
        if (mod.signature == signature) {
            lst << mod;
        }
    }

    return lst;
}

FieldModification ComplexTypeEntry::fieldModification(const QString &name) const
{
    for (int i=0; i<m_field_mods.size(); ++i)
        if (m_field_mods.at(i).name == name)
            return m_field_mods.at(i);
    FieldModification mod;
    mod.name = name;
    mod.modifiers = FieldModification::Readable | FieldModification::Writable;
    return mod;
}

QString ContainerTypeEntry::javaPackage() const
{
    if (m_type == PairContainer)
        return "qt";
    return "java.util";
}

QString ContainerTypeEntry::targetLangName() const
{

    switch (m_type) {
    case StringListContainer: return "List";
    case ListContainer: return "List";
    case LinkedListContainer: return "LinkedList";
    case VectorContainer: return "List";
    case StackContainer: return "Stack";
    case QueueContainer: return "Queue";
    case SetContainer: return "Set";
    case MapContainer: return "SortedMap";
    case MultiMapContainer: return "SortedMap";
    case HashContainer: return "HashMap";
        //     case MultiHashCollectio: return "MultiHash";
    case PairContainer: return "QPair";
    default:
        qWarning("bad type... %d", m_type);
        break;
    }
    return QString();
}

QString ContainerTypeEntry::qualifiedCppName() const
{
    if (m_type == StringListContainer)
        return "QStringList";
    return ComplexTypeEntry::qualifiedCppName();
}

QString EnumTypeEntry::javaQualifier() const
{
    TypeEntry *te = TypeDatabase::instance()->findType(m_qualifier);
    if (te != 0)
        return te->targetLangName();
    else
        return m_qualifier;
}

QString EnumTypeEntry::jniName() const
{
    return "jint";
}

QString FlagsTypeEntry::jniName() const
{
    return "jint";
}

void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue)
{
    m_enum_redirections << EnumValueRedirection(rejected, usedValue);
}

QString EnumTypeEntry::enumValueRedirection(const QString &value) const
{
    for (int i=0; i<m_enum_redirections.size(); ++i)
        if (m_enum_redirections.at(i).rejected == value)
            return m_enum_redirections.at(i).used;
    return QString();
}

QString FlagsTypeEntry::qualifiedTargetLangName() const
{
    return m_enum->javaQualifier() + "." + targetLangName();
}


void TypeDatabase::addRejection(const QString &class_name, const QString &function_name,
                                const QString &field_name, const QString &enum_name)
{
    TypeRejection r;
    r.class_name = class_name;
    r.function_name = function_name;
    r.field_name = field_name;
    r.enum_name = enum_name;

    m_rejections << r;
}

bool TypeDatabase::isClassRejected(const QString &class_name)
{
    if (!m_rebuild_classes.isEmpty())
        return !m_rebuild_classes.contains(class_name);

    foreach (const TypeRejection &r, m_rejections)
        if (r.class_name == class_name && r.function_name == "*" && r.field_name == "*" && r.enum_name == "*") {
            return true;
        }
    return false;
}

bool TypeDatabase::isEnumRejected(const QString &class_name, const QString &enum_name)
{
    foreach (const TypeRejection &r, m_rejections) {
        if (r.enum_name == enum_name
            && (r.class_name == class_name || r.class_name == "*")) {
            return true;
        }
    }

    return false;
}

bool TypeDatabase::isFunctionRejected(const QString &class_name, const QString &function_name)
{
    foreach (const TypeRejection &r, m_rejections)
        if (r.function_name == function_name &&
            (r.class_name == class_name || r.class_name == "*"))
            return true;
    return false;
}


bool TypeDatabase::isFieldRejected(const QString &class_name, const QString &field_name)
{
    foreach (const TypeRejection &r, m_rejections)
        if (r.field_name == field_name &&
            (r.class_name == class_name || r.class_name == "*"))
            return true;
    return false;
}

FlagsTypeEntry *TypeDatabase::findFlagsType(const QString &name) const
{
    FlagsTypeEntry *fte = (FlagsTypeEntry *) findType(name);
    return fte ? fte : (FlagsTypeEntry *) m_flags_entries.value(name);
}

QString TypeDatabase::globalNamespaceClassName(const TypeEntry * /*entry*/) {
    return QLatin1String("Global");
}


/*!
 * The Visual Studio 2002 compiler doesn't support these symbols,
 * which our typedefs unforntuatly expand to.
 */
QString fixCppTypeName(const QString &name)
{
    if (name == "long long") return "qint64";
    else if (name == "unsigned long long") return "quint64";
    return name;
}

QString formattedCodeHelper(QTextStream &s, Indentor &indentor, QStringList &lines) {
    bool multilineComment = false;
    bool lastEmpty = true;
    QString lastLine;
    while (!lines.isEmpty()) {
        const QString line = lines.takeFirst().trimmed();
        if (line.isEmpty()) {
            if (!lastEmpty)
                s << endl;
            lastEmpty = true;
            continue;
        } else {
            lastEmpty = false;
        }
        if (line.startsWith("/*"))
            multilineComment = true;

        if (multilineComment) {
            s << indentor;
            if (line.startsWith("*"))
                s << " ";
            s << line << endl;
            if (line.endsWith("*/"))
                multilineComment = false;
        } else if (line.startsWith("}")) {
            return line;
        } else if (line.endsWith("}")) {
            s << indentor << line << endl;
            return 0;
        } else if(line.endsWith("{")) {
            s << indentor << line << endl;
            QString tmp;
            {
                Indentation indent(indentor);
                tmp = formattedCodeHelper(s, indentor, lines);
            }
            if (!tmp.isNull()) {
                s << indentor << tmp << endl;
            }
            lastLine = tmp;
            continue;
        } else {
            s << indentor;
            if (!lastLine.isEmpty() &&
                !lastLine.endsWith(";") &&
                !line.startsWith("@") &&
                !line.startsWith("//") &&
                !lastLine.startsWith("//") &&
                !lastLine.endsWith("}") &&
                !line.startsWith("{"))
                s << "    ";
            s << line << endl;
        }
        lastLine = line;
    }
    return 0;
}


QTextStream &CodeSnip::formattedCode(QTextStream &s, Indentor &indentor) const
{
    QStringList lst(code().split("\n"));
    while (!lst.isEmpty()) {
        QString tmp = formattedCodeHelper(s, indentor, lst);
        if (!tmp.isNull()) {
            s << indentor << tmp << endl;
        }
    }
    s.flush();
    return s;
}

QString TemplateInstance::expandCode() const{
    TemplateEntry *templateEntry = TypeDatabase::instance()->findTemplate(m_name);
    if(templateEntry){
        QString res = templateEntry->code();
        foreach(QString key, replaceRules.keys()){
            res.replace(key, replaceRules[key]);
        }
        return "// TEMPLATE - " + m_name + " - START" + res + "// TEMPLATE - " + m_name + " - END";
    }
    else{
        ReportHandler::warning("insert-template referring to non-existing template '" + m_name + "'");
    }
    return QString();
}


QString CodeSnipAbstract::code() const{
    QString res;
    foreach(CodeSnipFragment *codeFrag, codeList){
        res.append(codeFrag->code());
    }
    return res;
}

QString CodeSnipFragment::code() const{
    if(m_instance)
        return m_instance->expandCode();
    else
        return m_code;
}

QString FunctionModification::toString() const
{
    QString str = signature + QLatin1String("->");
    if (modifiers & AccessModifierMask) {
        switch (modifiers & AccessModifierMask) {
        case Private: str += QLatin1String("private"); break;
        case Protected: str += QLatin1String("protected"); break;
        case Public: str += QLatin1String("public"); break;
        case Friendly: str += QLatin1String("friendly"); break;
        }
    }

    if (modifiers & Final) str += QLatin1String("final");
    if (modifiers & NonFinal) str += QLatin1String("non-final");

    if (modifiers & Readable) str += QLatin1String("readable");
    if (modifiers & Writable) str += QLatin1String("writable");

    if (modifiers & CodeInjection) {
        foreach (CodeSnip s, snips) {
            str += QLatin1String("\n//code injection:\n");
            str += s.code();
        }
    }

    if (modifiers & Rename) str += QLatin1String("renamed:") + renamedToName;

    if (modifiers & Deprecated) str += QLatin1String("deprecate");

    if (modifiers & ReplaceExpression) str += QLatin1String("replace-expression");

    return str;
}

static void removeFunction(ComplexTypeEntry *e, const char *signature)
{
    FunctionModification mod;
    mod.signature = QMetaObject::normalizedSignature(signature);
    mod.removal = TypeSystem::All;

    e->addFunctionModification(mod);
}




static void injectCode(ComplexTypeEntry *e,
                       const char *signature,
                       const QByteArray &code,
                       const ArgumentMap &args,
                       TypeSystem::Language lang = TypeSystem::NativeCode)
{
    CodeSnip snip;
    snip.language = lang;
    snip.position = CodeSnip::Beginning;
    snip.addCode(QString::fromLatin1(code));
    snip.argumentMap = args;

    FunctionModification mod;
    mod.signature = QMetaObject::normalizedSignature(signature);
    mod.snips << snip;
    mod.modifiers = Modification::CodeInjection;
    e->addFunctionModification(mod);
}


static void addRemoveFunctionToTemplates(TypeDatabase *db)
{
    ContainerTypeEntry *qvector = db->findContainerType(QLatin1String("QVector"));
    removeFunction(qvector, "constData() const");
    removeFunction(qvector, "data() const");
    removeFunction(qvector, "data()");
    removeFunction(qvector, "first()");
    removeFunction(qvector, "last()");
    removeFunction(qvector, "operator[](int)");
    removeFunction(qvector, "operator[](int) const");
    removeFunction(qvector, "operator=(QVector<T>)");

    ContainerTypeEntry *qlist = db->findContainerType(QLatin1String("QList"));
    removeFunction(qlist, "constData() const");
    removeFunction(qlist, "data() const");
    removeFunction(qlist, "data()");
    removeFunction(qlist, "back()");
    removeFunction(qlist, "front()");
    removeFunction(qlist, "first()");
    removeFunction(qlist, "last()");
    removeFunction(qlist, "operator[](int)");
    removeFunction(qlist, "operator[](int) const");
    removeFunction(qlist, "operator=(QList<T>)");

    ContainerTypeEntry *qqueue = db->findContainerType(QLatin1String("QQueue"));
    removeFunction(qqueue, "head() const");


    ArgumentMap args1;
    args1[1] = QLatin1String("$1");
    ArgumentMap args2 = args1;
    args2[2] = QLatin1String("$2");

    QByteArray code =
        "\nif ($1 >= __qt_this->size() || $1 < 0) {"
        "\n   __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
        "\n                       QString::fromLatin1(\"Accessing container of size %3 at %4\")"
        "\n                       .arg(__qt_this->size()).arg($1).toLatin1());"
        "\n   return;"
        "\n}";

    QByteArray code_with_return = QByteArray(code).replace("return;", "return 0;");

    QByteArray code_index_length =
        "\nif ($1 < 0 || $2 < 0 || ($1 + $2) >= __qt_this->size()) {"
        "\n   __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
        "\n                       QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
        "\n                       .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
        "\n   return;"
        "\n}";

    QByteArray code_non_empty =
        "\nif (__qt_this->isEmpty()) {"
        "\n   __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
        "\n                       QString::fromLatin1(\"Accessing empty container...\").toLatin1());"
        "\n   return;"
        "\n}";

    QByteArray code_two_indices =
        "\nif ($1 < 0 || $2 < 0 || $1 >= __qt_this->size() || $2 >= __qt_this->size()) {"
        "\n   __jni_env->ThrowNew(__jni_env->FindClass(\"java/lang/IndexOutOfBoundsException\"),"
        "\n                       QString::fromLatin1(\"Accessing container of size %3 from %4 to %5\")"
        "\n                       .arg(__qt_this->size()).arg($1).arg($1+$2).toLatin1());"
        "\n   return;"
        "\n}";
/* qtd2
    { // QVector safty...
        injectCode(qvector, "at(int) const", code_with_return, args1, TypeSystem::TargetLangCode);
        injectCode(qvector, "replace(int,T)", code, args1, TypeSystem::TargetLangCode);
        injectCode(qvector, "remove(int)", code, args1, TypeSystem::TargetLangCode);
        injectCode(qvector, "remove(int, int)", code_index_length, args2, TypeSystem::TargetLangCode);
        injectCode(qvector, "pop_back()", code_non_empty, ArgumentMap(), TypeSystem::TargetLangCode);
        injectCode(qvector, "pop_front()", code_non_empty, ArgumentMap(), TypeSystem::TargetLangCode);
    }

    { // QList safty...
        injectCode(qlist, "at(int) const", code_with_return, args1);
        injectCode(qlist, "replace(int, T)", code, args1);
        injectCode(qlist, "pop_back()", code_non_empty, ArgumentMap());
        injectCode(qlist, "pop_front()", code_non_empty, ArgumentMap());
        injectCode(qlist, "swap(int, int)", code_two_indices, args2);
        injectCode(qlist, "move(int, int)", code_two_indices, args2);
        injectCode(qlist, "removeAt(int)", code, args1);
        injectCode(qlist, "takeAt(int)", code_with_return, args1);
    }
*/
}