/****************************************************************************
**
** Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Script Generator project on Qt Labs.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <algorithm> // for std::stable_sort

#include "abstractmetalang.h"
#include "reporthandler.h"

/*******************************************************************************
 * AbstractMetaType
 */
AbstractMetaType::shared_pointer AbstractMetaType::copy() const
{
  AbstractMetaType* cpy = new AbstractMetaType;

  cpy->setTypeUsagePattern(typeUsagePattern());
  cpy->setConstant(isConstant());
  cpy->setReference(isReference());
  cpy->setIndirections(indirections());
  cpy->setInstantiations(instantiations());
  cpy->setArrayElementCount(arrayElementCount());
  cpy->setOriginalTypeDescription(originalTypeDescription());
  cpy->setOriginalTemplateType(originalTemplateType() ? originalTemplateType()->copy() : 0);

  cpy->setArrayElementType(arrayElementType() ? arrayElementType()->copy() : 0);

  cpy->setTypeEntry(typeEntry());

  return AbstractMetaType::shared_pointer(cpy);
}

QString AbstractMetaType::cppSignature() const
{
  QString s;

  if (isConstant())
    s += "const ";

  s += typeEntry()->qualifiedCppName();

  if (hasInstantiationInCpp()) {
    auto& types = instantiations();
    s += "<";
    for (int i = 0; i < types.count(); ++i) {
      if (i > 0)
        s += ", ";
      s += types.at(i)->cppSignature();
    }
    s += " >";
  }

  if (actualIndirections()) {
    s += ' ';
    if (indirections())
      s += QString(indirections(), '*');
    if (isReference())
      s += '&';
  }
  return s;
}

/*******************************************************************************
 * AbstractMetaVariable
 */
AbstractMetaVariable::~AbstractMetaVariable() {}

/*******************************************************************************
 * AbstractMetaArgument
 */
AbstractMetaArgument* AbstractMetaArgument::copy() const
{
  AbstractMetaArgument* cpy = new AbstractMetaArgument;
  cpy->setName(AbstractMetaVariable::name());
  cpy->setDefaultValueExpression(defaultValueExpression());
  cpy->setType(type());
  cpy->setArgumentIndex(argumentIndex());

  return cpy;
}

QString AbstractMetaArgument::argumentName() const
{
  QString n = AbstractMetaVariable::name();
  if (n.isEmpty()) {
    return QString("arg__%2").arg(m_argument_index + 1);
  }
  return n;
}

QString AbstractMetaArgument::indexedName() const
{
  QString n = AbstractMetaVariable::name();
  if (n.isEmpty())
    return argumentName();
  return QString("%1%2").arg(n).arg(m_argument_index);
}

QString AbstractMetaArgument::name() const
{
  Q_ASSERT_X(0, "AbstractMetaArgument::name()", "use argumentName() or indexedName() instead");
  return QString();
}

/*******************************************************************************
 * AbstractMetaFunction
 */
AbstractMetaFunction::~AbstractMetaFunction()
{
  qDeleteAll(m_arguments);
}

/*******************************************************************************
 * Indicates that this function has a modification that removes it
 */
bool AbstractMetaFunction::isModifiedRemoved(int types) const
{
  FunctionModificationList mods = modifications(implementingClass());
  for (FunctionModification mod : mods) {
    if (!mod.isRemoveModifier())
      continue;

    if ((mod.removal & types) == types)
      return true;
  }

  return false;
}

bool AbstractMetaFunction::needsCallThrough() const
{
  if (ownerClass()->isInterface())
    return false;
  if (referenceCounts(implementingClass()).size() > 0)
    return true;
  if (argumentsHaveNativeId() || !isStatic())
    return true;

  for (const AbstractMetaArgument* arg : arguments()) {
    if (arg->type()->isArray() || arg->type()->isTargetLangEnum() || arg->type()->isTargetLangFlags())
      return true;
  }

  if (type() && (type()->isArray() || type()->isTargetLangEnum() || type()->isTargetLangFlags()))
    return true;

  for (int i = -1; i <= arguments().size(); ++i) {
    TypeSystem::Ownership owner = this->ownership(implementingClass(), TypeSystem::TargetLangCode, i);
    if (owner != TypeSystem::InvalidOwnership)
      return true;
  }

  return false;
}

bool AbstractMetaFunction::needsSuppressUncheckedWarning() const
{
  for (int i = -1; i <= arguments().size(); ++i) {
    const QList<ReferenceCount> referenceCounts = this->referenceCounts(implementingClass(), i);
    for (const ReferenceCount& referenceCount : referenceCounts) {
      if (referenceCount.action != ReferenceCount::Set)
        return true;
    }
  }
  return false;
}

QString AbstractMetaFunction::marshalledName() const
{
  QString returned = "__qt_" + name();
  AbstractMetaArgumentList arguments = this->arguments();
  for (const AbstractMetaArgument* arg : arguments) {
    returned += "_";
    if (arg->type()->isNativePointer()) {
      returned += "nativepointer";
    } else if (arg->type()->isIntegerEnum() || arg->type()->isIntegerFlags()) {
      returned += "int";
    } else {
      returned += arg->type()->name().replace("[]", "_3").replace(".", "_");
    }
  }
  return returned;
}

bool AbstractMetaFunction::operator<(const AbstractMetaFunction& other) const
{
  uint result = compareTo(&other);
  return result & NameLessThan;
}

/*!
    Returns a mask of CompareResult describing how this function is
    compares to another function
*/
uint AbstractMetaFunction::compareTo(const AbstractMetaFunction* other) const
{
  uint result = 0;

  // Enclosing class...
  if (ownerClass() == other->ownerClass()) {
    result |= EqualImplementor;
  }

  // Attributes
  if (attributes() == other->attributes()) {
    result |= EqualAttributes;
  }

  // Compare types
  AbstractMetaType::shared_pointer t = type();
  AbstractMetaType::shared_pointer ot = other->type();
  if ((!t && !ot) || ((t && ot && t->name() == ot->name()))) {
    result |= EqualReturnType;
  }

  // Compare names
  int cmp = originalName().compare(other->originalName());

  if (cmp < 0) {
    result |= NameLessThan;
  } else if (cmp == 0) {
    result |= EqualName;
  }

  // compare name after modification...
  cmp = modifiedName().compare(other->modifiedName());
  if (cmp == 0)
    result |= EqualModifiedName;

  // Compare arguments...
  AbstractMetaArgumentList min_arguments;
  AbstractMetaArgumentList max_arguments;
  if (arguments().size() < other->arguments().size()) {
    min_arguments = arguments();
    max_arguments = other->arguments();
  } else {
    min_arguments = other->arguments();
    max_arguments = arguments();
  }

  int min_count = min_arguments.size();
  int max_count = max_arguments.size();
  bool same = true;
  for (int i = 0; i < max_count; ++i) {
    if (i < min_count) {
      const AbstractMetaArgument* min_arg = min_arguments.at(i);
      const AbstractMetaArgument* max_arg = max_arguments.at(i);
      if (min_arg->type()->name() != max_arg->type()->name()
          && (min_arg->defaultValueExpression().isEmpty() || max_arg->defaultValueExpression().isEmpty()))
      {
        same = false;
        break;
      }
    } else {
      if (max_arguments.at(i)->defaultValueExpression().isEmpty()) {
        same = false;
        break;
      }
    }
  }

  if (same)
    result |= min_count == max_count ? EqualArguments : EqualDefaultValueOverload;

  return result;
}

