Mercurial > projects > qtd
diff generator/abstractmetabuilder.cpp @ 1:e78566595089
initial import
author | mandel |
---|---|
date | Mon, 11 May 2009 16:01:50 +0000 |
parents | |
children | ae34188ddd84 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/generator/abstractmetabuilder.cpp Mon May 11 16:01:50 2009 +0000 @@ -0,0 +1,2601 @@ +/**************************************************************************** +** +** 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 "abstractmetabuilder.h" +#include "reporthandler.h" + +#include "ast.h" +#include "binder.h" +#include "control.h" +#include "default_visitor.h" +#include "dumptree.h" +#include "lexer.h" +#include "parser.h" +#include "tokens.h" + +#include <QtCore/QDebug> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> +#include <QtCore/QVariant> + +static QString strip_template_args(const QString &name) +{ + int pos = name.indexOf('<'); + return pos < 0 ? name : name.left(pos); +} + +static QHash<QString, QString> *operator_names; +QString rename_operator(const QString &oper) +{ + QString op = oper.trimmed(); + if (!operator_names) { + operator_names = new QHash<QString, QString>; + + operator_names->insert("+", "add"); + operator_names->insert("-", "subtract"); + operator_names->insert("*", "multiply"); + operator_names->insert("/", "divide"); + operator_names->insert("%", "modulo"); + operator_names->insert("&", "and"); + operator_names->insert("|", "or"); + operator_names->insert("^", "xor"); + operator_names->insert("~", "negate"); + operator_names->insert("<<", "shift_left"); + operator_names->insert(">>", "shift_right"); + + // assigments + operator_names->insert("=", "assign"); + operator_names->insert("+=", "add_assign"); + operator_names->insert("-=", "subtract_assign"); + operator_names->insert("*=", "multiply_assign"); + operator_names->insert("/=", "divide_assign"); + operator_names->insert("%=", "modulo_assign"); + operator_names->insert("&=", "and_assign"); + operator_names->insert("|=", "or_assign"); + operator_names->insert("^=", "xor_assign"); + operator_names->insert("<<=", "shift_left_assign"); + operator_names->insert(">>=", "shift_right_assign"); + + // Logical + operator_names->insert("&&", "logical_and"); + operator_names->insert("||", "logical_or"); + operator_names->insert("!", "not"); + + // incr/decr + operator_names->insert("++", "increment"); + operator_names->insert("--", "decrement"); + + // compare + operator_names->insert("<", "less"); + operator_names->insert(">", "greater"); + operator_names->insert("<=", "less_or_equal"); + operator_names->insert(">=", "greater_or_equal"); + operator_names->insert("!=", "not_equal"); + operator_names->insert("==", "equal"); + + // other + operator_names->insert("[]", "subscript"); + operator_names->insert("->", "pointer"); + } + + if (!operator_names->contains(op)) { + TypeDatabase *tb = TypeDatabase::instance(); + + TypeParser::Info typeInfo = TypeParser::parse(op); + QString cast_to_name = typeInfo.qualified_name.join("::"); + TypeEntry *te = tb->findType(cast_to_name); + if ((te && te->codeGeneration() == TypeEntry::GenerateNothing) + || tb->isClassRejected(cast_to_name)) { + return QString(); + } else if (te) { + return "operator_cast_" + typeInfo.qualified_name.join("_"); + } else { + ReportHandler::warning(QString("unknown operator '%1'").arg(op)); + return "operator " + op; + } + } + + return "operator_" + operator_names->value(op); +} + +AbstractMetaBuilder::AbstractMetaBuilder() + : m_current_class(0) +{ +} + +void AbstractMetaBuilder::checkFunctionModifications() +{ + TypeDatabase *types = TypeDatabase::instance(); + SingleTypeEntryHash entryHash = types->entries(); + QList<TypeEntry *> entries = entryHash.values(); + foreach (TypeEntry *entry, entries) { + if (entry == 0) + continue; + if (!entry->isComplex() || entry->codeGeneration() == TypeEntry::GenerateNothing) + continue; + + ComplexTypeEntry *centry = static_cast<ComplexTypeEntry *>(entry); + FunctionModificationList modifications = centry->functionModifications(); + + foreach (FunctionModification modification, modifications) { + QString signature = modification.signature; + + QString name = signature.trimmed(); + name = name.mid(0, signature.indexOf("(")); + + AbstractMetaClass *clazz = m_meta_classes.findClass(centry->qualifiedCppName()); + if (clazz == 0) + continue; + + AbstractMetaFunctionList functions = clazz->functions(); + bool found = false; + QStringList possibleSignatures; + foreach (AbstractMetaFunction *function, functions) { + if (function->minimalSignature() == signature && function->implementingClass() == clazz) { + found = true; + break; + } + + if (function->originalName() == name) + possibleSignatures.append(function->minimalSignature() + " in " + function->implementingClass()->name()); + } + + if (!found) { + QString warning + = QString("signature '%1' for function modification in '%2' not found. Possible candidates: %3") + .arg(signature) + .arg(clazz->qualifiedCppName()) + .arg(possibleSignatures.join(", ")); + + ReportHandler::warning(warning); + } + } + } +} + +AbstractMetaClass *AbstractMetaBuilder::argumentToClass(ArgumentModelItem argument) +{ + AbstractMetaClass *returned = 0; + bool ok = false; + AbstractMetaType *type = translateType(argument->type(), &ok); + if (ok && type != 0 && type->typeEntry() != 0 && type->typeEntry()->isComplex()) { + const TypeEntry *entry = type->typeEntry(); + returned = m_meta_classes.findClass(entry->name()); + } + delete type; + return returned; +} + +/** + * Checks the argument of a hash function and flags the type if it is a complex type + */ +void AbstractMetaBuilder::registerHashFunction(FunctionModelItem function_item) +{ + ArgumentList arguments = function_item->arguments(); + if (arguments.size() == 1) { + if (AbstractMetaClass *cls = argumentToClass(arguments.at(0))) + cls->setHasHashFunction(true); + } +} + +/** + * Check if a class has a debug stream operator that can be used as toString + */ + +void AbstractMetaBuilder::registerToStringCapability(FunctionModelItem function_item) +{ + ArgumentList arguments = function_item->arguments(); + if (arguments.size() == 2) { + if (arguments.at(0)->type().toString() == "QDebug"){ + ArgumentModelItem arg = arguments.at(1); + if (AbstractMetaClass *cls = argumentToClass(arg)) { + if (arg->type().indirections() < 2) { + cls->setToStringCapability(function_item); + } + } + } + } +} + +void AbstractMetaBuilder::traverseCompareOperator(FunctionModelItem item) { + ArgumentList arguments = item->arguments(); + if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) { + AbstractMetaClass *comparer_class = argumentToClass(arguments.at(0)); + AbstractMetaClass *compared_class = argumentToClass(arguments.at(1)); + if (comparer_class != 0 && compared_class != 0) { + AbstractMetaClass *old_current_class = m_current_class; + m_current_class = comparer_class; + + AbstractMetaFunction *meta_function = traverseFunction(item); + if (meta_function != 0 && !meta_function->isInvalid()) { + // Strip away first argument, since that is the containing object + AbstractMetaArgumentList arguments = meta_function->arguments(); + arguments.pop_front(); + meta_function->setArguments(arguments); + + meta_function->setFunctionType(AbstractMetaFunction::GlobalScopeFunction); + + meta_function->setOriginalAttributes(meta_function->attributes()); + setupFunctionDefaults(meta_function, comparer_class); + + comparer_class->addFunction(meta_function); + } else if (meta_function != 0) { + delete meta_function; + } + + m_current_class = old_current_class; + } + } +} + +void AbstractMetaBuilder::traverseStreamOperator(FunctionModelItem item) +{ + ArgumentList arguments = item->arguments(); + if (arguments.size() == 2 && item->accessPolicy() == CodeModel::Public) { + AbstractMetaClass *streamClass = argumentToClass(arguments.at(0)); + AbstractMetaClass *streamedClass = argumentToClass(arguments.at(1)); + + if (streamClass != 0 && streamedClass != 0 + && (streamClass->name() == "QDataStream" || streamClass->name() == "QTextStream")) { + AbstractMetaClass *old_current_class = m_current_class; + m_current_class = streamedClass; + AbstractMetaFunction *streamFunction = traverseFunction(item); + + if (streamFunction != 0 && !streamFunction->isInvalid()) { + QString name = item->name(); + streamFunction->setFunctionType(AbstractMetaFunction::GlobalScopeFunction); + + if (name.endsWith("<<")) + streamFunction->setName("writeTo"); + else + streamFunction->setName("readFrom"); + + // Strip away last argument, since that is the containing object + AbstractMetaArgumentList arguments = streamFunction->arguments(); + arguments.pop_back(); + streamFunction->setArguments(arguments); + + *streamFunction += AbstractMetaAttributes::Final; + *streamFunction += AbstractMetaAttributes::Public; + streamFunction->setOriginalAttributes(streamFunction->attributes()); + + streamFunction->setType(0); + + setupFunctionDefaults(streamFunction, streamedClass); + + streamedClass->addFunction(streamFunction); + streamedClass->typeEntry()->addExtraInclude(streamClass->typeEntry()->include()); + + m_current_class = old_current_class; + } + } + } +} + +void AbstractMetaBuilder::fixQObjectForScope(TypeDatabase *types, + NamespaceModelItem scope) +{ + foreach (ClassModelItem item, scope->classes()) { + QString qualified_name = item->qualifiedName().join("::"); + TypeEntry *entry = types->findType(qualified_name); + if (entry) { + if (isQObject(qualified_name) && entry->isComplex()) { + ((ComplexTypeEntry *) entry)->setQObject(true); + } + } + } + + foreach (NamespaceModelItem item, scope->namespaceMap().values()) { + if (scope != item) + fixQObjectForScope(types, item); + } +} + +static bool class_less_than(AbstractMetaClass *a, AbstractMetaClass *b) +{ + return a->name() < b->name(); +} + + +void AbstractMetaBuilder::sortLists() +{ + qSort(m_meta_classes.begin(), m_meta_classes.end(), class_less_than); + foreach (AbstractMetaClass *cls, m_meta_classes) { + cls->sortFunctions(); + } +} + +bool AbstractMetaBuilder::build() +{ + Q_ASSERT(!m_file_name.isEmpty()); + ReportHandler::setContext("Parser"); + + QFile file(m_file_name); + + if (!file.open(QFile::ReadOnly)) + return false; + + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + QByteArray contents = stream.readAll().toUtf8(); + file.close(); + + Control control; + Parser p(&control); + pool __pool; + + TranslationUnitAST *ast = p.parse(contents, contents.size(), &__pool); + + CodeModel model; + Binder binder(&model, p.location()); + m_dom = binder.run(ast); + + + ReportHandler::setContext("MetaJavaBuilder"); + + + pushScope(model_dynamic_cast<ScopeModelItem>(m_dom)); + + QHash<QString, ClassModelItem> typeMap = m_dom->classMap(); + + + // fix up QObject's in the type system.. + TypeDatabase *types = TypeDatabase::instance(); + fixQObjectForScope(types, model_dynamic_cast<NamespaceModelItem>(m_dom)); + + + // Start the generation... + foreach (ClassModelItem item, typeMap.values()) { + AbstractMetaClass *cls = traverseClass(item); + addAbstractMetaClass(cls); + } + + + QHash<QString, NamespaceModelItem> namespaceMap = m_dom->namespaceMap(); + foreach (NamespaceModelItem item, namespaceMap.values()) { + AbstractMetaClass *meta_class = traverseNamespace(item); + if (meta_class) + m_meta_classes << meta_class; + } + + + // Some trickery to support global-namespace enums... + QHash<QString, EnumModelItem> enumMap = m_dom->enumMap(); + m_current_class = 0; + foreach (EnumModelItem item, enumMap) { + AbstractMetaEnum *meta_enum = traverseEnum(item, 0, QSet<QString>()); + + if (meta_enum) { + QString package = meta_enum->typeEntry()->javaPackage(); + QString globalName = TypeDatabase::globalNamespaceClassName(meta_enum->typeEntry()); + + AbstractMetaClass *global = m_meta_classes.findClass(package + "." + globalName); + if (!global) { + ComplexTypeEntry *gte = new ObjectTypeEntry(globalName); + gte->setTargetLangPackage(meta_enum->typeEntry()->javaPackage()); + gte->setCodeGeneration(meta_enum->typeEntry()->codeGeneration()); + global = createMetaClass(); + global->setTypeEntry(gte); + *global += AbstractMetaAttributes::Final; + *global += AbstractMetaAttributes::Public; + *global += AbstractMetaAttributes::Fake; + + m_meta_classes << global; + } + + global->addEnum(meta_enum); + meta_enum->setEnclosingClass(global); + meta_enum->typeEntry()->setQualifier(globalName); + } + + + } + + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = m_dom->typeAliases(); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + addAbstractMetaClass(cls); + } + + + + + foreach (AbstractMetaClass *cls, m_meta_classes) { + if (!cls->isInterface() && !cls->isNamespace()) { + setupInheritance(cls); + } + } + + + foreach (AbstractMetaClass *cls, m_meta_classes) { + cls->fixFunctions(); + + if (cls->typeEntry() == 0) { + ReportHandler::warning(QString("class '%1' does not have an entry in the type system") + .arg(cls->name())); + } else { + if (!cls->hasConstructors() && !cls->isFinalInCpp() && !cls->isInterface() && !cls->isNamespace()) + cls->addDefaultConstructor(); + } + + if (cls->isAbstract() && !cls->isInterface()) { + cls->typeEntry()->setLookupName(cls->typeEntry()->targetLangName() + "_ConcreteWrapper"); + } + } + + QList<TypeEntry *> entries = TypeDatabase::instance()->entries().values(); + foreach (const TypeEntry *entry, entries) { + if (entry->isPrimitive()) + continue; + + if ((entry->isValue() || entry->isObject()) + && !entry->isString() + && !entry->isChar() + && !entry->isContainer() + && !entry->isCustom() + && !entry->isVariant() + && !m_meta_classes.findClass(entry->qualifiedCppName())) { + ReportHandler::warning(QString("type '%1' is specified in typesystem, but not defined. This could potentially lead to compilation errors.") + .arg(entry->qualifiedCppName())); + } + + if (entry->isEnum()) { + QString pkg = entry->javaPackage(); + QString name = (pkg.isEmpty() ? QString() : pkg + ".") + + ((EnumTypeEntry *) entry)->javaQualifier(); + AbstractMetaClass *cls = m_meta_classes.findClass(name); + + if (!cls) { + ReportHandler::warning(QString("namespace '%1' for enum '%2' is not declared") + .arg(name).arg(entry->targetLangName())); + } else { + AbstractMetaEnum *e = cls->findEnum(entry->targetLangName()); + if (!e) + ReportHandler::warning(QString("enum '%1' is specified in typesystem, " + "but not declared") + .arg(entry->qualifiedCppName())); + } + } + } + + { + FunctionList hash_functions = m_dom->findFunctions("qHash"); + foreach (FunctionModelItem item, hash_functions) { + registerHashFunction(item); + } + } + + { + FunctionList hash_functions = m_dom->findFunctions("operator<<"); + foreach (FunctionModelItem item, hash_functions) { + registerToStringCapability(item); + } + } + + { + FunctionList compare_operators = m_dom->findFunctions("operator==") + + m_dom->findFunctions("operator<=") + + m_dom->findFunctions("operator>=") + + m_dom->findFunctions("operator<") + + m_dom->findFunctions("operator>"); + foreach (FunctionModelItem item, compare_operators) { + traverseCompareOperator(item); + } + } + + { + FunctionList stream_operators = m_dom->findFunctions("operator<<") + m_dom->findFunctions("operator>>"); + foreach (FunctionModelItem item, stream_operators) { + traverseStreamOperator(item); + } + } + + figureOutEnumValues(); + figureOutDefaultEnumArguments(); + checkFunctionModifications(); + + foreach (AbstractMetaClass *cls, m_meta_classes) { + setupEquals(cls); + setupComparable(cls); + setupClonable(cls); + } + + dumpLog(); + + sortLists(); + + return true; +} + + +void AbstractMetaBuilder::addAbstractMetaClass(AbstractMetaClass *cls) +{ + if (!cls) + return; + + cls->setOriginalAttributes(cls->attributes()); + if (cls->typeEntry()->isContainer()) { + m_templates << cls; + } else { + m_meta_classes << cls; + if (cls->typeEntry()->designatedInterface()) { + AbstractMetaClass *interface = cls->extractInterface(); + m_meta_classes << interface; + ReportHandler::debugSparse(QString(" -> interface '%1'").arg(interface->name())); + } + } +} + + +AbstractMetaClass *AbstractMetaBuilder::traverseNamespace(NamespaceModelItem namespace_item) +{ + QString namespace_name = (!m_namespace_prefix.isEmpty() ? m_namespace_prefix + "::" : QString()) + namespace_item->name(); + NamespaceTypeEntry *type = TypeDatabase::instance()->findNamespaceType(namespace_name); + + if (TypeDatabase::instance()->isClassRejected(namespace_name)) { + m_rejected_classes.insert(namespace_name, GenerationDisabled); + return 0; + } + + if (!type) { + ReportHandler::warning(QString("namespace '%1' does not have a type entry") + .arg(namespace_name)); + return 0; + } + + AbstractMetaClass *meta_class = createMetaClass(); + meta_class->setTypeEntry(type); + + *meta_class += AbstractMetaAttributes::Public; + + m_current_class = meta_class; + + ReportHandler::debugSparse(QString("namespace '%1.%2'") + .arg(meta_class->package()) + .arg(namespace_item->name())); + + traverseEnums(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class, namespace_item->enumsDeclarations()); + traverseFunctions(model_dynamic_cast<ScopeModelItem>(namespace_item), meta_class); +// traverseClasses(model_dynamic_cast<ScopeModelItem>(namespace_item)); + + pushScope(model_dynamic_cast<ScopeModelItem>(namespace_item)); + m_namespace_prefix = currentScope()->qualifiedName().join("::"); + + + ClassList classes = namespace_item->classes(); + foreach (ClassModelItem cls, classes) { + AbstractMetaClass *mjc = traverseClass(cls); + addAbstractMetaClass(mjc); + } + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = namespace_item->typeAliases(); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + addAbstractMetaClass(cls); + } + + + + // Traverse namespaces recursively + QList<NamespaceModelItem> inner_namespaces = namespace_item->namespaceMap().values(); + foreach (const NamespaceModelItem &ni, inner_namespaces) { + AbstractMetaClass *mjc = traverseNamespace(ni); + addAbstractMetaClass(mjc); + } + + m_current_class = 0; + + + popScope(); + m_namespace_prefix = currentScope()->qualifiedName().join("::"); + + if (!type->include().isValid()) { + QFileInfo info(namespace_item->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return meta_class; +} + +struct Operator +{ + enum Type { Plus, ShiftLeft, None }; + + Operator() : type(None) { } + + int calculate(int x) { + switch (type) { + case Plus: return x + value; + case ShiftLeft: return x << value; + case None: return x; + } + return x; + } + + Type type; + int value; +}; + + + +Operator findOperator(QString *s) { + const char *names[] = { + "+", + "<<" + }; + + for (int i=0; i<Operator::None; ++i) { + QString name = QLatin1String(names[i]); + QString str = *s; + int splitPoint = str.indexOf(name); + if (splitPoint > 0) { + bool ok; + QString right = str.mid(splitPoint + name.length()); + Operator op; + op.value = right.toInt(&ok); + if (ok) { + op.type = Operator::Type(i); + *s = str.left(splitPoint).trimmed(); + return op; + } + } + } + return Operator(); +} + +int AbstractMetaBuilder::figureOutEnumValue(const QString &stringValue, + int oldValuevalue, + AbstractMetaEnum *meta_enum, + AbstractMetaFunction *meta_function) +{ + if (stringValue.isEmpty()) + return oldValuevalue; + + QStringList stringValues = stringValue.split("|"); + + int returnValue = 0; + + bool matched = false; + + for (int i=0; i<stringValues.size(); ++i) { + QString s = stringValues.at(i).trimmed(); + + bool ok; + int v; + + Operator op = findOperator(&s); + + if (s.length() > 0 && s.at(0) == QLatin1Char('0')) + v = s.toUInt(&ok, 0); + else + v = s.toInt(&ok); + + if (ok) { + matched = true; + + } else if (m_enum_values.contains(s)) { + v = m_enum_values[s]->value(); + matched = true; + + } else { + AbstractMetaEnumValue *ev = 0; + + if (meta_enum && (ev = meta_enum->values().find(s))) { + v = ev->value(); + matched = true; + + } else if (meta_enum && (ev = meta_enum->enclosingClass()->findEnumValue(s, meta_enum))) { + v = ev->value(); + matched = true; + + } else { + if (meta_enum) + ReportHandler::warning("unhandled enum value: " + s + " in " + + meta_enum->enclosingClass()->name() + "::" + + meta_enum->name()); + else + ReportHandler::warning("unhandled enum value: Unknown enum"); + } + } + + if (matched) + returnValue |= op.calculate(v); + } + + if (!matched) { + QString warn = QString("unmatched enum %1").arg(stringValue); + + if (meta_function != 0) { + warn += QString(" when parsing default value of '%1' in class '%2'") + .arg(meta_function->name()) + .arg(meta_function->implementingClass()->name()); + } + + ReportHandler::warning(warn); + returnValue = oldValuevalue; + } + + return returnValue; +} + +void AbstractMetaBuilder::figureOutEnumValuesForClass(AbstractMetaClass *meta_class, + QSet<AbstractMetaClass *> *classes) +{ + AbstractMetaClass *base = meta_class->baseClass(); + + if (base != 0 && !classes->contains(base)) + figureOutEnumValuesForClass(base, classes); + + if (classes->contains(meta_class)) + return; + + AbstractMetaEnumList enums = meta_class->enums(); + foreach (AbstractMetaEnum *e, enums) { + if (!e) + ReportHandler::warning("bad enum in class " + meta_class->name()); + AbstractMetaEnumValueList lst = e->values(); + int value = 0; + for (int i=0; i<lst.size(); ++i) { + value = figureOutEnumValue(lst.at(i)->stringValue(), value, e); + lst.at(i)->setValue(value); + value++; + } + + // Check for duplicate values... + EnumTypeEntry *ete = e->typeEntry(); + if (!ete->forceInteger()) { + QHash<int, AbstractMetaEnumValue *> entries; + foreach (AbstractMetaEnumValue *v, lst) { + + bool vRejected = ete->isEnumValueRejected(v->name()); + + AbstractMetaEnumValue *current = entries.value(v->value()); + if (current) { + bool currentRejected = ete->isEnumValueRejected(current->name()); + if (!currentRejected && !vRejected) { + ReportHandler::warning( + QString("duplicate enum values: %1::%2, %3 and %4 are %5, already rejected: (%6)") + .arg(meta_class->name()) + .arg(e->name()) + .arg(v->name()) + .arg(entries[v->value()]->name()) + .arg(v->value()) + .arg(ete->enumValueRejections().join(", "))); + continue; + } + } + + if (!vRejected) + entries[v->value()] = v; + } + + // Entries now contain all the original entries, no + // rejected ones... Use this to generate the enumValueRedirection table. + foreach (AbstractMetaEnumValue *reject, lst) { + if (!ete->isEnumValueRejected(reject->name())) + continue; + + AbstractMetaEnumValue *used = entries.value(reject->value()); + if (!used) { + ReportHandler::warning( + QString::fromLatin1("Rejected enum has no alternative...: %1::%2\n") + .arg(meta_class->name()) + .arg(reject->name())); + continue; + } + ete->addEnumValueRedirection(reject->name(), used->name()); + } + + } + } + + + + *classes += meta_class; +} + + +void AbstractMetaBuilder::figureOutEnumValues() +{ + // Keep a set of classes that we already traversed. We use this to + // enforce that we traverse base classes prior to subclasses. + QSet<AbstractMetaClass *> classes; + foreach (AbstractMetaClass *c, m_meta_classes) { + figureOutEnumValuesForClass(c, &classes); + } +} + +void AbstractMetaBuilder::figureOutDefaultEnumArguments() +{ + foreach (AbstractMetaClass *meta_class, m_meta_classes) { + foreach (AbstractMetaFunction *meta_function, meta_class->functions()) { + foreach (AbstractMetaArgument *arg, meta_function->arguments()) { + + QString expr = arg->defaultValueExpression(); + if (expr.isEmpty()) + continue; + + if (!meta_function->replacedDefaultExpression(meta_function->implementingClass(), + arg->argumentIndex()+1).isEmpty()) { + continue; + } + + QString new_expr = expr; + if (arg->type()->isEnum()) { + QStringList lst = expr.split(QLatin1String("::")); + if (lst.size() == 1) { + QVector<AbstractMetaClass *> classes(1, meta_class); + AbstractMetaEnum *e = 0; + while (!classes.isEmpty() && e == 0) { + if (classes.front() != 0) { + classes << classes.front()->baseClass(); + + AbstractMetaClassList interfaces = classes.front()->interfaces(); + foreach (AbstractMetaClass *interface, interfaces) + classes << interface->primaryInterfaceImplementor(); + + e = classes.front()->findEnumForValue(expr); + } + + classes.pop_front(); + } + + if (e != 0) { +/* qtd new_expr = QString("%1.%2") + .arg(e->typeEntry()->qualifiedTargetLangName()) + .arg(expr); + */ +// new_expr = arg->type()->typeEntry()->targetLangName() + "." + expr; + new_expr = arg->type()->typeEntry()->qualifiedTargetLangName() + "." + expr; + } else { + ReportHandler::warning("Cannot find enum constant for value '" + expr + "' in '" + meta_class->name() + "' or any of its super classes"); + } + } else if (lst.size() == 2) { + AbstractMetaClass *cl = m_meta_classes.findClass(lst.at(0)); + if (!cl) { + ReportHandler::warning("missing required class for enums: " + lst.at(0)); + continue; + } +/* qtd new_expr = QString("%1.%2.%3") + .arg(cl->typeEntry()->qualifiedTargetLangName()) + .arg(arg->type()->name()) + .arg(lst.at(1)); + */ +// new_expr = arg->type()->typeEntry()->targetLangName() + "." + lst.at(1); + new_expr = arg->type()->typeEntry()->qualifiedTargetLangName() + "." + lst.at(1); + } else { + ReportHandler::warning("bad default value passed to enum " + expr); + } + + } else if(arg->type()->isFlags()) { + const FlagsTypeEntry *flagsEntry = + static_cast<const FlagsTypeEntry *>(arg->type()->typeEntry()); + EnumTypeEntry *enumEntry = flagsEntry->originator(); + AbstractMetaEnum *meta_enum = m_meta_classes.findEnum(enumEntry); + if (!meta_enum) { + ReportHandler::warning("unknown required enum " + enumEntry->qualifiedCppName()); + continue; + } + + int value = figureOutEnumValue(expr, 0, meta_enum, meta_function); + new_expr = QString::number(value); + + } else if (arg->type()->isPrimitive()) { + AbstractMetaEnumValue *value = 0; + if (expr.contains("::")) + value = m_meta_classes.findEnumValue(expr); + if (!value) + value = meta_class->findEnumValue(expr, 0); + + if (value) { + new_expr = QString::number(value->value()); + } else if (expr.contains(QLatin1Char('+'))) { + new_expr = QString::number(figureOutEnumValue(expr, 0, 0)); + + } + + + + } + + arg->setDefaultValueExpression(new_expr); + } + } + } +} + + +AbstractMetaEnum *AbstractMetaBuilder::traverseEnum(EnumModelItem enum_item, AbstractMetaClass *enclosing, const QSet<QString> &enumsDeclarations) +{ + // Skipping private enums. + if (enum_item->accessPolicy() == CodeModel::Private) { + return 0; + } + + QString qualified_name = enum_item->qualifiedName().join("::"); + + TypeEntry *type_entry = TypeDatabase::instance()->findType(qualified_name); + QString enum_name = enum_item->name(); + + QString class_name; + if (m_current_class) + class_name = m_current_class->typeEntry()->qualifiedCppName(); + + if (TypeDatabase::instance()->isEnumRejected(class_name, enum_name)) { + m_rejected_enums.insert(qualified_name, GenerationDisabled); + return 0; + } + + if (!type_entry || !type_entry->isEnum()) { + QString context = m_current_class ? m_current_class->name() : QLatin1String(""); + ReportHandler::warning(QString("enum '%1' does not have a type entry or is not an enum") + .arg(qualified_name)); + m_rejected_enums.insert(qualified_name, NotInTypeSystem); + return 0; + } + + AbstractMetaEnum *meta_enum = createMetaEnum(); + if ( enumsDeclarations.contains(qualified_name) + || enumsDeclarations.contains(enum_name)) { + meta_enum->setHasQEnumsDeclaration(true); + } + + meta_enum->setTypeEntry((EnumTypeEntry *) type_entry); + switch (enum_item->accessPolicy()) { + case CodeModel::Public: *meta_enum += AbstractMetaAttributes::Public; break; + case CodeModel::Protected: *meta_enum += AbstractMetaAttributes::Protected; break; +// case CodeModel::Private: *meta_enum += AbstractMetaAttributes::Private; break; + default: break; + } + + ReportHandler::debugMedium(QString(" - traversing enum %1").arg(meta_enum->fullName())); + + foreach (EnumeratorModelItem value, enum_item->enumerators()) { + + AbstractMetaEnumValue *meta_enum_value = createMetaEnumValue(); + meta_enum_value->setName(value->name()); + // Deciding the enum value... + + meta_enum_value->setStringValue(value->value()); + meta_enum->addEnumValue(meta_enum_value); + + ReportHandler::debugFull(" - " + meta_enum_value->name() + " = " + + meta_enum_value->value()); + + // Add into global register... + if (enclosing) + m_enum_values[enclosing->name() + "::" + meta_enum_value->name()] = meta_enum_value; + else + m_enum_values[meta_enum_value->name()] = meta_enum_value; + } + + m_enums << meta_enum; + + return meta_enum; +} + +AbstractMetaClass *AbstractMetaBuilder::traverseTypeAlias(TypeAliasModelItem typeAlias) +{ + QString class_name = strip_template_args(typeAlias->name()); + + QString full_class_name = class_name; + // we have an inner class + if (m_current_class) { + full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName()) + + "::" + full_class_name; + } + + // If we haven't specified anything for the typedef, then we don't care + ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name); + if (type == 0) + return 0; + + if (type->isObject()) + static_cast<ObjectTypeEntry *>(type)->setQObject(isQObject(strip_template_args(typeAlias->type().qualifiedName().join("::")))); + + AbstractMetaClass *meta_class = createMetaClass(); + meta_class->setTypeAlias(true); + meta_class->setTypeEntry(type); + meta_class->setBaseClassNames(QStringList() << typeAlias->type().qualifiedName().join("::")); + *meta_class += AbstractMetaAttributes::Public; + + // Set the default include file name + if (!type->include().isValid()) { + QFileInfo info(typeAlias->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return meta_class; +} + +AbstractMetaClass *AbstractMetaBuilder::traverseClass(ClassModelItem class_item) +{ + QString class_name = strip_template_args(class_item->name()); + QString full_class_name = class_name; + + // we have inner an class + if (m_current_class) { + full_class_name = strip_template_args(m_current_class->typeEntry()->qualifiedCppName()) + + "::" + full_class_name; + } + + ComplexTypeEntry *type = TypeDatabase::instance()->findComplexType(full_class_name); + RejectReason reason = NoReason; + + + if (TypeDatabase::instance()->isClassRejected(full_class_name)) { + reason = GenerationDisabled; + } else if (!type) { + TypeEntry *te = TypeDatabase::instance()->findType(full_class_name); + if (te && !te->isComplex()) + reason = RedefinedToNotClass; + else + reason = NotInTypeSystem; + } else if (type->codeGeneration() == TypeEntry::GenerateNothing) { + reason = GenerationDisabled; + } + + if (reason != NoReason) { + m_rejected_classes.insert(full_class_name, reason); + return 0; + } + + if (type->isObject()) { + ((ObjectTypeEntry *)type)->setQObject(isQObject(full_class_name)); + } + + AbstractMetaClass *meta_class = createMetaClass(); + meta_class->setTypeEntry(type); + meta_class->setBaseClassNames(class_item->baseClasses()); + *meta_class += AbstractMetaAttributes::Public; + + AbstractMetaClass *old_current_class = m_current_class; + m_current_class = meta_class; + + if (type->isContainer()) { + ReportHandler::debugSparse(QString("container: '%1'").arg(full_class_name)); + } else { + ReportHandler::debugSparse(QString("class: '%1'").arg(meta_class->fullName())); + } + + TemplateParameterList template_parameters = class_item->templateParameters(); + QList<TypeEntry *> template_args; + template_args.clear(); + for (int i=0; i<template_parameters.size(); ++i) { + const TemplateParameterModelItem ¶m = template_parameters.at(i); + TemplateArgumentEntry *param_type = new TemplateArgumentEntry(param->name()); + param_type->setOrdinal(i); + template_args.append(param_type); + } + meta_class->setTemplateArguments(template_args); + + parseQ_Property(meta_class, class_item->propertyDeclarations()); + + traverseFunctions(model_dynamic_cast<ScopeModelItem>(class_item), meta_class); + traverseEnums(model_dynamic_cast<ScopeModelItem>(class_item), meta_class, class_item->enumsDeclarations()); + traverseFields(model_dynamic_cast<ScopeModelItem>(class_item), meta_class); + + // Inner classes + { + QList<ClassModelItem> inner_classes = class_item->classMap().values(); + foreach (const ClassModelItem &ci, inner_classes) { + AbstractMetaClass *cl = traverseClass(ci); + if (cl) { + cl->setEnclosingClass(meta_class); + m_meta_classes << cl; + } + } + + } + + // Go through all typedefs to see if we have defined any + // specific typedefs to be used as classes. + TypeAliasList typeAliases = class_item->typeAliases(); + foreach (TypeAliasModelItem typeAlias, typeAliases) { + AbstractMetaClass *cls = traverseTypeAlias(typeAlias); + if (cls != 0) { + cls->setEnclosingClass(meta_class); + addAbstractMetaClass(cls); + } + } + + + m_current_class = old_current_class; + + // Set the default include file name + if (!type->include().isValid()) { + QFileInfo info(class_item->fileName()); + type->setInclude(Include(Include::IncludePath, info.fileName())); + } + + return meta_class; +} + +AbstractMetaField *AbstractMetaBuilder::traverseField(VariableModelItem field, const AbstractMetaClass *cls) +{ + QString field_name = field->name(); + QString class_name = m_current_class->typeEntry()->qualifiedCppName(); + + // Ignore friend decl. + if (field->isFriend()) + return 0; + + if (field->accessPolicy() == CodeModel::Private) + return 0; + + if (TypeDatabase::instance()->isFieldRejected(class_name, field_name)) { + m_rejected_fields.insert(class_name + "::" + field_name, GenerationDisabled); + return 0; + } + + + AbstractMetaField *meta_field = createMetaField(); + meta_field->setName(field_name); + meta_field->setEnclosingClass(cls); + + bool ok; + TypeInfo field_type = field->type(); + AbstractMetaType *meta_type = translateType(field_type, &ok); + + if (!meta_type || !ok) { + ReportHandler::warning(QString("skipping field '%1::%2' with unmatched type '%3'") + .arg(m_current_class->name()) + .arg(field_name) + .arg(TypeInfo::resolveType(field_type, currentScope()->toItem()).qualifiedName().join("::"))); + delete meta_field; + return 0; + } + + meta_field->setType(meta_type); + + uint attr = 0; + if (field->isStatic()) + attr |= AbstractMetaAttributes::Static; + + CodeModel::AccessPolicy policy = field->accessPolicy(); + if (policy == CodeModel::Public) + attr |= AbstractMetaAttributes::Public; + else if (policy == CodeModel::Protected) + attr |= AbstractMetaAttributes::Protected; + else + attr |= AbstractMetaAttributes::Private; + meta_field->setAttributes(attr); + + return meta_field; +} + +void AbstractMetaBuilder::traverseFields(ScopeModelItem scope_item, AbstractMetaClass *meta_class) +{ + foreach (VariableModelItem field, scope_item->variables()) { + AbstractMetaField *meta_field = traverseField(field, meta_class); + + if (meta_field) { + meta_field->setOriginalAttributes(meta_field->attributes()); + meta_class->addField(meta_field); + } + } +} + +void AbstractMetaBuilder::setupFunctionDefaults(AbstractMetaFunction *meta_function, AbstractMetaClass *meta_class) +{ + // Set the default value of the declaring class. This may be changed + // in fixFunctions later on + meta_function->setDeclaringClass(meta_class); + + // Some of the queries below depend on the implementing class being set + // to function properly. Such as function modifications + meta_function->setImplementingClass(meta_class); + + if (meta_function->name() == "operator_equal") + meta_class->setHasEqualsOperator(true); + + if (!meta_function->isFinalInTargetLang() + && meta_function->isRemovedFrom(meta_class, TypeSystem::TargetLangCode)) { + *meta_function += AbstractMetaAttributes::FinalInCpp; + } +} + +void AbstractMetaBuilder::traverseFunctions(ScopeModelItem scope_item, AbstractMetaClass *meta_class) +{ + foreach (FunctionModelItem function, scope_item->functions()) { + AbstractMetaFunction *meta_function = traverseFunction(function); + + if (meta_function) { + meta_function->setOriginalAttributes(meta_function->attributes()); + if (meta_class->isNamespace()) + *meta_function += AbstractMetaAttributes::Static; + + if (!meta_function->isInvalid()) { + if (QPropertySpec *read = meta_class->propertySpecForRead(meta_function->name())) { + if (read->type() == meta_function->type()->typeEntry()) { + *meta_function += AbstractMetaAttributes::PropertyReader; + meta_function->setPropertySpec(read); +// printf("%s is reader for %s\n", +// qPrintable(meta_function->name()), +// qPrintable(read->name())); + } + } else if (QPropertySpec *write = + meta_class->propertySpecForWrite(meta_function->name())) { + if (write->type() == meta_function->arguments().at(0)->type()->typeEntry()) { + *meta_function += AbstractMetaAttributes::PropertyWriter; + meta_function->setPropertySpec(write); +/* + // qtd D properties syntax, rename setter from setPropertyName to just propertyName + AbstractMetaFunction *f_copy = new AbstractMetaFunction(); + *f_copy = *meta_function; + + QString f_name = meta_function->name(); + QString new_name = f_name.midRef(3, 1).toString().toLower() + f_name.right(f_name.length() - 4); + meta_function->setName(new_name); + // f_copy->setOwnerClass(meta_function->ownerClass()); + ((AbstractMetaClass*)(meta_function->ownerClass()))->addFunction(f_copy); // hack qtd2 + // +*/ +// printf("%s is writer for %s\n", +// qPrintable(meta_function->name()), +// qPrintable(write->name())); + } + } else if (QPropertySpec *reset = + meta_class->propertySpecForReset(meta_function->name())) { + *meta_function += AbstractMetaAttributes::PropertyResetter; + meta_function->setPropertySpec(reset); +// printf("%s is resetter for %s\n", +// qPrintable(meta_function->name()), +// qPrintable(reset->name())); + } + } + + + bool isInvalidDestructor = meta_function->isDestructor() && meta_function->isPrivate(); + bool isInvalidConstructor = meta_function->isConstructor() + && (meta_function->isPrivate() || meta_function->isInvalid()); + if ((isInvalidDestructor || isInvalidConstructor) + && !meta_class->hasNonPrivateConstructor()) { + *meta_class += AbstractMetaAttributes::Final; + } else if (meta_function->isConstructor() && !meta_function->isPrivate()) { + *meta_class -= AbstractMetaAttributes::Final; + meta_class->setHasNonPrivateConstructor(true); + } + + // Classes with virtual destructors should always have a shell class + // (since we aren't registering the destructors, we need this extra check) + if (meta_function->isDestructor() && !meta_function->isFinal()) + meta_class->setForceShellClass(true); + + if (!meta_function->isDestructor() + && !meta_function->isInvalid() + && (!meta_function->isConstructor() || !meta_function->isPrivate())) { + + if (meta_class->typeEntry()->designatedInterface() && !meta_function->isPublic() + && !meta_function->isPrivate()) { + QString warn = QString("non-public function '%1' in interface '%2'") + .arg(meta_function->name()).arg(meta_class->name()); + ReportHandler::warning(warn); + + meta_function->setVisibility(AbstractMetaClass::Public); + } + + setupFunctionDefaults(meta_function, meta_class); + + if (meta_function->isSignal() && meta_class->hasSignal(meta_function)) { + QString warn = QString("signal '%1' in class '%2' is overloaded.") + .arg(meta_function->name()).arg(meta_class->name()); + ReportHandler::warning(warn); + } + + if (meta_function->isSignal() && !meta_class->isQObject()) { + QString warn = QString("signal '%1' in non-QObject class '%2'") + .arg(meta_function->name()).arg(meta_class->name()); + ReportHandler::warning(warn); + } + + meta_class->addFunction(meta_function); + } else if (meta_function->isDestructor() && !meta_function->isPublic()) { + meta_class->setHasPublicDestructor(false); + } + } + } +} + +bool AbstractMetaBuilder::setupInheritance(AbstractMetaClass *meta_class) +{ + Q_ASSERT(!meta_class->isInterface()); + + if (m_setup_inheritance_done.contains(meta_class)) + return true; + m_setup_inheritance_done.insert(meta_class); + + QStringList base_classes = meta_class->baseClassNames(); + + TypeDatabase *types = TypeDatabase::instance(); + + // we only support our own containers and ONLY if there is only one baseclass + if (base_classes.size() == 1 && base_classes.first().count('<') == 1) { + QStringList scope = meta_class->typeEntry()->qualifiedCppName().split("::"); + scope.removeLast(); + for (int i=scope.size(); i>=0; --i) { + QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join("::") + "::" : QString(); + QString complete_name = prefix + base_classes.first(); + TypeParser::Info info = TypeParser::parse(complete_name); + QString base_name = info.qualified_name.join("::"); + + AbstractMetaClass *templ = 0; + foreach (AbstractMetaClass *c, m_templates) { + if (c->typeEntry()->name() == base_name) { + templ = c; + break; + } + } + + if (templ == 0) + templ = m_meta_classes.findClass(base_name); + + if (templ) { + setupInheritance(templ); + inheritTemplate(meta_class, templ, info); + return true; + } + } + + ReportHandler::warning(QString("template baseclass '%1' of '%2' is not known") + .arg(base_classes.first()) + .arg(meta_class->name())); + return false; + } + + int primary = -1; + int primaries = 0; + for (int i=0; i<base_classes.size(); ++i) { + + if (types->isClassRejected(base_classes.at(i))) + continue; + + TypeEntry *base_class_entry = types->findType(base_classes.at(i)); + if (!base_class_entry) { + ReportHandler::warning(QString("class '%1' inherits from unknown base class '%2'") + .arg(meta_class->name()).arg(base_classes.at(i))); + } + + // true for primary base class + else if (!base_class_entry->designatedInterface()) { + if (primaries > 0) { + ReportHandler::warning(QString("class '%1' has multiple primary base classes" + " '%2' and '%3'") + .arg(meta_class->name()) + .arg(base_classes.at(primary)) + .arg(base_class_entry->name())); + return false; + } + primaries++; + primary = i; + } + } + + if (primary >= 0) { + AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(primary)); + if (!base_class) { + ReportHandler::warning(QString("unknown baseclass for '%1': '%2'") + .arg(meta_class->name()) + .arg(base_classes.at(primary))); + return false; + } + meta_class->setBaseClass(base_class); + + if (meta_class->typeEntry()->designatedInterface() != 0 && meta_class->isQObject()) { + ReportHandler::warning(QString("QObject extended by interface type '%1'. This is not supported and the generated Java code will not compile.") + .arg(meta_class->name())); + } else if (meta_class->typeEntry()->designatedInterface() != 0 && base_class != 0 && !base_class->isInterface()) { + ReportHandler::warning(QString("object type '%1' extended by interface type '%2'. The resulting API will be less expressive than the original.") + .arg(base_class->name()) + .arg(meta_class->name())); + } + + } + + for (int i=0; i<base_classes.size(); ++i) { + if (types->isClassRejected(base_classes.at(i))) + continue; + + if (i != primary) { + AbstractMetaClass *base_class = m_meta_classes.findClass(base_classes.at(i)); + if (base_class == 0) { + ReportHandler::warning(QString("class not found for setup inheritance '%1'").arg(base_classes.at(i))); + return false; + } + + setupInheritance(base_class); + + QString interface_name = InterfaceTypeEntry::interfaceName(base_class->name()); + AbstractMetaClass *iface = m_meta_classes.findClass(interface_name); + if (!iface) { + ReportHandler::warning(QString("unknown interface for '%1': '%2'") + .arg(meta_class->name()) + .arg(interface_name)); + return false; + } + meta_class->addInterface(iface); + + AbstractMetaClassList interfaces = iface->interfaces(); + foreach (AbstractMetaClass *iface, interfaces) + meta_class->addInterface(iface); + } + } + + return true; +} + +void AbstractMetaBuilder::traverseEnums(ScopeModelItem scope_item, AbstractMetaClass *meta_class, const QStringList &enumsDeclarations) +{ + EnumList enums = scope_item->enums(); + foreach (EnumModelItem enum_item, enums) { + AbstractMetaEnum *meta_enum = traverseEnum(enum_item, meta_class, QSet<QString>::fromList(enumsDeclarations)); + if (meta_enum) { + meta_enum->setOriginalAttributes(meta_enum->attributes()); + meta_class->addEnum(meta_enum); + meta_enum->setEnclosingClass(meta_class); + } + } +} + +AbstractMetaFunction *AbstractMetaBuilder::traverseFunction(FunctionModelItem function_item) +{ + QString function_name = function_item->name(); + QString class_name = m_current_class->typeEntry()->qualifiedCppName(); + + if (TypeDatabase::instance()->isFunctionRejected(class_name, function_name)) { + m_rejected_functions.insert(class_name + "::" + function_name, GenerationDisabled); + return 0; + } + + + Q_ASSERT(function_item->functionType() == CodeModel::Normal + || function_item->functionType() == CodeModel::Signal + || function_item->functionType() == CodeModel::Slot); + + if (function_item->isFriend()) + return 0; + + + QString cast_type; + + if (function_name.startsWith("operator")) { + function_name = rename_operator(function_name.mid(8)); + if (function_name.isEmpty()) { + m_rejected_functions.insert(class_name + "::" + function_name, + GenerationDisabled); + return 0; + } + if (function_name.contains("_cast_")) + cast_type = function_name.mid(14).trimmed(); + } + + AbstractMetaFunction *meta_function = createMetaFunction(); + meta_function->setConstant(function_item->isConstant()); + + ReportHandler::debugMedium(QString(" - %2()").arg(function_name)); + + meta_function->setName(function_name); + meta_function->setOriginalName(function_item->name()); + + if (function_item->isAbstract()) + *meta_function += AbstractMetaAttributes::Abstract; + + if (!meta_function->isAbstract()) + *meta_function += AbstractMetaAttributes::Native; + + if (!function_item->isVirtual()) + *meta_function += AbstractMetaAttributes::Final; + + if (function_item->isInvokable()) + *meta_function += AbstractMetaAttributes::Invokable; + + if (function_item->isStatic()) { + *meta_function += AbstractMetaAttributes::Static; + *meta_function += AbstractMetaAttributes::Final; + } + + // Access rights + if (function_item->accessPolicy() == CodeModel::Public) + *meta_function += AbstractMetaAttributes::Public; + else if (function_item->accessPolicy() == CodeModel::Private) + *meta_function += AbstractMetaAttributes::Private; + else + *meta_function += AbstractMetaAttributes::Protected; + + + QString stripped_class_name = class_name; + int cc_pos = stripped_class_name.lastIndexOf("::"); + if (cc_pos > 0) + stripped_class_name = stripped_class_name.mid(cc_pos + 2); + + TypeInfo function_type = function_item->type(); + if (function_name.startsWith('~')) { + meta_function->setFunctionType(AbstractMetaFunction::DestructorFunction); + meta_function->setInvalid(true); + } else if (strip_template_args(function_name) == stripped_class_name) { + meta_function->setFunctionType(AbstractMetaFunction::ConstructorFunction); + meta_function->setName(m_current_class->name()); + } else { + bool ok; + AbstractMetaType *type = 0; + + if (!cast_type.isEmpty()) { + TypeInfo info; + info.setQualifiedName(QStringList(cast_type)); + type = translateType(info, &ok); + } else { + type = translateType(function_type, &ok); + } + + if (!ok) { + ReportHandler::warning(QString("skipping function '%1::%2', unmatched return type '%3'") + .arg(class_name) + .arg(function_item->name()) + .arg(function_item->type().toString())); + m_rejected_functions[class_name + "::" + function_name] = + UnmatchedReturnType; + meta_function->setInvalid(true); + return meta_function; + } + meta_function->setType(type); + + if (function_item->functionType() == CodeModel::Signal) + meta_function->setFunctionType(AbstractMetaFunction::SignalFunction); + else if (function_item->functionType() == CodeModel::Slot) + meta_function->setFunctionType(AbstractMetaFunction::SlotFunction); + } + + ArgumentList arguments = function_item->arguments(); + AbstractMetaArgumentList meta_arguments; + + int first_default_argument = 0; + for (int i=0; i<arguments.size(); ++i) { + ArgumentModelItem arg = arguments.at(i); + + bool ok; + AbstractMetaType *meta_type = translateType(arg->type(), &ok); + if (!meta_type || !ok) { + ReportHandler::warning(QString("skipping function '%1::%2', " + "unmatched parameter type '%3'") + .arg(class_name) + .arg(function_item->name()) + .arg(arg->type().toString())); + m_rejected_functions[class_name + "::" + function_name] = + UnmatchedArgumentType; + meta_function->setInvalid(true); + return meta_function; + } + AbstractMetaArgument *meta_argument = createMetaArgument(); + meta_argument->setType(meta_type); + meta_argument->setName(arg->name()); + meta_argument->setArgumentIndex(i); + meta_arguments << meta_argument; + } + + meta_function->setArguments(meta_arguments); + + // Find the correct default values + for (int i=0; i<arguments.size(); ++i) { + ArgumentModelItem arg = arguments.at(i); + AbstractMetaArgument *meta_arg = meta_arguments.at(i); + if (arg->defaultValue()) { + QString expr = arg->defaultValueExpression(); + if (!expr.isEmpty()) + meta_arg->setOriginalDefaultValueExpression(expr); + + expr = translateDefaultValue(arg, meta_arg->type(), meta_function, m_current_class, i); + if (expr.isEmpty()) { + first_default_argument = i; + } else { + meta_arg->setDefaultValueExpression(expr); + } + + if (meta_arg->type()->isEnum() || meta_arg->type()->isFlags()) { + m_enum_default_arguments + << QPair<AbstractMetaArgument *, AbstractMetaFunction *>(meta_arg, meta_function); + } + + } + } + + // If we where not able to translate the default argument make it + // reset all default arguments before this one too. + for (int i=0; i<first_default_argument; ++i) + meta_arguments[i]->setDefaultValueExpression(QString()); + + if (ReportHandler::debugLevel() == ReportHandler::FullDebug) + foreach(AbstractMetaArgument *arg, meta_arguments) + ReportHandler::debugFull(" - " + arg->toString()); + + return meta_function; +} + + +AbstractMetaType *AbstractMetaBuilder::translateType(const TypeInfo &_typei, bool *ok, bool resolveType, bool resolveScope) +{ + Q_ASSERT(ok); + *ok = true; + + // 1. Test the type info without resolving typedefs in case this is present in the + // type system + TypeInfo typei; + if (resolveType) { + bool ok; + AbstractMetaType *t = translateType(_typei, &ok, false, resolveScope); + if (t != 0 && ok) + return t; + } + + if (!resolveType) + typei = _typei; + else { + // Go through all parts of the current scope (including global namespace) + // to resolve typedefs. The parser does not properly resolve typedefs in + // the global scope when they are referenced from inside a namespace. + // This is a work around to fix this bug since fixing it in resolveType + // seemed non-trivial + int i = m_scopes.size() - 1; + while (i >= 0) { + typei = TypeInfo::resolveType(_typei, m_scopes.at(i--)->toItem()); + if (typei.qualifiedName().join("::") != _typei.qualifiedName().join("::")) + break; + } + + } + + if (typei.isFunctionPointer()) { + *ok = false; + return 0; + } + + TypeParser::Info typeInfo = TypeParser::parse(typei.toString()); + if (typeInfo.is_busted) { + *ok = false; + return 0; + } + + // 2. Handle pointers specified as arrays with unspecified size + bool array_of_unspecified_size = false; + if (typeInfo.arrays.size() > 0) { + array_of_unspecified_size = true; + for (int i=0; i<typeInfo.arrays.size(); ++i) + array_of_unspecified_size = array_of_unspecified_size && typeInfo.arrays.at(i).isEmpty(); + + if (!array_of_unspecified_size) { + TypeInfo newInfo; + //newInfo.setArguments(typei.arguments()); + newInfo.setIndirections(typei.indirections()); + newInfo.setConstant(typei.isConstant()); + newInfo.setFunctionPointer(typei.isFunctionPointer()); + newInfo.setQualifiedName(typei.qualifiedName()); + newInfo.setReference(typei.isReference()); + newInfo.setVolatile(typei.isVolatile()); + + AbstractMetaType *elementType = translateType(newInfo, ok); + if (!ok) + return 0; + + for (int i=typeInfo.arrays.size()-1; i>=0; --i) { + QString s = typeInfo.arrays.at(i); + bool ok; + + int elems = s.toInt(&ok); + if (!ok) + return 0; + + AbstractMetaType *arrayType = createMetaType(); + arrayType->setArrayElementCount(elems); + arrayType->setArrayElementType(elementType); + arrayType->setTypeEntry(new ArrayTypeEntry(elementType->typeEntry())); + decideUsagePattern(arrayType); + + elementType = arrayType; + } + + return elementType; + } else { + typeInfo.indirections += typeInfo.arrays.size(); + } + } + + QStringList qualifier_list = typeInfo.qualified_name; + if (qualifier_list.isEmpty()) { + ReportHandler::warning(QString("horribly broken type '%1'").arg(_typei.toString())); + *ok = false; + return 0; + } + + QString qualified_name = qualifier_list.join("::"); + QString name = qualifier_list.takeLast(); + + // 3. Special case 'void' type + if (name == "void" && typeInfo.indirections == 0) { + return 0; + } + + // 4. Special case QFlags (include instantiation in name) + if (qualified_name == "QFlags") + qualified_name = typeInfo.toString(); + + // 5. Try to find the type + const TypeEntry *type = TypeDatabase::instance()->findType(qualified_name); + + // 6. No? Try looking it up as a flags type + if (!type) + type = TypeDatabase::instance()->findFlagsType(qualified_name); + + // 7. No? Try looking it up as a container type + if (!type) + type = TypeDatabase::instance()->findContainerType(name); + + // 8. No? Check if the current class is a template and this type is one + // of the parameters. + if (type == 0 && m_current_class != 0) { + QList<TypeEntry *> template_args = m_current_class->templateArguments(); + foreach (TypeEntry *te, template_args) { + if (te->name() == qualified_name) + type = te; + } + } + + // 9. Try finding the type by prefixing it with the current + // context and all baseclasses of the current context + if (!type && !TypeDatabase::instance()->isClassRejected(qualified_name) && m_current_class != 0 && resolveScope) { + QStringList contexts; + contexts.append(m_current_class->qualifiedCppName()); + contexts.append(currentScope()->qualifiedName().join("::")); + + + TypeInfo info = typei; + bool subclasses_done = false; + while (!contexts.isEmpty() && type == 0) { + //type = TypeDatabase::instance()->findType(contexts.at(0) + "::" + qualified_name); + + bool ok; + info.setQualifiedName(QStringList() << contexts.at(0) << qualified_name); + AbstractMetaType *t = translateType(info, &ok, true, false); + if (t != 0 && ok) + return t; + + ClassModelItem item = m_dom->findClass(contexts.at(0)); + if (item != 0) + contexts += item->baseClasses(); + contexts.pop_front(); + + // 10. Last resort: Special cased prefix of Qt namespace since the meta object implicitly inherits this, so + // enum types from there may be addressed without any scope resolution in properties. + if (contexts.size() == 0 && !subclasses_done) { + contexts << "Qt"; + subclasses_done = true; + } + } + + } + + if (!type) { + *ok = false; + return 0; + } + + // Used to for diagnostics later... + m_used_types << type; + + // These are only implicit and should not appear in code... + Q_ASSERT(!type->isInterface()); + + AbstractMetaType *meta_type = createMetaType(); + meta_type->setTypeEntry(type); + meta_type->setIndirections(typeInfo.indirections); + meta_type->setReference(typeInfo.is_reference); + meta_type->setConstant(typeInfo.is_constant); + meta_type->setOriginalTypeDescription(_typei.toString()); + decideUsagePattern(meta_type); + + if (meta_type->typeEntry()->isContainer()) { + ContainerTypeEntry::Type container_type = static_cast<const ContainerTypeEntry *>(type)->type(); + + if (container_type == ContainerTypeEntry::StringListContainer) { + TypeInfo info; + info.setQualifiedName(QStringList() << "QString"); + AbstractMetaType *targ_type = translateType(info, ok); + + Q_ASSERT(*ok); + Q_ASSERT(targ_type); + + meta_type->addInstantiation(targ_type); + meta_type->setInstantiationInCpp(false); + + } else { + foreach (const TypeParser::Info &ta, typeInfo.template_instantiations) { + TypeInfo info; + info.setConstant(ta.is_constant); + info.setReference(ta.is_reference); + info.setIndirections(ta.indirections); + + info.setFunctionPointer(false); + info.setQualifiedName(ta.instantiationName().split("::")); + + AbstractMetaType *targ_type = translateType(info, ok); + if (!(*ok)) { + delete meta_type; + return 0; + } + + meta_type->addInstantiation(targ_type); + } + } + + if (container_type == ContainerTypeEntry::ListContainer + || container_type == ContainerTypeEntry::VectorContainer + || container_type == ContainerTypeEntry::StringListContainer) { + Q_ASSERT(meta_type->instantiations().size() == 1); + } + } + + return meta_type; +} + +void AbstractMetaBuilder::decideUsagePattern(AbstractMetaType *meta_type) +{ + const TypeEntry *type = meta_type->typeEntry(); + + if (type->isPrimitive() && (meta_type->actualIndirections() == 0 + || (meta_type->isConstant() && meta_type->isReference() && meta_type->indirections() == 0))) { + meta_type->setTypeUsagePattern(AbstractMetaType::PrimitivePattern); + + } else if (type->isVoid()) { + meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + + } else if (type->isString() + && meta_type->indirections() == 0 + && (meta_type->isConstant() == meta_type->isReference() + || meta_type->isConstant())) { + meta_type->setTypeUsagePattern(AbstractMetaType::StringPattern); + + } else if (type->isChar() + && meta_type->indirections() == 0 + && meta_type->isConstant() == meta_type->isReference()) { + meta_type->setTypeUsagePattern(AbstractMetaType::CharPattern); + + } else if (type->isJObjectWrapper() + && meta_type->indirections() == 0 + && meta_type->isConstant() == meta_type->isReference()) { + meta_type->setTypeUsagePattern(AbstractMetaType::JObjectWrapperPattern); + + } else if (type->isVariant() + && meta_type->indirections() == 0 + && meta_type->isConstant() == meta_type->isReference()) { + meta_type->setTypeUsagePattern(AbstractMetaType::VariantPattern); + + } else if (type->isEnum() && meta_type->actualIndirections() == 0) { + meta_type->setTypeUsagePattern(AbstractMetaType::EnumPattern); + + } else if (type->isObject() + && meta_type->indirections() == 0 + && meta_type->isReference()) { + if (((ComplexTypeEntry *) type)->isQObject()) + meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern); + else + meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern); + + } else if (type->isObject() + && meta_type->indirections() == 1) { + if (((ComplexTypeEntry *) type)->isQObject()) + meta_type->setTypeUsagePattern(AbstractMetaType::QObjectPattern); + else + meta_type->setTypeUsagePattern(AbstractMetaType::ObjectPattern); + + // const-references to pointers can be passed as pointers + if (meta_type->isReference() && meta_type->isConstant()) { + meta_type->setReference(false); + meta_type->setConstant(false); + } + + } else if (type->isContainer() && meta_type->indirections() == 0) { + meta_type->setTypeUsagePattern(AbstractMetaType::ContainerPattern); + + } else if (type->isTemplateArgument()) { + + } else if (type->isFlags() + && meta_type->indirections() == 0 + && (meta_type->isConstant() == meta_type->isReference())) { + meta_type->setTypeUsagePattern(AbstractMetaType::FlagsPattern); + + } else if (type->isArray()) { + meta_type->setTypeUsagePattern(AbstractMetaType::ArrayPattern); + + } else if (type->isThread()) { + Q_ASSERT(meta_type->indirections() == 1); + meta_type->setTypeUsagePattern(AbstractMetaType::ThreadPattern); + + } else if (type->isValue() + && meta_type->indirections() == 0 + && (meta_type->isConstant() == meta_type->isReference() + || !meta_type->isReference())) { + meta_type->setTypeUsagePattern(AbstractMetaType::ValuePattern); + + } else if (type->isObject() && meta_type->actualIndirections() == 0) { + + ReportHandler::warning(QString("Object type '%1' passed as value. Resulting code will not compile.") + .arg(meta_type->cppSignature())); + meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + + } else { + meta_type->setTypeUsagePattern(AbstractMetaType::NativePointerPattern); + ReportHandler::debugFull(QString("native pointer pattern for '%1'") + .arg(meta_type->cppSignature())); + } +} + +QString AbstractMetaBuilder::translateDefaultValue(ArgumentModelItem item, AbstractMetaType *type, + AbstractMetaFunction *fnc, AbstractMetaClass *implementing_class, + int argument_index) +{ + QString function_name = fnc->name(); + QString class_name = implementing_class->name(); + + QString replaced_expression = fnc->replacedDefaultExpression(implementing_class, argument_index + 1); + if (fnc->removedDefaultExpression(implementing_class, argument_index +1)) + return ""; + if (!replaced_expression.isEmpty()) + return replaced_expression; + + QString expr = item->defaultValueExpression(); + if (type->isPrimitive()) { + if (type->name() == "boolean") { + if (expr == "false" || expr=="true") { + return expr; + } else { + bool ok = false; + int number = expr.toInt(&ok); + if (ok && number) + return "true"; + else + return "false"; + } + } else if (expr == "ULONG_MAX") { + return "Long.MAX_VALUE"; + } else if (expr == "QVariant::Invalid") { + return QString::number(QVariant::Invalid); + } else { + // This can be an enum or flag so I need to delay the + // translation untill all namespaces are completly + // processed. This is done in figureOutEnumValues() + return expr; + } + } else if (type != 0 && (type->isFlags() || type->isEnum())) { + // Same as with enum explanation above... + return expr; + + } else { + + // constructor or functioncall can be a bit tricky... + if (expr == "QVariant()" /* qtd || expr == "QModelIndex()"*/) { + return "null"; + } else if (expr == "QString()") { + return "null"; + } else if (expr.endsWith(")") && expr.contains("::")) { + TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::"))); + if (typeEntry) + return typeEntry->qualifiedTargetLangName() + "." + expr.right(expr.length() - expr.indexOf("::") - 2); + } else if (expr.endsWith(")") && type->isValue()) { + int pos = expr.indexOf("("); + + TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(pos)); + if (typeEntry) { // qtd + if(typeEntry->isStructInD()) + return typeEntry->qualifiedCppName() + expr.right(expr.length() - pos); + else + return "new " + typeEntry->qualifiedCppName() + expr.right(expr.length() - pos); + } else + return expr; + } else if (expr == "0") { + return "null"; + } else if (type->isObject() || type->isValue() || expr.contains("::")) { // like Qt::black passed to a QColor + TypeEntry *typeEntry = TypeDatabase::instance()->findType(expr.left(expr.indexOf("::"))); + + expr = expr.right(expr.length() - expr.indexOf("::") - 2); + if (typeEntry) { + return "new " + type->typeEntry()->qualifiedCppName() + + "(" + typeEntry->qualifiedCppName() + "." + expr + ")"; + } + } + } + + QString warn = QString("unsupported default value '%3' of argument in function '%1', class '%2'") + .arg(function_name).arg(class_name).arg(item->defaultValueExpression()); + ReportHandler::warning(warn); + + return QString(); +} + + +bool AbstractMetaBuilder::isQObject(const QString &qualified_name) +{ + if (qualified_name == "QObject") + return true; + + ClassModelItem class_item = m_dom->findClass(qualified_name); + + if (!class_item) { + QStringList names = qualified_name.split(QLatin1String("::")); + NamespaceModelItem ns = model_dynamic_cast<NamespaceModelItem>(m_dom); + for (int i=0; i<names.size() - 1 && ns; ++i) + ns = ns->namespaceMap().value(names.at(i)); + if (ns && names.size() >= 2) + class_item = ns->findClass(names.at(names.size() - 1)); + } + + bool isqobject = class_item && class_item->extendsClass("QObject"); + + if (class_item && !isqobject) { + QStringList baseClasses = class_item->baseClasses(); + for (int i=0; i<baseClasses.count(); ++i) { + + isqobject = isQObject(baseClasses.at(i)); + if (isqobject) + break; + } + } + + return isqobject; +} + + +bool AbstractMetaBuilder::isEnum(const QStringList &qualified_name) +{ + CodeModelItem item = m_dom->model()->findItem(qualified_name, m_dom->toItem()); + return item && item->kind() == _EnumModelItem::__node_kind; +} + +AbstractMetaType *AbstractMetaBuilder::inheritTemplateType(const QList<AbstractMetaType *> &template_types, + AbstractMetaType *meta_type, bool *ok) +{ + if (ok != 0) + *ok = true; + if (!meta_type || (!meta_type->typeEntry()->isTemplateArgument() && !meta_type->hasInstantiations())) + return meta_type ? meta_type->copy() : 0; + + AbstractMetaType *returned = meta_type->copy(); + returned->setOriginalTemplateType(meta_type->copy()); + + if (returned->typeEntry()->isTemplateArgument()) { + const TemplateArgumentEntry *tae = static_cast<const TemplateArgumentEntry *>(returned->typeEntry()); + + // If the template is intantiated with void we special case this as rejecting the functions that use this + // parameter from the instantiation. + if (template_types.size() <= tae->ordinal() || template_types.at(tae->ordinal())->typeEntry()->name() == "void") { + if (ok != 0) + *ok = false; + return 0; + } + + AbstractMetaType *t = returned->copy(); + t->setTypeEntry(template_types.at(tae->ordinal())->typeEntry()); + t->setIndirections(template_types.at(tae->ordinal())->indirections() + t->indirections() + ? 1 + : 0); + decideUsagePattern(t); + + delete returned; + returned = inheritTemplateType(template_types, t, ok); + if (ok != 0 && !(*ok)) + return 0; + } + + if (returned->hasInstantiations()) { + QList<AbstractMetaType *> instantiations = returned->instantiations(); + for (int i=0; i<instantiations.count(); ++i) { + instantiations[i] = inheritTemplateType(template_types, instantiations.at(i), ok); + if (ok != 0 && !(*ok)) + return 0; + } + returned->setInstantiations(instantiations); + } + + return returned; +} + +bool AbstractMetaBuilder::inheritTemplate(AbstractMetaClass *subclass, + const AbstractMetaClass *template_class, + const TypeParser::Info &info) +{ + QList<TypeParser::Info> targs = info.template_instantiations; + + QList<AbstractMetaType *> template_types; + foreach (const TypeParser::Info &i, targs) { + TypeEntry *t = TypeDatabase::instance()->findType(i.qualified_name.join("::")); + + if (t != 0) { + AbstractMetaType *temporary_type = createMetaType(); + temporary_type->setTypeEntry(t); + temporary_type->setConstant(i.is_constant); + temporary_type->setReference(i.is_reference); + temporary_type->setIndirections(i.indirections); + template_types << temporary_type; + } + } + + AbstractMetaFunctionList funcs = subclass->functions(); + foreach (const AbstractMetaFunction *function, template_class->functions()) { + + if (function->isModifiedRemoved(TypeSystem::All)) + continue; + + AbstractMetaFunction *f = function->copy(); + f->setArguments(AbstractMetaArgumentList()); + + bool ok = true; + AbstractMetaType *ftype = function->type(); + f->setType(inheritTemplateType(template_types, ftype, &ok)); + if (!ok) { + delete f; + continue; + } + + foreach (AbstractMetaArgument *argument, function->arguments()) { + AbstractMetaType *atype = argument->type(); + + AbstractMetaArgument *arg = argument->copy(); + arg->setType(inheritTemplateType(template_types, atype, &ok)); + if (!ok) + break; + f->addArgument(arg); + } + + if (!ok) { + delete f; + continue ; + } + + // There is no base class in java to inherit from here, so the + // template instantiation is the class that implements the function.. + f->setImplementingClass(subclass); + + // We also set it as the declaring class, since the superclass is + // supposed to disappear. This allows us to make certain function modifications + // on the inherited functions. + f->setDeclaringClass(subclass); + + + if (f->isConstructor() && subclass->isTypeAlias()) { + f->setName(subclass->name()); + } else if (f->isConstructor()) { + delete f; + continue; + } + + // if the instantiation has a function named the same as an existing + // function we have shadowing so we need to skip it. + bool found = false; + for (int i=0; i<funcs.size(); ++i) { + if (funcs.at(i)->name() == f->name()) { + found = true; + continue; + } + } + if (found) { + delete f; + continue; + } + + ComplexTypeEntry *te = subclass->typeEntry(); + FunctionModificationList mods = function->modifications(template_class); + for (int i=0; i<mods.size(); ++i) { + FunctionModification mod = mods.at(i); + mod.signature = f->minimalSignature(); + + // If we ever need it... Below is the code to do + // substitution of the template instantation type inside + // injected code.. +#if 0 + if (mod.modifiers & Modification::CodeInjection) { + for (int j=0; j<template_types.size(); ++j) { + CodeSnip &snip = mod.snips.last(); + QString code = snip.code(); + code.replace(QString::fromLatin1("$$QT_TEMPLATE_%1$$").arg(j), + template_types.at(j)->typeEntry()->qualifiedCppName()); + snip.codeList.clear(); + snip.addCode(code); + } + } +#endif + te->addFunctionModification(mod); + } + + subclass->addFunction(f); + } + + // Clean up + foreach (AbstractMetaType *type, template_types) { + delete type; + } + + + { + subclass->setTemplateBaseClass(template_class); + + subclass->setInterfaces(template_class->interfaces()); + subclass->setBaseClass(template_class->baseClass()); + } + + return true; +} + +void AbstractMetaBuilder::parseQ_Property(AbstractMetaClass *meta_class, const QStringList &declarations) +{ + for (int i=0; i<declarations.size(); ++i) { + QString p = declarations.at(i); + + QStringList l = p.split(QLatin1String(" ")); + + + QStringList qualifiedScopeName = currentScope()->qualifiedName(); + bool ok = false; + AbstractMetaType *type = 0; + QString scope; + for (int j=qualifiedScopeName.size(); j>=0; --j) { + scope = j > 0 ? QStringList(qualifiedScopeName.mid(0, j)).join("::") + "::" : QString(); + TypeInfo info; + info.setQualifiedName((scope + l.at(0)).split("::")); + + type = translateType(info, &ok); + if (type != 0 && ok) { + break; + } + } + + if (type == 0 || !ok) { + ReportHandler::warning(QString("Unable to decide type of property: '%1' in class '%2'") + .arg(l.at(0)).arg(meta_class->name())); + continue; + } + + QString typeName = scope + l.at(0); + + QPropertySpec *spec = new QPropertySpec(type->typeEntry()); + spec->setName(l.at(1)); + spec->setIndex(i); + + for (int pos=2; pos+1<l.size(); pos+=2) { + if (l.at(pos) == QLatin1String("READ")) + spec->setRead(l.at(pos+1)); + else if (l.at(pos) == QLatin1String("WRITE")) + spec->setWrite(l.at(pos+1)); + else if (l.at(pos) == QLatin1String("DESIGNABLE")) + spec->setDesignable(l.at(pos+1)); + else if (l.at(pos) == QLatin1String("RESET")) + spec->setReset(l.at(pos+1)); + } + + meta_class->addPropertySpec(spec); + delete type; + } +} + +static void hide_functions(const AbstractMetaFunctionList &l) { + foreach (AbstractMetaFunction *f, l) { + FunctionModification mod; + mod.signature = f->minimalSignature(); + mod.modifiers = FunctionModification::Private; + ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod); + } +} + +static void remove_function(AbstractMetaFunction *f) { + FunctionModification mod; + mod.removal = TypeSystem::All; + mod.signature = f->minimalSignature(); + ((ComplexTypeEntry *) f->implementingClass()->typeEntry())->addFunctionModification(mod); +} + +static AbstractMetaFunctionList filter_functions(const AbstractMetaFunctionList &lst, QSet<QString> *signatures) +{ + AbstractMetaFunctionList functions; + foreach (AbstractMetaFunction *f, lst) { + QString signature = f->minimalSignature(); + int start = signature.indexOf(QLatin1Char('(')) + 1; + int end = signature.lastIndexOf(QLatin1Char(')')); + signature = signature.mid(start, end - start); + if (signatures->contains(signature)) { + remove_function(f); + continue; + } + (*signatures) << signature; + functions << f; + } + return functions; +} + +void AbstractMetaBuilder::setupEquals(AbstractMetaClass *cls) +{ + AbstractMetaFunctionList equals; + AbstractMetaFunctionList nequals; + + QString op_equals = QLatin1String("operator_equal"); + QString op_nequals = QLatin1String("operator_not_equal"); + + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements + | AbstractMetaClass::NotRemovedFromTargetLang); + foreach (AbstractMetaFunction *f, functions) { + if (f->name() == op_equals) + equals << f; + else if (f->name() == op_nequals) + nequals << f; + } + + if (equals.size() || nequals.size()) { + if (!cls->hasHashFunction()) { + ReportHandler::warning(QString::fromLatin1("Class '%1' has equals operators but no qHash() function") + .arg(cls->name())); + } + + hide_functions(equals); + hide_functions(nequals); + + // We only need == if we have both == and !=, and one == for + // each signature type, like QDateTime::==(QDate) and (QTime) + // if such a thing exists... + QSet<QString> func_signatures; + cls->setEqualsFunctions(filter_functions(equals, &func_signatures)); + cls->setNotEqualsFunctions(filter_functions(nequals, &func_signatures)); + } +} + +void AbstractMetaBuilder::setupComparable(AbstractMetaClass *cls) +{ + AbstractMetaFunctionList greater; + AbstractMetaFunctionList greaterEquals; + AbstractMetaFunctionList less; + AbstractMetaFunctionList lessEquals; + + QString op_greater = QLatin1String("operator_greater"); + QString op_greater_eq = QLatin1String("operator_greater_or_equal"); + QString op_less = QLatin1String("operator_less"); + QString op_less_eq = QLatin1String("operator_less_or_equal"); + + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements + | AbstractMetaClass::NotRemovedFromTargetLang); + foreach (AbstractMetaFunction *f, functions) { + if (f->name() == op_greater) + greater << f; + else if (f->name() == op_greater_eq) + greaterEquals << f; + else if (f->name() == op_less) + less << f; + else if (f->name() == op_less_eq) + lessEquals << f; + } + + bool hasEquals = cls->equalsFunctions().size() || cls->notEqualsFunctions().size(); + + // Conditions for comparable is: + // >, ==, < - The basic case + // >, == - Less than becomes else case + // <, == - Greater than becomes else case + // >=, <= - if (<= && >=) -> equal + bool mightBeComparable = greater.size() || greaterEquals.size() || less.size() || lessEquals.size() + || greaterEquals.size() == 1 || lessEquals.size() == 1; + + if (mightBeComparable) { + QSet<QString> signatures; + + // We only hide the original functions if we are able to make a compareTo() method + bool wasComparable = false; + + // The three upper cases, prefer the <, == approach + if (hasEquals && (greater.size() || less.size())) { + cls->setLessThanFunctions(filter_functions(less, &signatures)); + cls->setGreaterThanFunctions(filter_functions(greater, &signatures)); + filter_functions(greaterEquals, &signatures); + filter_functions(lessEquals, &signatures); + wasComparable = true; + } else if (hasEquals && (greaterEquals.size() || lessEquals.size())) { + cls->setLessThanEqFunctions(filter_functions(lessEquals, &signatures)); + cls->setGreaterThanEqFunctions(filter_functions(greaterEquals, &signatures)); + wasComparable = true; + } else if (greaterEquals.size() == 1 || lessEquals.size() == 1) { + cls->setGreaterThanEqFunctions(greaterEquals); + cls->setLessThanEqFunctions(lessEquals); + filter_functions(less, &signatures); + filter_functions(greater, &signatures); + wasComparable = true; + } + + if (wasComparable) { + hide_functions(greater); + hide_functions(greaterEquals); + hide_functions(less); + hide_functions(lessEquals); + } + } + +} + +void AbstractMetaBuilder::setupClonable(AbstractMetaClass *cls) +{ + // All value types are required to have a copy constructor, + // or they will not work as value types (it won't even compile, + // because of calls to qRegisterMetaType(). Thus all value types + // should be cloneable. + if (cls->typeEntry()->isValue()) { + cls->setHasCloneOperator(true); + return; + } else { + QString op_assign = QLatin1String("operator_assign"); + + AbstractMetaFunctionList functions = cls->queryFunctions(AbstractMetaClass::ClassImplements); + foreach (AbstractMetaFunction *f, functions) { + if ((f->name() == op_assign || f->isConstructor()) && f->isPublic()) { + AbstractMetaArgumentList arguments = f->arguments(); + if (f->actualMinimumArgumentCount() == 1) { + if (cls->typeEntry()->qualifiedCppName() == arguments.at(0)->type()->typeEntry()->qualifiedCppName()) { + if (cls->typeEntry()->isValue()) { + cls->setHasCloneOperator(true); + return; + } + } + } + } + } + } +} + +static void write_reject_log_file(const QString &name, + const QMap<QString, AbstractMetaBuilder::RejectReason> &rejects) +{ + QFile f(name); + if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { + ReportHandler::warning(QString("failed to write log file: '%1'") + .arg(f.fileName())); + return; + } + + QTextStream s(&f); + + + for (int reason=0; reason<AbstractMetaBuilder::NoReason; ++reason) { + s << QString(72, '*') << endl; + switch (reason) { + case AbstractMetaBuilder::NotInTypeSystem: + s << "Not in type system"; + break; + case AbstractMetaBuilder::GenerationDisabled: + s << "Generation disabled by type system"; + break; + case AbstractMetaBuilder::RedefinedToNotClass: + s << "Type redefined to not be a class"; + break; + + case AbstractMetaBuilder::UnmatchedReturnType: + s << "Unmatched return type"; + break; + + case AbstractMetaBuilder::UnmatchedArgumentType: + s << "Unmatched argument type"; + break; + + default: + s << "unknown reason"; + break; + } + + s << endl; + + for (QMap<QString, AbstractMetaBuilder::RejectReason>::const_iterator it = rejects.constBegin(); + it != rejects.constEnd(); ++it) { + if (it.value() != reason) + continue; + s << " - " << it.key() << endl; + } + + s << QString(72, '*') << endl << endl; + } + +} + + +void AbstractMetaBuilder::dumpLog() +{ + write_reject_log_file("mjb_rejected_classes.log", m_rejected_classes); + write_reject_log_file("mjb_rejected_enums.log", m_rejected_enums); + write_reject_log_file("mjb_rejected_functions.log", m_rejected_functions); + write_reject_log_file("mjb_rejected_fields.log", m_rejected_fields); +} + +AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted() const +{ + AbstractMetaClassList res; + + AbstractMetaClassList classes = m_meta_classes; + qSort(classes); + + QSet<AbstractMetaClass*> noDependency; + QHash<AbstractMetaClass*, QSet<AbstractMetaClass* >* > hash; + foreach (AbstractMetaClass *cls, classes) { + QSet<AbstractMetaClass* > *depends = new QSet<AbstractMetaClass* >(); + + if (cls->baseClass()) + depends->insert(cls->baseClass()); + + foreach (AbstractMetaClass *interface, cls->interfaces()) { + depends->insert(interface); + } + + if (depends->empty()) { + noDependency.insert(cls); + } else { + hash.insert(cls, depends); + } + } + + while (!noDependency.empty()) { + foreach (AbstractMetaClass *cls, noDependency.values()) { + if(!cls->isInterface()) + res.append(cls); + noDependency.remove(cls); + QHashIterator<AbstractMetaClass*, QSet<AbstractMetaClass* >* > i(hash); + while (i.hasNext()) { + i.next(); + i.value()->remove(cls); + if (i.value()->empty()) { + AbstractMetaClass *key = i.key(); + noDependency.insert(key); + hash.remove(key); + delete(i.value()); + } + } + } + } + + if (!noDependency.empty() || !hash.empty()) { + qWarning("dependency graph was cyclic."); + } + + return res; +}