/* This file is part of kdepim. Copyright (c) 2004 Cornelius Schumacher Copyright (c) 2010 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "class.h" #include using namespace KODE; class Class::Private { public: QString mName; QString mNameSpace; QString mExportDeclaration; QString mDPointer; bool mUseSharedData = false; bool mCanBeCopied = false; Function::List mFunctions; MemberVariable::List mMemberVariables; QStringList mIncludes; QStringList mForwardDeclarations; Include::List mHeaderIncludes; Class::List mBaseClasses; Typedef::List mTypedefs; Enum::List mEnums; QString mDocs; Class::List mNestedClasses; QString mParentClassName; QStringList mDeclMacros; bool mIsQGadget = false; bool mIsQObject = false; }; Class::Class() : d(new Private) {} Class::Class(const Class &other) : d(new Private) { *d = *other.d; } Class::Class(const QString &name, const QString &nameSpace) : d(new Private) { Q_ASSERT(!name.isEmpty()); d->mName = name; d->mNameSpace = nameSpace; } Class::~Class() { delete d; } Class &Class::operator=(const Class &other) { if (this == &other) return *this; *d = *other.d; return *this; } void Class::setName(const QString &name) { Q_ASSERT(!name.isEmpty()); d->mName = name; } QString Class::name() const { return d->mName; } void Class::setNameSpace(const QString &nameSpace) { d->mNameSpace = nameSpace; } QString Class::nameSpace() const { return d->mNameSpace; } QString Class::qualifiedName() const { if (d->mNameSpace.isEmpty()) return d->mName; return d->mNameSpace + QLatin1String("::") + d->mName; } void Class::setExportDeclaration(const QString &name) { addHeaderInclude(name.toLower() + "_export.h"); if (name.contains("/")) { d->mExportDeclaration = name.split("/").value(1); } else { d->mExportDeclaration = name; } } QString Class::exportDeclaration() const { return d->mExportDeclaration; } void Class::setUseDPointer(bool useDPointer, const QString &dPointer) { d->mDPointer = useDPointer ? dPointer : QString(); } bool Class::useDPointer() const { return !d->mDPointer.isEmpty(); } void Class::setUseSharedData(bool b, const QString &dPointer) { d->mUseSharedData = b; if (b) { setUseDPointer(true, dPointer); d->mCanBeCopied = true; } } bool Class::useSharedData() const { return d->mUseSharedData; } void Class::setCanBeCopied(bool b) { d->mCanBeCopied = b; } bool Class::canBeCopied() const { return d->mCanBeCopied; } void Class::addInclude(const QString &include, const QString &forwardDeclaration) { if (!include.isEmpty() && !d->mIncludes.contains(include)) d->mIncludes.append(include); if (!forwardDeclaration.isEmpty() && !d->mForwardDeclarations.contains(forwardDeclaration)) d->mForwardDeclarations.append(forwardDeclaration); } void Class::addIncludes(const QStringList &files, const QStringList &forwardDeclarations) { for (int i = 0; i < files.count(); ++i) { if (!d->mIncludes.contains(files[i])) if (!files[i].isEmpty()) d->mIncludes.append(files[i]); } for (int i = 0; i < forwardDeclarations.count(); ++i) { if (!d->mForwardDeclarations.contains(forwardDeclarations[i])) d->mForwardDeclarations.append(forwardDeclarations[i]); } } QStringList Class::includes() const { return d->mIncludes; } QStringList Class::forwardDeclarations() const { return d->mForwardDeclarations; } void Class::addHeaderInclude(const QString &include, Include::IncludeType type) { auto newInclude = Include(include, type); if (include.isEmpty() || d->mHeaderIncludes.contains(newInclude)) return; d->mHeaderIncludes.append(newInclude); } void Class::addHeaderIncludes(const QStringList &includes, Include::IncludeType type) { QStringList::ConstIterator it; for (it = includes.constBegin(); it != includes.constEnd(); ++it) addHeaderInclude(*it, type); } Include::List Class::headerIncludes() const { return d->mHeaderIncludes; } void Class::addBaseClass(const Class &c) { d->mBaseClasses.append(c); } Class::List Class::baseClasses() const { return d->mBaseClasses; } void Class::addFunction(const Function &function) { d->mFunctions.append(function); if (!d->mIsQObject) { if (function.access() & Function::Signal || function.access() & Function::Slot) d->mIsQObject = true; } } Function::List Class::functions() const { return d->mFunctions; } void Class::addMemberVariable(const MemberVariable &v) { d->mMemberVariables.append(v); } MemberVariable::List Class::memberVariables() const { return d->mMemberVariables; } void Class::addTypedef(const Typedef &typeDefinition) { d->mTypedefs.append(typeDefinition); } Typedef::List Class::typedefs() const { return d->mTypedefs; } void Class::addEnum(const Enum &enumValue) { d->mEnums.append(enumValue); } Enum::List Class::enums() const { return d->mEnums; } bool Class::hasEnum(const QString &name) const { for (const Enum &e : std::as_const(d->mEnums)) { if (e.name() == name) return true; } return false; } bool Class::isValid() const { return !d->mName.isEmpty(); } bool Class::hasFunction(const QString &functionName) const { Function::List::ConstIterator it; for (it = d->mFunctions.constBegin(); it != d->mFunctions.constEnd(); ++it) { if ((*it).name() == functionName) return true; } return false; } void Class::setQObject(bool isQObject) { d->mIsQObject = isQObject; } bool Class::isQObject() const { return d->mIsQObject; } bool Class::isQGadget() const { return d->mIsQGadget; } void Class::setQGadget(const bool isQGadget) { d->mIsQGadget = isQGadget; } void Class::setDocs(const QString &str) { d->mDocs = str; } QString Class::docs() const { return d->mDocs; } void Class::addNestedClass(const Class &nestedClass) { Class addedClass = nestedClass; addedClass.setParentClassName(name()); d->mNestedClasses.append(addedClass); } Class::List Class::nestedClasses() const { return d->mNestedClasses; } QString Class::parentClassName() const { return d->mParentClassName; } void Class::setParentClassName(const QString &parentClassName) { d->mParentClassName = parentClassName; } QString Class::dPointerName() const { return d->mDPointer; } //// // Returns what a class depends on: its base class(es) and any by-value member var static QStringList dependenciesForClass(const Class &aClass, const QStringList &allClasses, const QStringList &excludedClasses) { QStringList lst; Q_FOREACH (const Class &baseClass, aClass.baseClasses()) { const QString baseName = baseClass.qualifiedName(); if (!baseName.startsWith('Q') && !excludedClasses.contains(baseName)) lst.append(baseClass.name()); } if (!aClass.useDPointer()) { Q_FOREACH (const MemberVariable &member, aClass.memberVariables()) { const QString type = member.type(); if (allClasses.contains(type)) { lst.append(type); } } } return lst; } static bool allKnown(const QStringList &deps, const QStringList &classNames) { Q_FOREACH (const QString &dep, deps) { if (!classNames.contains(dep)) { return false; } } return true; } static bool classLessThan(const Class &c1, const Class &c2) { return c1.qualifiedName() < c2.qualifiedName(); } /** * This method sorts a list of classes in a way that the base class * of a class, as well as the classes it use by value in member vars, * always appear before the class itself. */ static Class::List sortByDependenciesHelper(const Class::List &classes, const QStringList &excludedClasses) { Class::List allClasses(classes); std::sort(allClasses.begin(), allClasses.end(), classLessThan); // make result deterministic QStringList allClassNames; Q_FOREACH (const Class &c, allClasses) allClassNames.append(c.qualifiedName()); Class::List retval; QStringList classNames; // copy all classes without dependencies Class::List::Iterator it = allClasses.begin(); while (it != allClasses.end()) { if (dependenciesForClass(*it, allClassNames, excludedClasses).isEmpty()) { retval.append(*it); classNames.append((*it).qualifiedName()); it = allClasses.erase(it); } else { ++it; } } while (!allClasses.isEmpty()) { const int currentCount = allClasses.count(); // copy all classes which have a class from retval/classNames (i.e. already written out) // as base class - or as member variable for (it = allClasses.begin(); it != allClasses.end();) { const QStringList deps = dependenciesForClass(*it, allClassNames, excludedClasses); if (allKnown(deps, classNames)) { retval.append(*it); classNames.append((*it).qualifiedName()); it = allClasses.erase(it); } else { ++it; } } if (allClasses.count() == currentCount) { // We didn't resolve anything this time around, so let's not loop forever qDebug() << "ERROR: Couldn't find class dependencies (base classes, member vars) for " "classes" << allClasses.classNames(); Q_FOREACH (const Class &c, allClasses) { qDebug() << c.qualifiedName() << "depends on" << dependenciesForClass(c, allClassNames, excludedClasses); } return retval; } } return retval; } void ClassList::sortByDependencies(const QStringList &excludedClasses) { *this = sortByDependenciesHelper(*this, excludedClasses); } void ClassList::sortAlphabetically() { std::sort(begin(), end(), classLessThan); } ClassList::const_iterator ClassList::findClass(const QString &qualifiedName) const { ClassList::const_iterator it = begin(); for (; it != end(); ++it) if ((*it).qualifiedName() == qualifiedName) break; return it; } QStringList KODE::ClassList::classNames() const { QStringList names; ClassList::const_iterator it = begin(); for (; it != end(); ++it) names.append((*it).name()); return names; } void KODE::ClassList::addClass(const Class &cl) { const QString qn = cl.qualifiedName(); ClassList::iterator it = begin(); for (; it != end(); ++it) { if ((*it).qualifiedName() == qn) { qWarning() << "ERROR: Already having a class called" << qn; } } *this += cl; } void KODE::Class::addDeclarationMacro(const QString ¯o) { d->mDeclMacros.append(macro); } QStringList KODE::Class::declarationMacros() const { return d->mDeclMacros; } void KODE::Class::setNamespaceAndName(const QString &name) { d->mName = name; d->mNameSpace.clear(); while (d->mName.contains("::")) { const int pos = d->mName.indexOf("::"); if (!d->mNameSpace.isEmpty()) d->mNameSpace += QLatin1String("::"); d->mNameSpace += d->mName.left(pos); d->mName = d->mName.mid(pos + 2); } }