AbstractMetaFunction* AbstractMetaFunction::copy() const
{
  AbstractMetaFunction* cpy = new AbstractMetaFunction;
  cpy->setName(name());
  cpy->setOriginalName(originalName());
  cpy->setOwnerClass(ownerClass());
  cpy->setImplementingClass(implementingClass());
  cpy->setInterfaceClass(interfaceClass());
  cpy->setFunctionType(functionType());
  cpy->setAttributes(attributes());
  cpy->setDeclaringClass(declaringClass());
  if (type())
    cpy->setType(type());
  cpy->setConstant(isConstant());
  cpy->setConstexpr(isConstexpr());
  cpy->setAuto(isAuto());
  cpy->setException(exception());
  cpy->setOriginalAttributes(originalAttributes());

  for (AbstractMetaArgument* arg : arguments()) {
    cpy->addArgument(arg->copy());
  }

  Q_ASSERT((!type() && !cpy->type()) || (type()->instantiations() == cpy->type()->instantiations()));

  return cpy;
}

QStringList AbstractMetaFunction::introspectionCompatibleSignatures(const QStringList& resolvedArguments) const
{
  AbstractMetaArgumentList arguments = this->arguments();
  if (arguments.size() == resolvedArguments.size()) {
    return (QStringList() << TypeSystem::normalizedSignature(
              (name() + "(" + resolvedArguments.join(",") + ")").toUtf8().constData()));
  } else {
    QStringList returned;

    AbstractMetaArgument* argument = arguments.at(resolvedArguments.size());
    QStringList minimalTypeSignature = argument->type()->minimalSignature().split("::");
    for (int i = 0; i < minimalTypeSignature.size(); ++i) {
      returned += introspectionCompatibleSignatures(
        QStringList(resolvedArguments)
        << QStringList(minimalTypeSignature.mid(minimalTypeSignature.size() - i - 1)).join("::"));
    }

    return returned;
  }
}

QString AbstractMetaFunction::signature() const
{
  QString s(m_original_name);

  s += "(";

  for (int i = 0; i < m_arguments.count(); ++i) {
    if (i > 0)
      s += ", ";
    AbstractMetaArgument* a = m_arguments.at(i);
    s += a->type()->cppSignature();

    // We need to have the argument names in the qdoc files
    s += " ";
    s += a->argumentName();
  }
  s += ")";

  if (isConstant())
    s += " const";

  return s;
}

int AbstractMetaFunction::actualMinimumArgumentCount() const
{
  AbstractMetaArgumentList arguments = this->arguments();

  int count = 0;
  for (int i = 0; i < arguments.size(); ++i && ++count) {
    if (argumentRemoved(i + 1))
      --count;
    else if (!arguments.at(i)->defaultValueExpression().isEmpty())
      break;
  }

  return count;
}

// Returns reference counts for argument at idx, or all arguments if idx == -2
QList<ReferenceCount> AbstractMetaFunction::referenceCounts(const AbstractMetaClass* cls, int idx) const
{
  QList<ReferenceCount> returned;

  const FunctionModificationList mods = this->modifications(cls);
  for (const FunctionModification& mod : mods) {
    QList<ArgumentModification> argument_mods = mod.argument_mods;
    for (ArgumentModification argument_mod : argument_mods) {
      if (argument_mod.index != idx && idx != -2)
        continue;
      returned += argument_mod.referenceCounts;
    }
  }

  return returned;
}

QString AbstractMetaFunction::replacedDefaultExpression(const AbstractMetaClass* cls, int key) const
{
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key && !argument_modification.replaced_default_expression.isEmpty()) {
        return argument_modification.replaced_default_expression;
      }
    }
  }

  return QString();
}

bool AbstractMetaFunction::removedDefaultExpression(const AbstractMetaClass* cls, int key) const
{
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key && argument_modification.removed_default_expression) {
        return true;
      }
    }
  }

  return false;
}

bool AbstractMetaFunction::resetObjectAfterUse(int argument_idx) const
{
  const AbstractMetaClass* cls = declaringClass();
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argumentModifications = modification.argument_mods;
    for (const ArgumentModification& argumentModification : argumentModifications) {
      if (argumentModification.index == argument_idx && argumentModification.reset_after_use)
        return true;
    }
  }

  return false;
}

QString AbstractMetaFunction::nullPointerDefaultValue(const AbstractMetaClass* mainClass, int argument_idx) const
{
  Q_ASSERT(nullPointersDisabled(mainClass, argument_idx));

  const AbstractMetaClass* cls = mainClass;
  if (cls == 0)
    cls = implementingClass();

  do {
    const FunctionModificationList modifications = this->modifications(cls);
    for (const FunctionModification& modification : modifications) {
      const QList<ArgumentModification> argument_modifications = modification.argument_mods;
      for (const ArgumentModification& argument_modification : argument_modifications) {
        if (argument_modification.index == argument_idx && argument_modification.no_null_pointers) {
          return argument_modification.null_pointer_default_value;
        }
      }
    }

    cls = cls->baseClass();
  } while (cls != 0 && mainClass == 0); // Once when mainClass != 0, or once for all base classes of implementing class

  return QString();
}

bool AbstractMetaFunction::nullPointersDisabled(const AbstractMetaClass* mainClass, int argument_idx) const
{
  const AbstractMetaClass* cls = mainClass;
  if (cls == 0)
    cls = implementingClass();

  do {
    const FunctionModificationList& modifications = this->modifications(cls);
    for (const FunctionModification& modification : modifications) {
      const QList<ArgumentModification> argument_modifications = modification.argument_mods;
      for (const ArgumentModification& argument_modification : argument_modifications) {
        if (argument_modification.index == argument_idx && argument_modification.no_null_pointers) {
          return true;
        }
      }
    }

    cls = cls->baseClass();
  } while (cls != 0 && mainClass == 0); // Once when mainClass != 0, or once for all base classes of implementing class

  return false;
}

QString AbstractMetaFunction::conversionRule(TypeSystem::Language language, int key) const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index != key)
        continue;

      for (CodeSnip snip : argument_modification.conversion_rules) {
        if (snip.language == language && !snip.code().isEmpty())
          return snip.code();
      }
    }
  }

  return QString();
}

QString AbstractMetaFunction::argumentReplaced(int key) const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key && !argument_modification.replace_value.isEmpty()) {
        return argument_modification.replace_value;
      }
    }
  }

  return "";
}

bool AbstractMetaFunction::argumentRemoved(int key) const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key) {
        if (argument_modification.removed) {
          return true;
        }
      }
    }
  }

  return false;
}

bool AbstractMetaFunction::isVirtualSlot() const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    if (modification.isVirtualSlot())
      return true;
  }

  return false;
}

bool AbstractMetaFunction::disabledGarbageCollection(const AbstractMetaClass* cls, int key) const
{
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index != key)
        continue;

      for (const TypeSystem::Ownership& ownership : argument_modification.ownerships.values()) {
        if (ownership == TypeSystem::CppOwnership)
          return true;
      }
    }
  }

  return false;
}

bool AbstractMetaFunction::isDeprecated() const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    if (modification.isDeprecated())
      return true;
  }
  return false;
}

TypeSystem::Ownership AbstractMetaFunction::ownership(const AbstractMetaClass* cls, TypeSystem::Language language,
  int key) const
{
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key)
        return argument_modification.ownerships.value(language, TypeSystem::InvalidOwnership);
    }
  }

  return TypeSystem::InvalidOwnership;
}

bool AbstractMetaFunction::isRemovedFromAllLanguages(const AbstractMetaClass* cls) const
{
  return isRemovedFrom(cls, TypeSystem::All);
}

bool AbstractMetaFunction::isRemovedFrom(const AbstractMetaClass* cls, TypeSystem::Language language) const
{
  const FunctionModificationList modifications = this->modifications(cls);
  for (const FunctionModification& modification : modifications) {
    if ((modification.removal & language) == language)
      return true;
  }

  return false;
}

QString AbstractMetaFunction::typeReplaced(int key) const
{
  const FunctionModificationList modifications = this->modifications(declaringClass());
  for (const FunctionModification& modification : modifications) {
    const QList<ArgumentModification> argument_modifications = modification.argument_mods;
    for (const ArgumentModification& argument_modification : argument_modifications) {
      if (argument_modification.index == key && !argument_modification.modified_type.isEmpty()) {
        return argument_modification.modified_type;
      }
    }
  }

  return QString();
}

QString AbstractMetaFunction::minimalSignature() const
{
  if (!m_cached_minimal_signature.isEmpty())
    return m_cached_minimal_signature;

  QString minimalSignature = originalName() + "(";
  AbstractMetaArgumentList arguments = this->arguments();

  for (int i = 0; i < arguments.count(); ++i) {
    AbstractMetaType::shared_pointer t = arguments.at(i)->type();

    if (i > 0)
      minimalSignature += ",";

    minimalSignature += t->minimalSignature();
  }
  minimalSignature += ")";
  if (isConstant())
    minimalSignature += "const";

  minimalSignature = TypeSystem::normalizedSignature(minimalSignature.toLocal8Bit().constData());
  m_cached_minimal_signature = minimalSignature;

  return minimalSignature;
}

FunctionModificationList AbstractMetaFunction::modifications(const AbstractMetaClass* implementor) const
{
  Q_ASSERT(implementor);
  return implementor->typeEntry()->functionModifications(minimalSignature());
}

bool AbstractMetaFunction::hasModifications(const AbstractMetaClass* implementor) const
{
  FunctionModificationList mods = modifications(implementor);
  return mods.count() > 0;
}

QString AbstractMetaFunction::modifiedName() const
{
  if (m_cached_modified_name.isEmpty()) {
    const FunctionModificationList mods = modifications(implementingClass());
    for (const FunctionModification& mod : mods) {
      if (mod.isRenameModifier()) {
        m_cached_modified_name = mod.renamedToName;
        break;
      }
    }
    if (m_cached_modified_name.isEmpty())
      m_cached_modified_name = name();
  }
  return m_cached_modified_name;
}

QString AbstractMetaFunction::targetLangSignature(bool minimal) const
{
  QString s;

  // Attributes...
  if (!minimal) {
    // Return type
    if (type())
      s += type()->name() + " ";
    else
      s += "void ";
  }

  s += modifiedName();
  s += "(";

  int j = 0;
  for (int i = 0; i < m_arguments.size(); ++i) {
    if (argumentRemoved(i + 1))
      continue;
    if (j != 0) {
      s += ",";
      if (!minimal)
        s += QLatin1Char(' ');
    }
    s += m_arguments.at(i)->type()->name();

    if (!minimal) {
      s += " ";
      s += m_arguments.at(i)->argumentName();
    }
    ++j;
  }

  s += ")";

  return s;
}

bool function_sorter(AbstractMetaFunction* a, AbstractMetaFunction* b)
{
  return a->signature() < b->signature();
}

/*******************************************************************************
 * AbstractMetaClass
 */
AbstractMetaClass::~AbstractMetaClass()
{
  qDeleteAll(m_functions);
  qDeleteAll(m_fields);
  qDeleteAll(m_property_specs);
}

/*AbstractMetaClass *AbstractMetaClass::copy() const
{
    AbstractMetaClass *cls = new AbstractMetaClass;
    cls->setAttributes(attributes());
    cls->setBaseClass(baseClass());
    cls->setTypeEntry(typeEntry());
    for (AbstractMetaFunction* function : functions()) {
        AbstractMetaFunction *copy = function->copy();
        function->setImplementingClass(cls);
        cls->addFunction(copy);
    }
    cls->setEnums(enums());
    for (const AbstractMetaField* field : fields()) {
        AbstractMetaField *copy = field->copy();
        copy->setEnclosingClass(cls);
        cls->addField(copy);
    }
    cls->setInterfaces(interfaces());

    return cls;
}*/

/*******************************************************************************
 * Returns true if this class is a subclass of the given class
 */
bool AbstractMetaClass::inheritsFrom(const AbstractMetaClass* cls) const
{
  Q_ASSERT(cls != 0);

  const AbstractMetaClass* clazz = this;
  while (clazz != 0) {
    if (clazz == cls)
      return true;

    clazz = clazz->baseClass();
  }

  return false;
}

/*******************************************************************************
 * Constructs an interface based on the functions and enums in this
 * class and returns it...
 */
AbstractMetaClass* AbstractMetaClass::extractInterface()
{
  Q_ASSERT(typeEntry()->designatedInterface());

  if (m_extracted_interface == 0) {
    AbstractMetaClass* iface = new AbstractMetaClass;
    iface->setAttributes(attributes());
    iface->setBaseClass(0);
    iface->setPrimaryInterfaceImplementor(this);

    iface->setTypeEntry(typeEntry()->designatedInterface());

    for (const AbstractMetaFunction* function : functions()) {
      if (!function->isConstructor())
        iface->addFunction(function->copy());
    }

    //         iface->setEnums(enums());
    //         setEnums(AbstractMetaEnumList());

    for (const AbstractMetaField* field : fields()) {
      if (field->isPublic()) {
        AbstractMetaField* new_field = field->copy();
        new_field->setEnclosingClass(iface);
        iface->addField(new_field);
      }
    }

    m_extracted_interface = iface;
    addInterface(iface);
  }

  return m_extracted_interface;
}

/*******************************************************************************
 * Returns a list of all the functions with a given name
 */
AbstractMetaFunctionList AbstractMetaClass::queryFunctionsByName(const QString& name) const
{
  AbstractMetaFunctionList returned;
  AbstractMetaFunctionList functions = this->functions();
  for (AbstractMetaFunction* function : functions) {
    if (function->name() == name)
      returned.append(function);
  }

  return returned;
}

QString AbstractMetaClass::getDefaultNonZeroFunction() const
{
  for (const AbstractMetaFunction* fun : queryFunctionsByName("isEmpty")) {
    if (fun->actualMinimumArgumentCount() == 0 && fun->isPublic()) {
      return "isEmpty";
    }
  }
  for (const AbstractMetaFunction* fun : queryFunctionsByName("isValid")) {
    if (fun->actualMinimumArgumentCount() == 0 && fun->isPublic()) {
      return "isValid";
    }
  }
  for (const AbstractMetaFunction* fun : queryFunctionsByName("isNull")) {
    if (fun->actualMinimumArgumentCount() == 0 && fun->isPublic()) {
      return "isNull";
    }
  }
  return QString();
}

/*******************************************************************************
 * Returns all reference count modifications for any function in the class
 */
QList<ReferenceCount> AbstractMetaClass::referenceCounts() const
{
  QList<ReferenceCount> returned;

  AbstractMetaFunctionList functions = this->functions();
  for (const AbstractMetaFunction* function : functions) {
    returned += function->referenceCounts(this);
  }

  return returned;
}

/*******************************************************************************
 * Returns a list of all the functions retrieved during parsing which should
 * be added to the Java API.
 */
AbstractMetaFunctionList AbstractMetaClass::functionsInTargetLang() const
{
  int default_flags = NormalFunctions | Visible | NotRemovedFromTargetLang;

  // Interfaces don't implement functions
  default_flags |= isInterface() ? 0 : ClassImplements;

  // Only public functions in final classes
  // default_flags |= isFinal() ? WasPublic : 0;
  int public_flags = isFinal() ? WasPublic : 0;

  // Constructors
  AbstractMetaFunctionList returned = queryFunctions(Constructors | default_flags | public_flags);

  // Final functions
  returned += queryFunctions(FinalInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags);

  // Virtual functions
  returned += queryFunctions(VirtualInTargetLangFunctions | NonStaticFunctions | default_flags | public_flags);

  // Static functions
  returned += queryFunctions(StaticFunctions | default_flags | public_flags);

  // Empty, private functions, since they aren't caught by the other ones
  returned += queryFunctions(Empty | Invisible);

  return returned;
}

AbstractMetaFunctionList AbstractMetaClass::virtualFunctions() const
{
  const AbstractMetaFunctionList list = functionsInShellClass();

  AbstractMetaFunctionList returned;
  for (AbstractMetaFunction* f : list) {
    if (!f->isFinalInCpp() || f->isVirtualSlot())
      returned += f;
  }

  return returned;
}

AbstractMetaFunctionList AbstractMetaClass::nonVirtualShellFunctions() const
{
  AbstractMetaFunctionList list = functionsInShellClass();
  AbstractMetaFunctionList returned;
  for (AbstractMetaFunction* f : list) {
    if (f->isFinalInCpp() && !f->isVirtualSlot())
      returned += f;
  }

  return returned;
}

/*******************************************************************************
 * Returns a list of all functions that should be declared and implemented in
 * the shell class which is generated as a wrapper on top of the actual C++ class
 */
AbstractMetaFunctionList AbstractMetaClass::functionsInShellClass() const
{
  // Only functions and only protected and public functions
  int default_flags = NormalFunctions | Visible | WasVisible | NotRemovedFromShell;

  // All virtual functions
  AbstractMetaFunctionList returned = queryFunctions(VirtualFunctions | default_flags);

  // All functions explicitly set to be implemented by the shell class
  // (mainly superclass functions that are hidden by other declarations)
  returned += queryFunctions(ForcedShellFunctions | default_flags);

  // All functions explicitly set to be virtual slots
  returned += queryFunctions(VirtualSlots | default_flags);

  return returned;
}

/*******************************************************************************
 * Returns a list of all functions that require a public override function to
 * be generated in the shell class. This includes all functions that were originally
 * protected in the superclass.
 */
AbstractMetaFunctionList AbstractMetaClass::publicOverrideFunctions() const
{
  return queryFunctions(NormalFunctions | WasProtected | FinalInCppFunctions | NotRemovedFromTargetLang)
         + queryFunctions(Signals | WasProtected | FinalInCppFunctions | NotRemovedFromTargetLang);
}

AbstractMetaFunctionList AbstractMetaClass::virtualOverrideFunctions() const
{
  return queryFunctions(NormalFunctions | NonEmptyFunctions | Visible | VirtualInCppFunctions | NotRemovedFromShell)
         + queryFunctions(Signals | NonEmptyFunctions | Visible | VirtualInCppFunctions | NotRemovedFromShell);
}

void AbstractMetaClass::sortFunctions()
{
  std::sort(m_functions.begin(), m_functions.end(), function_sorter);
}

void AbstractMetaClass::setFunctions(const AbstractMetaFunctionList& functions)
{
  m_functions = functions;

  // Functions must be sorted by name before next loop
  sortFunctions();

  QString currentName;
  bool hasVirtuals = false;
  AbstractMetaFunctionList final_functions;
  for (AbstractMetaFunction* f : m_functions) {
    f->setOwnerClass(this);

    m_has_virtual_slots |= f->isVirtualSlot();
    m_has_virtuals |= !f->isFinal() || f->isVirtualSlot();
    m_has_nonpublic |= !f->isPublic();

    // If we have non-virtual overloads of a virtual function, we have to implement
    // all the overloads in the shell class to override the hiding rule
    if (currentName == f->name()) {
      hasVirtuals = hasVirtuals || !f->isFinal();
      if (f->isFinal())
        final_functions += f;
    } else {
      if (hasVirtuals && final_functions.size() > 0) {
        for (AbstractMetaFunction* final_function : final_functions) {
          *final_function += AbstractMetaAttributes::ForceShellImplementation;

          QString warn = QString("hiding of function '%1' in class '%2'").arg(final_function->name()).arg(name());
          ReportHandler::warning(warn);
        }
      }

      hasVirtuals = !f->isFinal();
      final_functions.clear();
      if (f->isFinal())
        final_functions += f;
      currentName = f->name();
    }
  }

#ifndef QT_NO_DEBUG
  bool duplicate_function = false;
  for (int j = 0; j < m_functions.size(); ++j) {
    FunctionModificationList mods = m_functions.at(j)->modifications(m_functions.at(j)->implementingClass());

    bool removed = false;
    for (const FunctionModification& mod : mods) {
      if (mod.isRemoveModifier()) {
        removed = true;
        break;
      }
    }
    if (removed)
      continue;

    for (int i = 0; i < m_functions.size() - 1; ++i) {
      if (j == i)
        continue;

      mods = m_functions.at(i)->modifications(m_functions.at(i)->implementingClass());
      bool removed = false;
      for (const FunctionModification& mod : mods) {
        if (mod.isRemoveModifier()) {
          removed = true;
          break;
        }
      }
      if (removed)
        continue;

      uint cmp = m_functions.at(i)->compareTo(m_functions.at(j));
      if ((cmp & AbstractMetaFunction::EqualName) && (cmp & AbstractMetaFunction::EqualArguments)) {
        printf("%s.%s mostly equal to %s.%s\n",
          qPrintable(m_functions.at(i)->implementingClass()->typeEntry()->qualifiedCppName()),
          qPrintable(m_functions.at(i)->signature()),
          qPrintable(m_functions.at(j)->implementingClass()->typeEntry()->qualifiedCppName()),
          qPrintable(m_functions.at(j)->signature()));
        duplicate_function = true;
      }
    }
  }
  //Q_ASSERT(!duplicate_function);
#endif
}

bool AbstractMetaClass::hasFieldAccessors() const
{
  for (const AbstractMetaField* field : fields()) {
    if (field->getter() || field->setter())
      return true;
  }

  return false;
}

bool AbstractMetaClass::hasDefaultToStringFunction() const
{
  for (AbstractMetaFunction* f : queryFunctionsByName("toString")) {
    if (f->actualMinimumArgumentCount() == 0) {
      return true;
    }
  }
  return false;
}

void AbstractMetaClass::addFunction(AbstractMetaFunction* function, bool check_duplicates)
{
  function->setOwnerClass(this);

  if (check_duplicates) {
    for (const auto f : m_functions) {
      if (f->name() == function->name() && f->minimalSignature() == function->minimalSignature()) {
        return;
      }
    }
  }

  if (!function->isDestructor()) {
    m_functions << function;
    // seems like this is not needed and takes a lot of performance
    //std::sort(m_functions.begin(), m_functions.end(), function_sorter);
  }

  m_has_virtual_slots |= function->isVirtualSlot();
  m_has_virtuals |= !function->isFinal() || function->isVirtualSlot();
  m_has_nonpublic |= !function->isPublic();
}

void AbstractMetaClass::removeFunction(AbstractMetaFunction* function)
{
  m_functions.removeOne(function);
}

bool AbstractMetaClass::hasSignal(const AbstractMetaFunction* other) const
{
  if (!other->isSignal())
    return false;

  for (const AbstractMetaFunction* f : functions()) {
    if (f->isSignal() && f->compareTo(other) & AbstractMetaFunction::EqualName)
      return other->modifiedName() == f->modifiedName();
  }

  return false;
}

QString AbstractMetaClass::name() const
{
  return QString(m_type_entry->targetLangName()).replace("::", "__");
}

bool AbstractMetaClass::hasFunction(const QString& str) const
{
  for (const AbstractMetaFunction* f : functions())
    if (f->name() == str)
      return true;
  return false;
}

/* Returns true if this class has one or more functions that are
   protected. If a class has protected members we need to generate a
   shell class with public accessors to the protected functions, so
   they can be called from the native functions.
*/
bool AbstractMetaClass::hasProtectedFunctions() const
{
  for (AbstractMetaFunction* func : m_functions) {
    if (func->isProtected())
      return true;
  }
  return false;
}

bool AbstractMetaClass::generateShellClass() const
{
  return typeEntry()->shouldCreateShell()
         && (m_force_shell_class
             || (!isFinal() && (hasVirtualFunctions() || hasProtectedFunctions() || hasFieldAccessors())));
}

QPropertySpec* AbstractMetaClass::propertySpecForRead(const QString& name) const
{
  for (int i = 0; i < m_property_specs.size(); ++i)
    if (name == m_property_specs.at(i)->read())
      return m_property_specs.at(i);
  return 0;
}

QPropertySpec* AbstractMetaClass::propertySpecForWrite(const QString& name) const
{
  for (int i = 0; i < m_property_specs.size(); ++i)
    if (name == m_property_specs.at(i)->write())
      return m_property_specs.at(i);
  return 0;
}

QPropertySpec* AbstractMetaClass::propertySpecForReset(const QString& name) const
{
  for (int i = 0; i < m_property_specs.size(); ++i) {
    if (name == m_property_specs.at(i)->reset())
      return m_property_specs.at(i);
  }
  return 0;
}

bool AbstractMetaClass::hasVirtualDestructor() const
{
  if (m_has_virtual_destructor) {
    return true;
  } else {
    // check all super classes
    for (int i = 0; i < m_super_classes.size(); ++i) {
      AbstractMetaClass* super_class = m_super_classes.at(i);
      if (super_class->hasVirtualDestructor()) {
        return true;
      }
    }
  }
  return false;
}

static bool functions_contains(const AbstractMetaFunctionList& l, const AbstractMetaFunction* func)
{
  for (const AbstractMetaFunction* f : l) {
    if ((f->compareTo(func) & AbstractMetaFunction::PrettySimilar) == AbstractMetaFunction::PrettySimilar)
      return true;
  }
  return false;
}

AbstractMetaField::~AbstractMetaField()
{
  delete m_setter;
  delete m_getter;
}
ushort painters; // refcount
AbstractMetaField* AbstractMetaField::copy() const
{
  AbstractMetaField* returned = new AbstractMetaField;
  returned->setEnclosingClass(0);
  returned->setAttributes(attributes());
  returned->setName(name());
  returned->setType(type());
  returned->setOriginalAttributes(originalAttributes());

  return returned;
}
/* UNUSED
static QString upCaseFirst(const QString &str) {
    Q_ASSERT(!str.isEmpty());
    QString s = str;
    s[0] = s.at(0).toUpper();
    return s;
} */

static AbstractMetaFunction* createXetter(const AbstractMetaField* g, const QString& name, uint type)
{
  AbstractMetaFunction* f = new AbstractMetaFunction;

  f->setName(name);
  f->setOriginalName(name);
  f->setOwnerClass(g->enclosingClass());
  f->setImplementingClass(g->enclosingClass());
  f->setDeclaringClass(g->enclosingClass());

  uint attr = AbstractMetaAttributes::Native | AbstractMetaAttributes::Final | type;
  if (g->isStatic())
    attr |= AbstractMetaAttributes::Static;
  if (g->isPublic())
    attr |= AbstractMetaAttributes::Public;
  else if (g->isProtected())
    attr |= AbstractMetaAttributes::Protected;
  else
    attr |= AbstractMetaAttributes::Private;
  f->setAttributes(attr);
  f->setOriginalAttributes(attr);

  const FieldModificationList mods = g->modifications();
  for (const FieldModification& mod : mods) {
    if (mod.isRenameModifier())
      f->setName(mod.renamedTo());
    if (mod.isAccessModifier()) {
      if (mod.isPrivate())
        f->setVisibility(AbstractMetaAttributes::Private);
      else if (mod.isProtected())
        f->setVisibility(AbstractMetaAttributes::Protected);
      else if (mod.isPublic())
        f->setVisibility(AbstractMetaAttributes::Public);
      else if (mod.isFriendly())
        f->setVisibility(AbstractMetaAttributes::Friendly);
    }
  }
  return f;
}

FieldModificationList AbstractMetaField::modifications() const
{
  FieldModificationList mods = enclosingClass()->typeEntry()->fieldModifications();
  FieldModificationList returned;

  for (FieldModification mod : mods) {
    if (mod.name == name())
      returned += mod;
  }

  return returned;
}

const AbstractMetaFunction* AbstractMetaField::setter() const
{
  if (m_setter == 0) {
    m_setter = createXetter(this, name(), AbstractMetaAttributes::SetterFunction);
    AbstractMetaArgumentList arguments;
    AbstractMetaArgument* argument = new AbstractMetaArgument;
    argument->setType(type());
    argument->setName(name());
    arguments.append(argument);
    m_setter->setArguments(arguments);
  }
  return m_setter;
}

const AbstractMetaFunction* AbstractMetaField::getter() const
{
  if (m_getter == 0) {
    m_getter = createXetter(this, name(), AbstractMetaAttributes::GetterFunction);
    m_getter->setType(type());
  }

  return m_getter;
}

bool AbstractMetaClass::hasConstructors() const
{
  return queryFunctions(Constructors).size() != 0;
}

void AbstractMetaClass::addDefaultConstructor()
{
  AbstractMetaFunction* f = new AbstractMetaFunction;
  f->setName(name());
  f->setOwnerClass(this);
  f->setFunctionType(AbstractMetaFunction::ConstructorFunction);
  f->setArguments(AbstractMetaArgumentList());
  f->setDeclaringClass(this);

  uint attr = AbstractMetaAttributes::Native;
  attr |= AbstractMetaAttributes::Public;
  f->setAttributes(attr);
  f->setImplementingClass(this);
  f->setOriginalAttributes(f->attributes());

  addFunction(f);
}

bool AbstractMetaClass::hasFunction(const AbstractMetaFunction* f) const
{
  return functions_contains(m_functions, f);
}

/* Goes through the list of functions and returns a list of all
   functions matching all of the criteria in \a query.
 */

AbstractMetaFunctionList AbstractMetaClass::queryFunctions(uint query) const
{
  AbstractMetaFunctionList functions;

  for (AbstractMetaFunction* f : m_functions) {

    if ((query & VirtualSlots) && !f->isVirtualSlot())
      continue;

    if ((query & NotRemovedFromTargetLang) && f->isRemovedFrom(f->implementingClass(), TypeSystem::TargetLangCode)) {
      continue;
    }

    if ((query & NotRemovedFromTargetLang) && !f->isFinal()
        && f->isRemovedFrom(f->declaringClass(), TypeSystem::TargetLangCode))
    {
      continue;
    }

    if ((query & NotRemovedFromShell) && f->isRemovedFrom(f->implementingClass(), TypeSystem::ShellCode)) {
      continue;
    }

    if ((query & NotRemovedFromShell) && !f->isFinal() && f->isRemovedFrom(f->declaringClass(), TypeSystem::ShellCode))
    {
      continue;
    }

    if ((query & Visible) && f->isPrivate()) {
      continue;
    }

    if ((query & VirtualInTargetLangFunctions) && f->isFinalInTargetLang()) {
      continue;
    }

    if ((query & Invisible) && !f->isPrivate()) {
      continue;
    }

    if ((query & Empty) && !f->isEmptyFunction()) {
      continue;
    }

    if ((query & WasPublic) && !f->wasPublic()) {
      continue;
    }

    if ((query & WasVisible) && f->wasPrivate()) {
      continue;
    }

    if ((query & WasProtected) && !f->wasProtected()) {
      continue;
    }

    if ((query & ClassImplements) && f->ownerClass() != f->implementingClass()) {
      continue;
    }

    if ((query & Inconsistent) && (f->isFinalInTargetLang() || !f->isFinalInCpp() || f->isStatic())) {
      continue;
    }

    if ((query & FinalInTargetLangFunctions) && !f->isFinalInTargetLang()) {
      continue;
    }

    if ((query & FinalInCppFunctions) && !f->isFinalInCpp()) {
      continue;
    }

    if ((query & VirtualInCppFunctions) && f->isFinalInCpp()) {
      continue;
    }

    if ((query & Signals) && (!f->isSignal())) {
      continue;
    }

    if ((query & ForcedShellFunctions) && (!f->isForcedShellImplementation() || !f->isFinal())) {
      continue;
    }

    if (((query & Constructors) && (!f->isConstructor() || f->ownerClass() != f->implementingClass()))
        || (f->isConstructor() && (query & Constructors) == 0))
    {
      continue;
    }

    // Destructors are never included in the functions of a class currently
    /*
           if ((query & Destructors) && (!f->isDestructor()
                                       || f->ownerClass() != f->implementingClass())
            || f->isDestructor() && (query & Destructors) == 0) {
            continue;
        }*/

    if ((query & VirtualFunctions) && (f->isFinal() || f->isSignal() || f->isStatic())) {
      continue;
    }

    if ((query & StaticFunctions) && (!f->isStatic() || f->isSignal())) {
      continue;
    }

    if ((query & NonStaticFunctions) && (f->isStatic())) {
      continue;
    }

    if ((query & NonEmptyFunctions) && (f->isEmptyFunction())) {
      continue;
    }

    if ((query & NormalFunctions) && (f->isSignal())) {
      continue;
    }

    if ((query & AbstractFunctions) && !f->isAbstract()) {
      continue;
    }

    functions << f;
  }

  //    qDebug() << "queried" << m_type_entry->qualifiedCppName() << "got" << functions.size() << "out of" << m_functions.size();

  return functions;
}

bool AbstractMetaClass::hasInconsistentFunctions() const
{
  return cppInconsistentFunctions().size() > 0;
}

bool AbstractMetaClass::hasSignals() const
{
  return cppSignalFunctions().size() > 0;
}

/**
 * Adds the specified interface to this class by adding all the
 * functions in the interface to this class.
 */
void AbstractMetaClass::addInterface(AbstractMetaClass* interface)
{
  Q_ASSERT(!m_interfaces.contains(interface));
  m_interfaces << interface;

  if (m_extracted_interface != 0 && m_extracted_interface != interface)
    m_extracted_interface->addInterface(interface);

  for (AbstractMetaFunction* function : interface->functions())
    if (!hasFunction(function) && !function->isConstructor()) {
      AbstractMetaFunction* cpy = function->copy();
      // We do not want this in PythonQt:
      //cpy->setImplementingClass(this);

      // Setup that this function is an interface class.
      cpy->setInterfaceClass(interface);
      *cpy += AbstractMetaAttributes::InterfaceFunction;

      // Copy the modifications in interface into the implementing classes.
      //FunctionModificationList mods = function->modifications(interface);
      //for (const FunctionModification& mod : mods) {
      //    m_type_entry->addFunctionModification(mod);
      //}

      // It should be mostly safe to assume that when we implement an interface
      // we don't "pass on" pure virtual functions to our sublcasses...
      //             *cpy -= AbstractMetaAttributes::Abstract;

      addFunction(cpy);
    }
}

void AbstractMetaClass::setInterfaces(const AbstractMetaClassList& interfaces)
{
  m_interfaces = interfaces;
}

AbstractMetaEnum* AbstractMetaClass::findEnum(const QString& enumName)
{
  for (AbstractMetaEnum* e : m_enums) {
    if (e->name() == enumName)
      return e;
  }

  if (typeEntry()->designatedInterface())
    return extractInterface()->findEnum(enumName);

  return 0;
}

/*!  Recursivly searches for the enum value named \a enumValueName in
  this class and its superclasses and interfaces. Values belonging to
  \a meta_enum are excluded from the search.
*/
AbstractMetaEnumValue* AbstractMetaClass::findEnumValue(const QString& enumValueName, AbstractMetaEnum* meta_enum)
{
  for (AbstractMetaEnum* e : m_enums) {
    if (e == meta_enum)
      continue;
    for (AbstractMetaEnumValue* v : e->values()) {
      if (v->name() == enumValueName)
        return v;
    }
  }

  if (typeEntry()->designatedInterface())
    return extractInterface()->findEnumValue(enumValueName, meta_enum);

  if (baseClass() != 0)
    return baseClass()->findEnumValue(enumValueName, meta_enum);

  return 0;
}

/*!
 * Searches through all of this class' enums for a value matching the
 * name \a enumValueName. The name is excluding the class/namespace
 * prefix. The function recursivly searches interfaces and baseclasses
 * of this class.
 */
AbstractMetaEnum* AbstractMetaClass::findEnumForValue(const QString& enumValueName)
{
  for (AbstractMetaEnum* e : m_enums) {
    for (AbstractMetaEnumValue* v : e->values()) {
      if (v->name() == enumValueName)
        return e;
    }
  }

  if (typeEntry()->designatedInterface())
    return extractInterface()->findEnumForValue(enumValueName);

  if (baseClass() != 0)
    return baseClass()->findEnumForValue(enumValueName);

  return 0;
}

static void add_extra_include_for_type(AbstractMetaClass* meta_class, AbstractMetaType::const_shared_pointer type)
{

  if (type == 0)
    return;

  Q_ASSERT(meta_class != 0);
  const TypeEntry* entry = (type ? type->typeEntry() : 0);
  if (entry != 0 && entry->isComplex()) {
    const ComplexTypeEntry* centry = static_cast<const ComplexTypeEntry*>(entry);
    ComplexTypeEntry* class_entry = meta_class->typeEntry();
    if (class_entry != 0 && centry->include().isValid())
      class_entry->addExtraInclude(centry->include());
  }

  if (type->hasInstantiations()) {
    auto& instantiations = type->instantiations();
    for (auto&& instantiation : instantiations)
      add_extra_include_for_type(meta_class, instantiation);
  }
}

static void add_extra_includes_for_function(AbstractMetaClass* meta_class, const AbstractMetaFunction* meta_function)
{
  Q_ASSERT(meta_class != 0);
  Q_ASSERT(meta_function != 0);
  add_extra_include_for_type(meta_class, meta_function->type());

  AbstractMetaArgumentList arguments = meta_function->arguments();
  for (AbstractMetaArgument* argument : arguments) {
    add_extra_include_for_type(meta_class, argument->type());
  }
}

void AbstractMetaClass::fixFunctions()
{
  if (m_functions_fixed)
    return;
  else
    m_functions_fixed = true;

  AbstractMetaClass* super_class = baseClass();
  AbstractMetaFunctionList funcs = functions();
  AbstractMetaClassList interfaceClasses = interfaces();

  //     printf("fix functions for %s\n", qPrintable(name()));

  if (super_class != 0)
    super_class->fixFunctions();
  int iface_idx = 0;
  while (super_class || iface_idx < interfaceClasses.size()) {

    // Since we always traverse the complete hierarchy we are only
    // interested in what each super class implements, not what
    // we may have propagated from their base classes again.
    AbstractMetaFunctionList super_funcs;
    if (super_class) {
      //             printf(" - base: %s\n", qPrintable(super_class->name()));

      // Super classes can never be final
      if (super_class->isFinalInTargetLang()) {
        ReportHandler::warning(
          "Final class '" + super_class->name() + "' set to non-final, as it is extended by other classes");
        *super_class -= AbstractMetaAttributes::FinalInTargetLang;
      }
      super_funcs = super_class->queryFunctions(AbstractMetaClass::ClassImplements);
    } else {
      AbstractMetaClass* iface_class = interfaceClasses.at(iface_idx);
      //             printf(" - iface: %s\n", qPrintable(iface_class->name()));
      iface_class->fixFunctions();
      super_funcs = iface_class->queryFunctions(AbstractMetaClass::NormalFunctions);
    }

    QSet<AbstractMetaFunction*> funcs_to_add;
    for (int sfi = 0; sfi < super_funcs.size(); ++sfi) {
      AbstractMetaFunction* sf = super_funcs.at(sfi);

      if (sf->isRemovedFromAllLanguages(sf->implementingClass()))
        continue;

      // we generally don't care about private functions, but we have to get the ones that are
      // virtual in case they override abstract functions.
      bool add = (sf->isNormal() || sf->isSignal() || sf->isEmptyFunction());
      for (int fi = 0; fi < funcs.size(); ++fi) {
        AbstractMetaFunction* f = funcs.at(fi);
        if (f->isRemovedFromAllLanguages(f->implementingClass()))
          continue;

        uint cmp = f->compareTo(sf);

        if (cmp & AbstractMetaFunction::EqualModifiedName) {
          //                     printf("   - %s::%s similar to %s::%s %x vs %x\n",
          //                            qPrintable(sf->implementingClass()->typeEntry()->qualifiedCppName()),
          //                            qPrintable(sf->name()),
          //                            qPrintable(f->implementingClass()->typeEntry()->qualifiedCppName()),
          //                            qPrintable(f->name()),
          //                            sf->attributes(),
          //                            f->attributes());

          add = false;
          if (cmp & AbstractMetaFunction::EqualArguments) {

            //                         if (!(cmp & AbstractMetaFunction::EqualReturnType)) {
            //                             ReportHandler::warning(QString("%1::%2 and %3::%4 differ in retur type")
            //                                                    .arg(sf->implementingClass()->name())
            //                                                    .arg(sf->name())
            //                                                    .arg(f->implementingClass()->name())
            //                                                    .arg(f->name()));
            //                         }

            // Same function, propagate virtual...
            if (!(cmp & AbstractMetaFunction::EqualAttributes)) {
              if (!f->isEmptyFunction()) {
                if (!sf->isFinalInCpp() && f->isFinalInCpp()) {
                  *f -= AbstractMetaAttributes::FinalInCpp;
                  //                                 printf("   --- inherit virtual\n");
                }
                if (!sf->isFinalInTargetLang() && f->isFinalInTargetLang()) {
                  *f -= AbstractMetaAttributes::FinalInTargetLang;
                  //                                 printf("   --- inherit virtual\n");
                }
                if (!f->isFinalInTargetLang() && f->isPrivate()) {
                  f->setFunctionType(AbstractMetaFunction::EmptyFunction);
                  f->setVisibility(AbstractMetaAttributes::Protected);
                  *f += AbstractMetaAttributes::FinalInTargetLang;
                  ReportHandler::warning(QString("private virtual function '%1' in '%2'")
                      .arg(f->signature())
                      .arg(f->implementingClass()->name()));
                }
              }
            }

            if (f->visibility() != sf->visibility()) {
              QString warn = QString("visibility of function '%1' modified in class '%2'").arg(f->name()).arg(name());
              ReportHandler::warning(warn);

              // If new visibility is private, we can't
              // do anything. If it isn't, then we
              // prefer the parent class's visibility
              // setting for the function.
              if (!f->isPrivate() && !sf->isPrivate())
                f->setVisibility(sf->visibility());

              // Private overrides of abstract functions have to go into the class or
              // the subclasses will not compile as non-abstract classes.
              // But they don't need to be implemented, since they can never be called.
              if (f->isPrivate() && sf->isAbstract()) {
                f->setFunctionType(AbstractMetaFunction::EmptyFunction);
                f->setVisibility(sf->visibility());
                *f += AbstractMetaAttributes::FinalInTargetLang;
                *f += AbstractMetaAttributes::FinalInCpp;
              }
            }

            // Set the class which first declares this function, afawk
            f->setDeclaringClass(sf->declaringClass());

            if (sf->isFinalInTargetLang() && !sf->isPrivate() && !f->isPrivate() && !sf->isStatic() && !f->isStatic()) {
              // Shadowed function, need to make base class
              // function non-virtual
              if (f->implementingClass() != sf->implementingClass()
                  && f->implementingClass()->inheritsFrom(sf->implementingClass()))
              {

                // Check whether the superclass method has been redefined to non-final

                bool hasNonFinalModifier = false;
                bool isBaseImplPrivate = false;
                FunctionModificationList mods = sf->modifications(sf->implementingClass());
                for (FunctionModification mod : mods) {
                  if (mod.isNonFinal()) {
                    hasNonFinalModifier = true;
                    break;
                  } else if (mod.isPrivate()) {
                    isBaseImplPrivate = true;
                    break;
                  }
                }

                if (!hasNonFinalModifier && !isBaseImplPrivate) {
                  ReportHandler::warning(QString::fromLatin1("Shadowing: %1::%2 and %3::%4; Java code will not compile")
                      .arg(sf->implementingClass()->name())
                      .arg(sf->signature())
                      .arg(f->implementingClass()->name())
                      .arg(f->signature()));
                }
              }
            }
          }

          /* I did not see a good reason for erasing the default arguments,
                       maybe this was a java related problem?
                       I uncommented this code, because it causes that the default arguments
                       of QBoxLayout::addWidget(QWidget, stretch, alignment) are removed because of
                       QLayout::addWidget(QWidget)
                       */
          /*
                    if (cmp & AbstractMetaFunction::EqualDefaultValueOverload) {
                        AbstractMetaArgumentList arguments;
                        if (f->arguments().size() < sf->arguments().size())
                            arguments = sf->arguments();
                        else
                            arguments = f->arguments();

                        for (int i=0; i<arguments.size(); ++i)
                            arguments[i]->setDefaultValueExpression(QString());
                    }*/

          // Otherwise we have function shadowing and we can
          // skip the thing...
        } else if (cmp & AbstractMetaFunction::EqualName && !sf->isSignal()) {

          // In the case of function shadowing where the function name has been altered to
          // avoid conflict, we don't copy in the original.
          add = false;
        }
      }

      if (add)
        funcs_to_add << sf;
    }

    for (AbstractMetaFunction* f : funcs_to_add) {
      funcs << f->copy();
    }

    if (super_class) {
      interfaceClasses += super_class->interfaces();
      super_class = super_class->baseClass();
    } else {
      iface_idx++;
    }
  }

  //     printf("end fix functions for %s\n", qPrintable(name()));

  bool hasPrivateConstructors = false;
  bool hasPublicConstructors = false;
  for (AbstractMetaFunction* func : funcs) {
    FunctionModificationList mods = func->modifications(this);
    for (const FunctionModification& mod : mods) {
      if (mod.isRenameModifier()) {
        //                 qDebug() << name() << func->originalName() << func << " from "
        //                          << func->implementingClass()->name() << "renamed to" << mod.renamedTo();
        func->setName(mod.renamedTo());
      }
    }

    // Make sure class is abstract if one of the functions is
    if (func->isAbstract()) {
      (*this) += AbstractMetaAttributes::Abstract;
      (*this) -= AbstractMetaAttributes::Final;
    }

    if (func->isConstructor()) {
      if (func->isPrivate())
        hasPrivateConstructors = true;
      else
        hasPublicConstructors = true;
    }

    // Make sure that we include files for all classes that are in use

    if (!func->isRemovedFrom(this, TypeSystem::ShellCode))
      add_extra_includes_for_function(this, func);
  }

  if (hasPrivateConstructors && !hasPublicConstructors) {
    (*this) += AbstractMetaAttributes::Abstract;
    (*this) -= AbstractMetaAttributes::Final;
  }

  // we don't care about FinalOverload for PythonQt, so we
  // can remove this compare orgy...

  /*for (AbstractMetaFunction* f1 : funcs) {
        for (AbstractMetaFunction* f2 : funcs) {
            if (f1 != f2) {
                uint cmp = f1->compareTo(f2);
                if ((cmp & AbstractMetaFunction::EqualName)
                    && !f1->isFinalInCpp()
                    && f2->isFinalInCpp()) {
                    *f2 += AbstractMetaAttributes::FinalOverload;
//                     qDebug() << f2 << f2->implementingClass()->name() << "::" << f2->name() << f2->arguments().size() << " vs " << f1 << f1->implementingClass()->name() << "::" << f1->name() << f1->arguments().size();
//                     qDebug() << "    " << f2;
//                     AbstractMetaArgumentList f2Args = f2->arguments();
//                     for (AbstractMetaArgument* a : f2Args)
//                         qDebug() << "        " << a->type()->name() << a->name();
//                     qDebug() << "    " << f1;
//                     AbstractMetaArgumentList f1Args = f1->arguments();
//                     for (AbstractMetaArgument* a : f1Args)
//                         qDebug() << "        " << a->type()->name() << a->name();

                }
            }
        }
    }*/

  setFunctions(funcs);
}

QString AbstractMetaType::minimalSignature() const
{
  QString minimalSignature;
  if (isConstant())
    minimalSignature += "const ";
  minimalSignature += typeEntry()->qualifiedCppName();
  if (hasInstantiations()) {
    auto& instantiations = this->instantiations();
    minimalSignature += "<";
    for (int i = 0; i < instantiations.size(); ++i) {
      if (i > 0)
        minimalSignature += ",";
      minimalSignature += instantiations.at(i)->minimalSignature();
    }
    minimalSignature += ">";
  }

  if (isReference())
    minimalSignature += "&";
  for (int j = 0; j < indirections(); ++j)
    minimalSignature += "*";

  return minimalSignature;
}

bool AbstractMetaType::hasNativeId() const
{
  return (isQObject() || isValue() || isObject()) && typeEntry()->isNativeIdBased();
}

/*******************************************************************************
 * Other stuff...
 */

AbstractMetaEnum* AbstractMetaClassList::findEnum(const EnumTypeEntry* entry) const
{
  Q_ASSERT(entry->isEnum());

  QString qualified_name = entry->qualifiedCppName();
  int pos = qualified_name.lastIndexOf("::");

  QString enum_name;
  QString class_name;

  if (pos > 0) {
    enum_name = qualified_name.mid(pos + 2);
    class_name = qualified_name.mid(0, pos);
  } else {
    enum_name = qualified_name;
    class_name = TypeDatabase::globalNamespaceClassName(entry);
  }

  AbstractMetaClass* meta_class = findClass(class_name);
  if (!meta_class) {
    ReportHandler::warning(
      QString("AbstractMeta::findEnum(), unknown class '%1' in '%2'").arg(class_name).arg(entry->qualifiedCppName()));
    return 0;
  }

  return meta_class->findEnum(enum_name);
}

AbstractMetaEnumValue* AbstractMetaEnumValueList::find(const QString& name) const
{
  for (int i = 0; i < size(); ++i) {
    if (name == at(i)->name())
      return at(i);
  }
  return 0;
}

AbstractMetaEnumValue* AbstractMetaClassList::findEnumValue(const QString& name) const
{
  QStringList lst = name.split(QLatin1String("::"));

  Q_ASSERT_X(lst.size() == 2, "AbstractMetaClassList::findEnumValue()", "Expected qualified enum");

  QString prefixName = lst.at(0);
  QString enumName = lst.at(1);

  AbstractMetaClass* cl = findClass(prefixName);
  if (cl)
    return cl->findEnumValue(enumName, 0);

  ReportHandler::warning(QString("no matching enum '%1'").arg(name));
  return 0;
}

/*!
 * Searches the list after a class that mathces \a name; either as
 * C++, Java base name or complete Java package.class name.
 */

AbstractMetaClass* AbstractMetaClassList::findClass(const QString& name) const
{
  if (name.isEmpty())
    return 0;

  for (AbstractMetaClass* c : *this) {
    if (c->qualifiedCppName() == name)
      return c;
  }

  for (AbstractMetaClass* c : *this) {
    if (c->fullName() == name)
      return c;
  }

  for (AbstractMetaClass* c : *this) {
    if (c->name() == name)
      return c;
  }

  return 0;
}

void AbstractMetaClassList::sort(void)
{
  std::stable_sort(begin(), end(), AbstractMetaClass::less_than);
}
