diff --git a/umbrello/codeimport/import_utils.cpp b/umbrello/codeimport/import_utils.cpp index 08c1b2616..71d397233 100644 --- a/umbrello/codeimport/import_utils.cpp +++ b/umbrello/codeimport/import_utils.cpp @@ -1,726 +1,726 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2005-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "import_utils.h" // app includes #include "association.h" #include "artifact.h" #include "classifier.h" #include "datatype.h" #include "debug_utils.h" #include "folder.h" #include "enum.h" #include "object_factory.h" #include "operation.h" #include "package.h" #include "template.h" #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "umlobject.h" // kde includes #include #include #ifdef Q_OS_WIN #define PATH_SEPARATOR QLatin1Char(';') #else #define PATH_SEPARATOR QLatin1Char(':') #endif DEBUG_REGISTER_DISABLED(Import_Utils) #undef DBG_SRC #define DBG_SRC QLatin1String("Import_Utils") namespace Import_Utils { /** * Flag manipulated by createUMLObject(). * Global state is generally bad, I know. * It would be cleaner to make this into a return value from * createUMLObject(). */ bool bNewUMLObjectWasCreated = false; /** * Related classifier for creation of dependencies on template * parameters in createUMLObject(). */ UMLClassifier * gRelatedClassifier = 0; /** * On encountering a scoped typename string where the scopes * have not yet been seen, we synthesize UML objects for the * unknown scopes (using a question dialog to the user to decide * whether to treat a scope as a class or as a package.) * However, such an unknown scope is put at the global level. * I.e. before calling createUMLObject() we set this flag to true. */ bool bPutAtGlobalScope = false; /** * The include path list (see addIncludePath() and includePathList()) */ QStringList incPathList; /** * Control whether an object which is newly created by createUMLObject() * is put at the global scope. * * @param yesno When set to false, the object is created at the scope * given by the parentPkg argument of createUMLObject(). */ void putAtGlobalScope(bool yesno) { bPutAtGlobalScope = yesno; } /** * Set a related classifier for creation of dependencies on template * parameters in createUMLObject(). */ void setRelatedClassifier(UMLClassifier *c) { gRelatedClassifier = c; } /** * Control whether the creation methods solicit a new unique ID for the * created object. * By default, unique ID generation is turned on. * * @param yesno False turns UID generation off, true turns it on. */ void assignUniqueIdOnCreation(bool yesno) { Object_Factory::assignUniqueIdOnCreation(yesno); } /** * Returns whether the last createUMLObject() actually created * a new object or just returned an existing one. */ bool newUMLObjectWasCreated() { return bNewUMLObjectWasCreated; } /** * Strip comment lines of leading whitespace and stars. */ QString formatComment(const QString &comment) { if (comment.isEmpty()) return comment; QStringList lines = comment.split(QLatin1Char('\n')); QString& first = lines.first(); QRegExp wordex(QLatin1String("\\w")); if (first.startsWith(QLatin1String(QLatin1String("/*")))) { int wordpos = wordex.indexIn(first); if (wordpos != -1) first = first.mid(wordpos); // remove comment start else lines.pop_front(); // nothing interesting on this line } if (! lines.count()) return QString(); QString& last = lines.last(); int endpos = last.indexOf(QLatin1String("*/")); if (endpos != -1) { if (last.contains(wordex)) last = last.mid(0, endpos - 1); // remove comment end else lines.pop_back(); // nothing interesting on this line } if (! lines.count()) return QString(); QStringList::Iterator end(lines.end()); for (QStringList::Iterator lit(lines.begin()); lit != end; ++lit) { (*lit).remove(QRegExp(QLatin1String("^\\s+"))); (*lit).remove(QRegExp(QLatin1String("^\\*+\\s?"))); } return lines.join(QLatin1String("\n")); } /* UMLObject* findUMLObject(QString name, UMLObject::ObjectType type) { // Why an extra wrapper? See comment at addMethodParameter() UMLObject * o = umldoc->findUMLObject(name, type); return o; } */ /** * Find or create a document object. * @param type object type * @param inName name of uml object * @param parentPkg parent package * @param comment comment for uml object * @param stereotype stereotype for uml object * @param searchInParentPackageOnly flags to search only in parent package * @param remapParent flag to control remapping of parents if an uml object has been found * @return new object or zero */ UMLObject *createUMLObject(UMLObject::ObjectType type, const QString& inName, UMLPackage *parentPkg, const QString& comment, const QString& stereotype, bool searchInParentPackageOnly, bool remapParent) { QString name = inName; UMLDoc *umldoc = UMLApp::app()->document(); UMLFolder *logicalView = umldoc->rootFolder(Uml::ModelType::Logical); if (parentPkg == 0) { // DEBUG(DBG_SRC) << "Import_Utils::createUMLObject(" << name // << "): parentPkg is 0, assuming Logical View"; parentPkg = logicalView; } else if (parentPkg->baseType() == UMLObject::ot_Artifact) { DEBUG(DBG_SRC) << "Import_Utils::createUMLObject(" << name << "): Artifact as parent package is not supported yet, using Logical View"; parentPkg = logicalView; } else if (parentPkg->baseType() == UMLObject::ot_Association) { DEBUG(DBG_SRC) << "Import_Utils::createUMLObject(" << name << "): Association as parent package is not supported yet, using Logical View"; parentPkg = logicalView; } else if (name.startsWith(UMLApp::app()->activeLanguageScopeSeparator())) { name = name.mid(2); parentPkg = logicalView; } bNewUMLObjectWasCreated = false; UMLObject *o = 0; if (searchInParentPackageOnly) { o = Model_Utils::findUMLObject(parentPkg->containedObjects(), name, type); if (!o) { o = Object_Factory::createNewUMLObject(type, name, parentPkg); bNewUMLObjectWasCreated = true; bPutAtGlobalScope = false; } } else { o = umldoc->findUMLObject(name, type, parentPkg); } if (o == 0) { // Strip possible adornments and look again. const bool isConst = name.contains(QRegExp(QLatin1String("^const "))); name.remove(QRegExp(QLatin1String("^const\\s+"))); QString typeName(name); bool isAdorned = typeName.contains(QRegExp(QLatin1String("[^\\w:\\. ]"))); const bool isPointer = typeName.contains(QLatin1Char('*')); const bool isRef = typeName.contains(QLatin1Char('&')); typeName.remove(QRegExp(QLatin1String("[^\\w:\\. ].*$"))); typeName = typeName.simplified(); UMLObject *origType = umldoc->findUMLObject(typeName, UMLObject::ot_UMLObject, parentPkg); if (origType == 0) { // Still not found. Create the stripped down type. if (bPutAtGlobalScope) parentPkg = logicalView; // Find, or create, the scopes. QStringList components; QString scopeSeparator = UMLApp::app()->activeLanguageScopeSeparator(); if (typeName.contains(scopeSeparator)) { components = typeName.split(scopeSeparator, QString::SkipEmptyParts); } else if (typeName.contains(QLatin1String("..."))) { // Java variable length arguments type = UMLObject::ot_Datatype; parentPkg = umldoc->datatypeFolder(); isAdorned = false; } if (components.count() > 1) { typeName = components.back(); components.pop_back(); while (components.count()) { QString scopeName = components.front(); components.pop_front(); o = umldoc->findUMLObject(scopeName, UMLObject::ot_UMLObject, parentPkg); if (o) { parentPkg = o->asUMLPackage(); continue; } o = Object_Factory::createUMLObject(UMLObject::ot_Class, scopeName, parentPkg); o->setStereotypeCmd(QLatin1String("class-or-package")); // setStereotypeCmd() triggers tree view item update if not loading by default if (umldoc->loading()) { UMLListViewItem *item = UMLApp::app()->listView()->findUMLObject(o); if (item) item->updateObject(); } parentPkg = o->asUMLPackage(); Model_Utils::treeViewSetCurrentItem(o); } // All scope qualified datatypes live in the global scope. bPutAtGlobalScope = true; } UMLObject::ObjectType t = type; if (type == UMLObject::ot_UMLObject || isAdorned) t = UMLObject::ot_Class; origType = Object_Factory::createUMLObject(t, typeName, parentPkg, false); bNewUMLObjectWasCreated = true; bPutAtGlobalScope = false; } if (isConst || isAdorned) { // Create the full given type (including adornments.) if (isConst) name.prepend(QLatin1String("const ")); o = Object_Factory::createUMLObject(UMLObject::ot_Datatype, name, umldoc->datatypeFolder(), false); //solicitNewName UMLDatatype *dt = o ? o->asUMLDatatype() : 0; UMLClassifier *c = origType->asUMLClassifier(); if (dt && c) dt->setOriginType(c); else uError() << "createUMLObject(" << name << "): " << "origType " << typeName << " is not a UMLClassifier"; if (dt && (isRef || isPointer)) dt->setIsReference(); /* if (isPointer) { UMLObject *pointerDecl = Object_Factory::createUMLObject(UMLObject::ot_Datatype, type); UMLClassifier *dt = pointerDecl->asUMLClassifier(); dt->setOriginType(classifier); dt->setIsReference(); classifier = dt; } */ } else { o = origType; } } else if (parentPkg && !bPutAtGlobalScope && remapParent) { UMLPackage *existingPkg = o->umlPackage(); if (existingPkg != parentPkg && existingPkg != umldoc->datatypeFolder()) { if (existingPkg) existingPkg->removeObject(o); else uError() << "createUMLObject(" << name << "): " << "o->getUMLPackage() was NULL"; parentPkg->addObject(o); o->setUMLPackage(parentPkg); // setUMLPackage() triggers tree view item update if not loading by default if (umldoc->loading()) { UMLListViewItem *item = UMLApp::app()->listView()->findUMLObject(o); if (item) item->updateObject(); } } } QString strippedComment = formatComment(comment); if (! strippedComment.isEmpty()) { o->setDoc(strippedComment); } if (o && !stereotype.isEmpty()) { o->setStereotype(stereotype); } if (gRelatedClassifier == 0 || gRelatedClassifier == o) return o; QRegExp templateInstantiation(QLatin1String("^[\\w:\\.]+\\s*<(.*)>")); int pos = templateInstantiation.indexIn(name); if (pos == -1) return o; // Create dependencies on template parameters. QString caption = templateInstantiation.cap(1); const QStringList params = caption.split(QRegExp(QLatin1String("[^\\w:\\.]+"))); if (!params.count()) return o; QStringList::ConstIterator end(params.end()); for (QStringList::ConstIterator it(params.begin()); it != end; ++it) { UMLObject *p = umldoc->findUMLObject(*it, UMLObject::ot_UMLObject, parentPkg); if (p == 0 || p->isUMLDatatype()) continue; const Uml::AssociationType::Enum at = Uml::AssociationType::Dependency; UMLAssociation *assoc = umldoc->findAssociation(at, gRelatedClassifier, p); if (assoc) continue; assoc = new UMLAssociation(at, gRelatedClassifier, p); assoc->setUMLPackage(umldoc->rootFolder(Uml::ModelType::Logical)); umldoc->addAssociation(assoc); } if (o == 0) { uError() << "is NULL!"; } return o; } /** * Create a UMLOperation. * The reason for this method is to not generate any Qt signals. * Instead, these are generated by insertMethod(). * (If we generated a creation signal prematurely, i.e. without * the method parameters being known yet, then that would lead to * a conflict with a pre-existing parameterless method of the same * name.) */ UMLOperation* makeOperation(UMLClassifier *parent, const QString &name) { UMLOperation *op = Object_Factory::createOperation(parent, name); return op; } /** * Create a UMLAttribute and insert it into the document. * Use the specified existing attrType. */ -UMLObject* insertAttribute(UMLClassifier *owner, +UMLAttribute* insertAttribute(UMLClassifier *owner, Uml::Visibility::Enum scope, const QString& name, UMLClassifier *attrType, const QString& comment /* =QString() */, bool isStatic /* =false */) { UMLObject::ObjectType ot = owner->baseType(); Uml::ProgrammingLanguage::Enum pl = UMLApp::app()->activeLanguage(); if (! (ot == UMLObject::ot_Class || (ot == UMLObject::ot_Interface && pl == Uml::ProgrammingLanguage::Java))) { DEBUG(DBG_SRC) << "insertAttribute: Don not know what to do with " << owner->name() << " (object type " << UMLObject::toString(ot) << ")"; return 0; } UMLObject *o = owner->findChildObject(name, UMLObject::ot_Attribute); if (o) { - return o; + return o->asUMLAttribute(); } UMLAttribute *attr = owner->addAttribute(name, attrType, scope); attr->setStatic(isStatic); QString strippedComment = formatComment(comment); if (! strippedComment.isEmpty()) { attr->setDoc(strippedComment); } UMLApp::app()->document()->setModified(true); return attr; } /** * Create a UMLAttribute and insert it into the document. */ -UMLObject* insertAttribute(UMLClassifier *owner, Uml::Visibility::Enum scope, +UMLAttribute *insertAttribute(UMLClassifier *owner, Uml::Visibility::Enum scope, const QString& name, const QString& type, const QString& comment /* =QString() */, bool isStatic /* =false */) { UMLObject *attrType = owner->findTemplate(type); if (attrType == 0) { bPutAtGlobalScope = true; gRelatedClassifier = owner; attrType = createUMLObject(UMLObject::ot_UMLObject, type, owner); gRelatedClassifier = 0; bPutAtGlobalScope = false; } return insertAttribute (owner, scope, name, attrType->asUMLClassifier(), comment, isStatic); } /** * Insert the UMLOperation into the given classifier. * * @param klass The classifier into which the operation shall be added. * @param op Reference to pointer to the temporary UMLOperation * for insertion. The caller relinquishes ownership of the * object pointed to. If an UMLOperation of same signature * already exists at the classifier then the incoming * UMLOperation is deleted and the pointer is set to the * existing UMLOperation. * @param scope The Uml::Visibility of the method * @param type The return type * @param isStatic boolean switch to decide if method is static * @param isAbstract boolean switch to decide if method is abstract * @param isFriend true boolean switch to decide if methods is a friend function * @param isConstructor boolean switch to decide if methods is a constructor * @param isDestructor boolean switch to decide if methods is a destructor * @param comment The Documentation for this method */ void insertMethod(UMLClassifier *klass, UMLOperation* &op, Uml::Visibility::Enum scope, const QString& type, bool isStatic, bool isAbstract, bool isFriend, bool isConstructor, bool isDestructor, const QString& comment) { op->setVisibilityCmd(scope); if (!type.isEmpty() // return type may be missing (constructor/destructor) && type != QLatin1String("void")) { if (type == klass->name()) { op->setType(klass); } else { UMLObject *typeObj = klass->findTemplate(type); if (typeObj == 0) { bPutAtGlobalScope = true; gRelatedClassifier = klass; typeObj = createUMLObject(UMLObject::ot_UMLObject, type, klass); gRelatedClassifier = 0; bPutAtGlobalScope = false; op->setType(typeObj); } } } op->setStatic(isStatic); op->setAbstract(isAbstract); // if the operation is friend, add it as a stereotype if (isFriend) op->setStereotype(QLatin1String("friend")); // if the operation is a constructor, add it as a stereotype if (isConstructor) op->setStereotype(QLatin1String("constructor")); if (isDestructor) op->setStereotype(QLatin1String("destructor")); QString strippedComment = formatComment(comment); if (! strippedComment.isEmpty()) { op->setDoc(strippedComment); } UMLAttributeList params = op->getParmList(); UMLOperation *exist = klass->checkOperationSignature(op->name(), params); if (exist) { // copy contents to existing operation exist->setVisibilityCmd(scope); exist->setStatic(isStatic); exist->setAbstract(isAbstract); if (! strippedComment.isEmpty()) exist->setDoc(strippedComment); UMLAttributeList exParams = exist->getParmList(); for (UMLAttributeListIt it(params), exIt(exParams) ; it.hasNext() ;) { UMLAttribute *param = it.next(), *exParam = exIt.next(); exParam->setName(param->name()); exParam->setVisibilityCmd(param->visibility()); exParam->setStatic(param->isStatic()); exParam->setAbstract(param->isAbstract()); exParam->setDoc(param->doc()); exParam->setInitialValue(param->getInitialValue()); exParam->setParmKind(param->getParmKind()); } // delete incoming UMLOperation and pass out the existing one delete op; op = exist; } else { klass->addOperation(op); } } /** * Add an argument to a UMLOperation. * The parentPkg arg is only used for resolving possible scope * prefixes in the `type'. */ UMLAttribute* addMethodParameter(UMLOperation *method, const QString& type, const QString& name) { UMLClassifier *owner = method->umlParent()->asUMLClassifier(); UMLObject *typeObj = owner ? owner->findTemplate(type) : 0; if (typeObj == 0) { bPutAtGlobalScope = true; gRelatedClassifier = owner; typeObj = createUMLObject(UMLObject::ot_UMLObject, type, owner); gRelatedClassifier = 0; bPutAtGlobalScope = false; } UMLAttribute *attr = Object_Factory::createAttribute(method, name, typeObj); method->addParm(attr); return attr; } /** * Add an enum literal to an UMLEnum. */ void addEnumLiteral(UMLEnum *enumType, const QString &literal, const QString &comment, const QString &value) { UMLObject *el = enumType->addEnumLiteral(literal, Uml::ID::None, value); el->setDoc(comment); } /** * Create a generalization from the given child classifier to the given * parent classifier. */ UMLAssociation *createGeneralization(UMLClassifier *child, UMLClassifier *parent) { // if the child is an interface, so is the parent. if (child->isInterface()) parent->setBaseType(UMLObject::ot_Interface); Uml::AssociationType::Enum association = Uml::AssociationType::Generalization; if (parent->isInterface() && !child->isInterface()) { // if the parent is an interface, but the child is not, then // this is really realization. // association = Uml::AssociationType::Realization; } UMLAssociation *assoc = new UMLAssociation(association, child, parent); UMLDoc *umldoc = UMLApp::app()->document(); assoc->setUMLPackage(umldoc->rootFolder(Uml::ModelType::Logical)); umldoc->addAssociation(assoc); return assoc; } /** * Create a subdir with the given name. */ UMLFolder *createSubDir(const QString& name, UMLFolder *parentPkg, const QString &comment) { UMLDoc *umldoc = UMLApp::app()->document(); if (!parentPkg) { parentPkg = umldoc->rootFolder(Uml::ModelType::Component); } UMLObject::ObjectType type = UMLObject::ot_Folder; UMLFolder *o = umldoc->findUMLObjectRaw(parentPkg, name, type)->asUMLFolder(); if (o) return o; o = Object_Factory::createUMLObject(type, name, parentPkg, false)->asUMLFolder(); if (o) o->setDoc(comment); return o; } /** * Create a folder for artifacts */ UMLObject *createArtifactFolder(const QString& name, UMLPackage *parentPkg, const QString &comment) { Q_UNUSED(parentPkg); UMLObject::ObjectType type = UMLObject::ot_Folder; UMLDoc *umldoc = UMLApp::app()->document(); UMLFolder *componentView = umldoc->rootFolder(Uml::ModelType::Component); UMLObject *o = umldoc->findUMLObjectRaw(componentView, name, type); if (o) return o; o = Object_Factory::createUMLObject(type, name, componentView, false); UMLFolder *a = o->asUMLFolder(); a->setDoc(comment); DEBUG(DBG_SRC) << name << comment; return o; } /** * Create an artifact with the given name. */ UMLObject *createArtifact(const QString& name, UMLFolder *parentPkg, const QString &comment) { UMLDoc *umldoc = UMLApp::app()->document(); if (!parentPkg) { parentPkg = umldoc->rootFolder(Uml::ModelType::Component); } UMLObject::ObjectType type = UMLObject::ot_Artifact; UMLObject *o = umldoc->findUMLObjectRaw(parentPkg, name, type); if (o) return o; o = Object_Factory::createUMLObject(type, name, parentPkg, false); UMLArtifact *a = o->asUMLArtifact(); a->setDrawAsType(UMLArtifact::file); a->setDoc(comment); DEBUG(DBG_SRC) << name << comment; return o; } /** * Create a generalization from the existing child UMLObject to the given * parent class name. * This method does not handle scopes well and is only a last resort. * The method * createGeneralization(UMLClassifier *child, UMLClassifier *parent) * should be used instead. */ void createGeneralization(UMLClassifier *child, const QString &parentName) { UMLObject *parentObj = createUMLObject(UMLObject::ot_Class, parentName); UMLClassifier *parent = parentObj->asUMLClassifier(); createGeneralization(child, parent); } /** * Remap UMLObject instance in case it does not have the correct type. * * @param ns uml object instance with incorrect class * @param currentScope parent uml object * @return newly created UMLEnum instance or zero in case of error */ UMLEnum *remapUMLEnum(UMLObject *ns, UMLPackage *currentScope) { if (ns) { QString comment = ns->doc(); QString name = ns->name(); QString stereotype = ns->stereotype(); Uml::Visibility::Enum visibility = ns->visibility(); UMLApp::app()->document()->removeUMLObject(ns, true); if (currentScope == 0) currentScope = UMLApp::app()->document()->rootFolder(Uml::ModelType::Logical); UMLObject *o = Object_Factory::createNewUMLObject(UMLObject::ot_Enum, name, currentScope, false); if (!o) return 0; UMLEnum *e = o->asUMLEnum(); if (!e) return 0; e->setDoc(comment); e->setStereotypeCmd(stereotype.isEmpty() ? QLatin1String("enum") : stereotype); e->setVisibilityCmd(visibility); // add to parents child list if (!currentScope->containedObjects().contains(e)) currentScope->containedObjects().append(e); return e; } return 0; } /** * Return the list of paths set by previous calls to addIncludePath() * and the environment variable UMBRELLO_INCPATH. * This list can be used for finding included (or Ada with'ed or...) * files. */ QStringList includePathList() { QStringList includePathList(incPathList); QString umbrello_incpath = QString::fromLatin1(qgetenv("UMBRELLO_INCPATH")); if (!umbrello_incpath.isEmpty()) { includePathList += umbrello_incpath.split(PATH_SEPARATOR); } return includePathList; } /** * Add a path to the include path list. */ void addIncludePath(const QString& path) { if (! incPathList.contains(path)) incPathList.append(path); } /** * Returns true if a type is an actual Datatype */ bool isDatatype(const QString& name, UMLPackage *parentPkg) { UMLDoc *umldoc = UMLApp::app()->document(); UMLObject * o = umldoc->findUMLObject(name, UMLObject::ot_Datatype, parentPkg); return (o != 0); } } // end namespace Import_Utils diff --git a/umbrello/codeimport/import_utils.h b/umbrello/codeimport/import_utils.h index 70fa925c9..187d1c5fd 100644 --- a/umbrello/codeimport/import_utils.h +++ b/umbrello/codeimport/import_utils.h @@ -1,106 +1,106 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2005-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef IMPORT_UTILS_H #define IMPORT_UTILS_H #include "basictypes.h" #include "folder.h" #include "umlattributelist.h" #include class UMLObject; class UMLClassifier; class UMLPackage; class UMLOperation; class UMLEnum; class UMLScene; class QMimeData; /** * Utilities for code import * @author Oliver Kellogg * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ namespace Import_Utils { UMLFolder *createSubDir(const QString& name, UMLFolder *parentPkg, const QString &comment = QString()); UMLObject *createArtifactFolder(const QString& name, UMLPackage *parentPkg, const QString &comment); UMLObject *createArtifact(const QString& name, UMLFolder *parentPkg = NULL, const QString &comment = QString()); UMLObject* createUMLObject(UMLObject::ObjectType type, const QString& name, UMLPackage *parentPkg = 0, const QString& comment = QString(), const QString& stereotype = QString(), bool searchInParentPackageOnly = false, bool remapParent = true); void putAtGlobalScope(bool yesno); void setRelatedClassifier(UMLClassifier *c); void assignUniqueIdOnCreation(bool yesno); - UMLObject* insertAttribute(UMLClassifier *klass, Uml::Visibility::Enum scope, + UMLAttribute* insertAttribute(UMLClassifier *klass, Uml::Visibility::Enum scope, const QString& name, const QString& type, const QString& comment = QString(), bool isStatic = false); - UMLObject* insertAttribute(UMLClassifier *klass, Uml::Visibility::Enum scope, + UMLAttribute *insertAttribute(UMLClassifier *klass, Uml::Visibility::Enum scope, const QString& name, UMLClassifier *attrType, const QString& comment /* =QString() */, bool isStatic /* =false */); UMLOperation* makeOperation(UMLClassifier *parent, const QString &name); void insertMethod(UMLClassifier *klass, UMLOperation* &op, Uml::Visibility::Enum scope, const QString& type, bool isStatic, bool isAbstract, bool isFriend = false, bool isConstructor = false, bool isDestructor = false, const QString& comment = QString()); UMLAttribute* addMethodParameter(UMLOperation *method, const QString& type, const QString& name); void addEnumLiteral(UMLEnum *enumType, const QString &literal, const QString &comment = QString(), const QString &value = QString()); UMLAssociation *createGeneralization(UMLClassifier *child, UMLClassifier *parent); void createGeneralization(UMLClassifier *child, const QString &parentName); UMLEnum *remapUMLEnum(UMLObject *ns, UMLPackage *currentScope); QString formatComment(const QString &comment); QStringList includePathList(); void addIncludePath(const QString& path); bool newUMLObjectWasCreated(); bool isDatatype(const QString& name, UMLPackage *parentPkg = 0); } // end namespace Import_Utils #endif diff --git a/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp b/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp index a9abc5d2a..43fba04fd 100644 --- a/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp +++ b/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp @@ -1,768 +1,771 @@ /*************************************************************************** * Based on kdevelop-3.0 languages/cpp/store_walker.cpp by Roberto Raggi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2004-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "cpptree2uml.h" // app includes #include "uml.h" #include "umldoc.h" #include "umllistview.h" #include "datatype.h" #include "operation.h" #include "debug_utils.h" #include "ast_utils.h" #include "codeimpthread.h" #include "driver.h" #include "import_utils.h" // FIXME: The sole reason for the next 2 includes is parseTypedef(). // Make capsule methods in ClassImport, and remove these includes. #include "classifier.h" // FIXME The next include is motivated by template params #include "template.h" // qt includes #include #include #include #include CppTree2Uml::CppTree2Uml(const QString& fileName, CodeImpThread* thread) : m_thread(thread), m_rootFolder(0), m_doc(UMLApp::app()->document()) { clear(); QDir dir(fileName); m_fileName = dir.canonicalPath(); } CppTree2Uml::~CppTree2Uml() { } void CppTree2Uml::clear() { m_currentScope.clear(); m_currentNamespace[0] = 0; // index 0 is reserved (always 0) m_currentClass[0] = 0; // index 0 is reserved (always 0) m_nsCnt = 0; m_clsCnt = 0; m_currentAccess = Uml::Visibility::Public; m_inSlots = false; m_inSignals = false; m_inStorageSpec = false; m_inTypedef = false; m_currentDeclarator = 0; m_anon = 0; } void CppTree2Uml::setRootPath(const QString &rootPath) { m_rootPath = rootPath; if (Settings::optionState().codeImportState.createArtifacts) { if (!m_rootFolder) { UMLFolder *componentView = m_doc->rootFolder(Uml::ModelType::Component); if (!m_rootPath.isEmpty()) { UMLFolder *root = Import_Utils::createSubDir(m_rootPath, componentView); m_rootFolder = root; } else { m_rootFolder = componentView; } } } } void CppTree2Uml::parseTranslationUnit(const ParsedFile &file) { clear(); if (Settings::optionState().codeImportState.createArtifacts) { QFileInfo fi(file.fileName()); UMLFolder *parent = m_rootFolder; QString path = fi.path().replace(m_rootPath, QLatin1String("")); if (!path.isEmpty()) parent = Import_Utils::createSubDir(path.mid(1), m_rootFolder); Import_Utils::createArtifact(fi.fileName(), parent, file->comment()); } TreeParser::parseTranslationUnit(file); } void CppTree2Uml::parseNamespace(NamespaceAST* ast) { if (m_clsCnt > 0) { uDebug() << "error - cannot nest namespace inside class"; return; } QString nsName; if (!ast->namespaceName() || ast->namespaceName()->text().isEmpty()){ QFileInfo fileInfo(m_fileName); QString shortFileName = fileInfo.baseName(); nsName.sprintf("(%s_%d)", shortFileName.toLocal8Bit().constData(), m_anon++); } else { nsName = ast->namespaceName()->text(); } uDebug() << nsName; if (m_thread) { m_thread->emitMessageToLog(QString(), QLatin1String("namespace ") + nsName); } UMLObject *o = m_doc->findUMLObject(nsName, UMLObject::ot_Package, m_currentNamespace[m_nsCnt]); if (!o) o = m_doc->findUMLObject(nsName, UMLObject::ot_Class, m_currentNamespace[m_nsCnt]); if (o && o->stereotype() == QLatin1String("class-or-package")) { o->setStereotype(QString()); o->setBaseType(UMLObject::ot_Package); } // TODO reduce multiple finds else o = Import_Utils::createUMLObject(UMLObject::ot_Package, nsName, m_currentNamespace[m_nsCnt], ast->comment()); UMLPackage *ns = (UMLPackage *)o; m_currentScope.push_back(nsName); if (++m_nsCnt > STACKSIZE) { uError() << "excessive namespace nesting"; m_nsCnt = STACKSIZE; } m_currentNamespace[m_nsCnt] = ns; TreeParser::parseNamespace(ast); --m_nsCnt; m_currentScope.pop_back(); } void CppTree2Uml::parseTypedef(TypedefAST* ast) { TypeSpecifierAST* typeSpec = ast->typeSpec(); InitDeclaratorListAST* declarators = ast->initDeclaratorList(); if (typeSpec && declarators){ QString typeId; if (typeSpec->name()) typeId = typeSpec->name()->text(); QList l(declarators->initDeclaratorList()); InitDeclaratorAST* initDecl = 0; for (int i = 0; i < l.size(); ++i) { initDecl = l.at(i); if (initDecl==0) break; QString type, id; if (initDecl->declarator()){ type = typeOfDeclaration(typeSpec, initDecl->declarator()); DeclaratorAST* d = initDecl->declarator(); while (d->subDeclarator()){ d = d->subDeclarator(); } if (d->declaratorId()) id = d->declaratorId()->text(); } /* @todo Trace typedefs back to their root type for deciding whether to build a Datatype (for pointers.) */ /* check out if the ID type is a Datatype ex: typedef unsigned int uint; where unsigned int is a known datatype I'm not sure if setIsReference() should be run */ bool isDatatype = Import_Utils::isDatatype(typeId, m_currentNamespace[m_nsCnt]); if (type.contains(QLatin1Char('*')) || isDatatype) { UMLObject *inner = 0; if (m_currentNamespace[m_nsCnt] && m_currentNamespace[m_nsCnt]->baseType() == UMLObject::ot_Class && typeId == m_currentNamespace[m_nsCnt]->name()) inner = m_currentNamespace[m_nsCnt]; else inner = Import_Utils::createUMLObject(UMLObject::ot_Class, type, m_currentNamespace[m_nsCnt]); UMLObject *typedefObj = Import_Utils::createUMLObject(UMLObject::ot_Datatype, id, m_currentNamespace[m_nsCnt]); UMLDatatype *dt = typedefObj->asUMLDatatype(); if (dt) { dt->setIsReference(); dt->setOriginType(inner->asUMLClassifier()); } else { uError() << "Could not create datatype from" << id; } } else { Import_Utils::createUMLObject(UMLObject::ot_Class, id, m_currentNamespace[m_nsCnt], QString() /* doc */, QLatin1String("typedef") /* stereotype */); } } } } void CppTree2Uml::parseTemplateDeclaration(TemplateDeclarationAST* ast) { TemplateParameterListAST* parmListAST = ast->templateParameterList(); if (parmListAST == 0) return; QList parmList = parmListAST->templateParameterList(); for (int i = 0; i < parmList.size(); ++i) { // The template is either a typeParameter or a typeValueParameter. TemplateParameterAST* tmplParmNode = parmList.at(i); TypeParameterAST* typeParmNode = tmplParmNode->typeParameter(); if (typeParmNode) { NameAST* nameNode = typeParmNode->name(); if (nameNode) { QString typeName = nameNode->unqualifiedName()->text(); Model_Utils::NameAndType nt(typeName, 0); m_templateParams.append(nt); } else { uError() << "nameNode is NULL"; } } ParameterDeclarationAST* valueNode = tmplParmNode->typeValueParameter(); if (valueNode) { TypeSpecifierAST* typeSpec = valueNode->typeSpec(); if (typeSpec == 0) { uError() << "typeSpec is NULL"; continue; } QString typeName = typeSpec->name()->text(); UMLObject *t = Import_Utils::createUMLObject(UMLObject::ot_UMLObject, typeName, m_currentNamespace[m_nsCnt]); DeclaratorAST* declNode = valueNode->declarator(); NameAST* nameNode = declNode->declaratorId(); if (nameNode == 0) { uError() << "CppTree2Uml::parseTemplateDeclaration(value):" << " nameNode is NULL"; continue; } QString paramName = nameNode->unqualifiedName()->text(); Model_Utils::NameAndType nt(paramName, t); m_templateParams.append(nt); } } if (ast->declaration()) TreeParser::parseDeclaration(ast->declaration()); } void CppTree2Uml::parseSimpleDeclaration(SimpleDeclarationAST* ast) { TypeSpecifierAST* typeSpec = ast->typeSpec(); InitDeclaratorListAST* declarators = ast->initDeclaratorList(); GroupAST* storageSpec = ast->storageSpecifier(); if (storageSpec && storageSpec->text() == QLatin1String("friend")) return; m_comment = ast->comment(); if (typeSpec) parseTypeSpecifier(typeSpec); if (declarators){ QList l = declarators->initDeclaratorList(); for (int i = 0; i < l.size(); ++i) { parseDeclaration2(ast->functionSpecifier(), ast->storageSpecifier(), typeSpec, l.at(i)); } } } void CppTree2Uml::parseFunctionDefinition(FunctionDefinitionAST* ast) { TypeSpecifierAST* typeSpec = ast->typeSpec(); GroupAST* funSpec = ast->functionSpecifier(); GroupAST* storageSpec = ast->storageSpecifier(); if (!ast->initDeclarator()) return; DeclaratorAST* d = ast->initDeclarator()->declarator(); if (!d->declaratorId()) return; bool isFriend = false; //:unused: bool isVirtual = false; bool isStatic = false; //:unused: bool isInline = false; bool isConstructor = false; bool isDestructor = false; bool isConstExpression = false; if (funSpec){ //:unused: QList l = funSpec->nodeList(); //:unused: for (int i = 0; i < l.size(); ++i) { //:unused: QString text = l.at(i)->text(); //:unused: if (text == "virtual") isVirtual = true; //:unused: else if (text == "inline") isInline = true; //:unused: } } if (storageSpec){ QList l = storageSpec->nodeList(); for (int i = 0; i < l.size(); ++i) { QString text = l.at(i)->text(); if (text == QLatin1String("friend")) isFriend = true; else if (text == QLatin1String("static")) isStatic = true; else if (text == QLatin1String("constexpr")) isConstExpression = true; } } QString id = d->declaratorId()->unqualifiedName()->text().trimmed(); if (m_thread) { m_thread->emitMessageToLog(QString(), QLatin1String("method ") + id); } uDebug() << id; UMLClassifier *c = m_currentClass[m_clsCnt]; if (c == 0) { uDebug() << id << ": need a surrounding class."; return; } QString returnType = typeOfDeclaration(typeSpec, d); UMLOperation *m = Import_Utils::makeOperation(c, id); if (isConstExpression) m->setStereotype(QLatin1String("constexpr")); if (d->override()) m->setOverride(true); if (d->constant()) m->setConst(true); // if a class has no return type, it could be a constructor or // a destructor if (d && returnType.isEmpty()) { if (id.indexOf(QLatin1Char('~')) == -1) isConstructor = true; else isDestructor = true; } parseFunctionArguments(d, m); Import_Utils::insertMethod(c, m, m_currentAccess, returnType, isStatic, false /*isAbstract*/, isFriend, isConstructor, isDestructor, m_comment); m_comment = QString(); /* For reference, Kdevelop does some more: method->setFileName(m_fileName); if (m_inSignals) method->setSignal(true); if (m_inSlots) method->setSlot(true); */ } void CppTree2Uml::parseClassSpecifier(ClassSpecifierAST* ast) { Uml::Visibility::Enum oldAccess = m_currentAccess; bool oldInSlots = m_inSlots; bool oldInSignals = m_inSignals; QString kind = ast->classKey()->text(); m_currentAccess = Uml::Visibility::fromString(kind); m_inSlots = false; m_inSignals = false; QString className; if (!ast->name() && m_currentDeclarator && m_currentDeclarator->declaratorId()) { className = m_currentDeclarator->declaratorId()->text().trimmed(); } else if (!ast->name()){ QFileInfo fileInfo(m_fileName); QString shortFileName = fileInfo.baseName(); className.sprintf("(%s_%d)", shortFileName.toLocal8Bit().constData(), m_anon++); } else { className = ast->name()->unqualifiedName()->text().trimmed(); } uDebug() << "name=" << className; if (m_thread) { m_thread->emitMessageToLog(QString(), QLatin1String("class ") + className); } QStringList scope = scopeOfName(ast->name(), QStringList()); UMLObject *localParent = 0; if (!scope.isEmpty()) { localParent = m_doc->findUMLObject(scope.join(QLatin1String("::")), UMLObject::ot_Class, m_currentNamespace[m_nsCnt]); if (!localParent) localParent = m_doc->findUMLObject(scope.join(QLatin1String("::")), UMLObject::ot_Package, m_currentNamespace[m_nsCnt]); if (!localParent) { localParent = Import_Utils::createUMLObject(UMLObject::ot_Class, className, m_currentNamespace[m_nsCnt], ast->comment(), QString(), true); localParent->setStereotype(QLatin1String("class-or-package")); } m_currentNamespace[++m_nsCnt] = localParent->asUMLPackage(); } if (className.isEmpty()) { className = QLatin1String("anon_") + QString::number(m_anon); m_anon++; } UMLObject *o = m_doc->findUMLObject(className, UMLObject::ot_Class, m_currentNamespace[m_nsCnt]); if (!o) o = m_doc->findUMLObject(className, UMLObject::ot_Datatype, m_currentNamespace[m_nsCnt]); if (o && o->stereotype() == QLatin1String("class-or-package")) { o->setStereotype(QString()); o->setBaseType(UMLObject::ot_Class); } // TODO reduce multiple finds else o = Import_Utils::createUMLObject(UMLObject::ot_Class, className, m_currentNamespace[m_nsCnt], ast->comment(), QString(), true); UMLClassifier *klass = o->asUMLClassifier(); flushTemplateParams(klass); if (ast->baseClause()) parseBaseClause(ast->baseClause(), klass); m_currentScope.push_back(className); if (++m_clsCnt > STACKSIZE) { uError() << "excessive class nesting"; m_clsCnt = STACKSIZE; } m_currentClass[m_clsCnt] = klass; if (++m_nsCnt > STACKSIZE) { uError() << "excessive namespace nesting"; m_nsCnt = STACKSIZE; } m_currentNamespace[m_nsCnt] = (UMLPackage*)klass; TreeParser::parseClassSpecifier(ast); --m_nsCnt; --m_clsCnt; m_currentScope.pop_back(); // check is class is an interface bool isInterface = true; foreach(UMLOperation *op, klass->getOpList()) { if (!op->isDestructorOperation() && op->isAbstract() == false) isInterface = false; } foreach(UMLAttribute *attr, klass->getAttributeList()) { if (!(attr->isStatic() && attr->getTypeName().contains(QLatin1String("const")))) isInterface = false; } if (isInterface) klass->setBaseType(UMLObject::ot_Interface); m_currentAccess = oldAccess; m_inSlots = oldInSlots; m_inSignals = oldInSignals; if (localParent) m_currentNamespace[m_nsCnt--] = 0; } void CppTree2Uml::parseEnumSpecifier(EnumSpecifierAST* ast) { NameAST *nameNode = ast->name(); if (nameNode == 0) return; // skip constants QString typeName = nameNode->unqualifiedName()->text().trimmed(); if (typeName.isEmpty()) return; // skip constants UMLObject *o = Import_Utils::createUMLObject(UMLObject::ot_Enum, typeName, m_currentNamespace[m_nsCnt], ast->comment()); QList l = ast->enumeratorList(); for (int i = 0; i < l.size(); ++i) { QString enumLiteral = l.at(i)->id()->text(); QString enumLiteralValue = QString(); if (l.at(i)->expr()) { enumLiteralValue = l.at(i)->expr()->text(); } Import_Utils::addEnumLiteral((UMLEnum*)o, enumLiteral, QString(), enumLiteralValue); } } void CppTree2Uml::parseElaboratedTypeSpecifier(ElaboratedTypeSpecifierAST* typeSpec) { // This is invoked for forward declarations. /// @todo Refine - Currently only handles class forward declarations. /// - Using typeSpec->text() is probably not good, decode /// the kind() instead. QString text = typeSpec->text(); uDebug() << "forward declaration of " << text; if (m_thread) { m_thread->emitMessageToLog(QString(), QLatin1String("forward declaration of ") + text); } text.remove(QRegExp(QLatin1String("^class\\s+"))); #if 0 if (m_thread) { //:TODO: for testing only int answer; m_thread->emitAskQuestion("Soll CppTree2Uml::parseElaboratedTypeSpecifier ausgeführt werden?"); uDebug() << "Antwort: " << answer; } #endif UMLObject *o = Import_Utils::createUMLObject(UMLObject::ot_Class, text, m_currentNamespace[m_nsCnt]); #if 0 if (m_thread) { //:TODO: for testing only m_thread->emitAskQuestion("Soll nach CppTree2Uml::parseElaboratedTypeSpecifier weiter gemacht werden?"); } #endif flushTemplateParams(o->asUMLClassifier()); } void CppTree2Uml::parseDeclaration2(GroupAST* funSpec, GroupAST* storageSpec, TypeSpecifierAST* typeSpec, InitDeclaratorAST* decl) { if (m_inStorageSpec) return; DeclaratorAST* d = decl->declarator(); if (!d) return; if (!d->subDeclarator() && d->parameterDeclarationClause()) return parseFunctionDeclaration(funSpec, storageSpec, typeSpec, decl); DeclaratorAST* t = d; while (t && t->subDeclarator()) t = t->subDeclarator(); QString id; if (t && t->declaratorId() && t->declaratorId()->unqualifiedName()) id = t->declaratorId()->unqualifiedName()->text(); if (!scopeOfDeclarator(d, QStringList()).isEmpty()){ uDebug() << id << ": skipping."; return; } UMLClassifier *c = m_currentClass[m_clsCnt]; if (c == 0) { uDebug() << id << ": need a surrounding class."; return; } QString typeName = typeOfDeclaration(typeSpec, d); -//:unused: bool isFriend = false; + bool isFriend = false; bool isStatic = false; //:unused: bool isInitialized = decl->initializer() != 0; if (storageSpec){ QList l = storageSpec->nodeList(); for (int i = 0; i < l.size(); ++i) { QString text = l.at(i)->text(); - if (text == QLatin1String("static")) isStatic = true; -//:unused: else if (text == QLatin1String("friend")) isFriend = true; + if (text == QLatin1String("static")) + isStatic = true; + else if (text == QLatin1String("friend")) + isFriend = true; } } - Import_Utils::insertAttribute(c, m_currentAccess, id, typeName, - m_comment, isStatic); + UMLAttribute *attribute = Import_Utils::insertAttribute(c, m_currentAccess, id, typeName, m_comment, isStatic); + if (isFriend) + attribute->setStereotype(QLatin1String("friend")); m_comment = QString(); } void CppTree2Uml::parseAccessDeclaration(AccessDeclarationAST * access) { QList l = access->accessList(); QString accessStr = l.at(0)->text(); m_currentAccess=Uml::Visibility::fromString(accessStr); m_inSlots = l.count() > 1 ? l.at(1)->text() == QLatin1String("slots") : false; m_inSignals = l.count() >= 1 ? l.at(0)->text() == QLatin1String("signals") : false; } void CppTree2Uml::parseFunctionDeclaration(GroupAST* funSpec, GroupAST* storageSpec, TypeSpecifierAST * typeSpec, InitDeclaratorAST * decl) { bool isFriend = false; //:unused: bool isVirtual = false; bool isStatic = false; //:unused: bool isInline = false; bool isPure = decl->initializer() != 0; bool isConstructor = false; bool isConstExpression = false; bool isDestructor = false; if (funSpec){ //:unused: QList l = funSpec->nodeList(); //:unused: for (int i = 0; i < l.size(); ++i) { //:unused: QString text = l.at(i)->text(); //:unused: if (text == QLatin1String("virtual")) isVirtual = true; //:unused: else if (text == QLatin1String("inline")) isInline = true; //:unused: } } if (storageSpec){ QList l = storageSpec->nodeList(); for (int i = 0; i < l.size(); ++i) { QString text = l.at(i)->text(); if (text == QLatin1String("friend")) isFriend = true; else if (text == QLatin1String("static")) isStatic = true; else if (text == QLatin1String("constexpr")) isConstExpression = true; } } DeclaratorAST* d = decl->declarator(); QString id = d->declaratorId()->unqualifiedName()->text(); UMLClassifier *c = m_currentClass[m_clsCnt]; if (c == 0) { uDebug() << id << ": need a surrounding class."; return; } QString returnType = typeOfDeclaration(typeSpec, d); UMLOperation *m = Import_Utils::makeOperation(c, id); if (d->override()) m->setOverride(true); if (d->constant()) m->setConst(true); if (isConstExpression) m->setStereotype(QLatin1String("constexpr")); // if a class has no return type, it could de a constructor or // a destructor if (d && returnType.isEmpty()) { if (id.indexOf(QLatin1Char('~')) == -1) isConstructor = true; else isDestructor = true; } parseFunctionArguments(d, m); Import_Utils::insertMethod(c, m, m_currentAccess, returnType, isStatic, isPure, isFriend, isConstructor, isDestructor, m_comment); if (isPure) c->setAbstract(true); m_comment = QString(); } void CppTree2Uml::parseFunctionArguments(DeclaratorAST* declarator, UMLOperation* method) { if (!declarator) return; ParameterDeclarationClauseAST* clause = declarator->parameterDeclarationClause(); if (clause && clause->parameterDeclarationList()){ ParameterDeclarationListAST* params = clause->parameterDeclarationList(); QList l(params->parameterList()); for (int i = 0; i < l.size(); ++i) { ParameterDeclarationAST* param = l.at(i); QString name; if (param->declarator()) name = declaratorToString(param->declarator(), QString(), true); QString tp = typeOfDeclaration(param->typeSpec(), param->declarator()); if (tp != QLatin1String("void")) Import_Utils::addMethodParameter(method, tp, name); } } } QString CppTree2Uml::typeOfDeclaration(TypeSpecifierAST* typeSpec, DeclaratorAST* declarator) { if (!typeSpec || !declarator) return QString(); QString text; text += typeSpec->text(); QList ptrOpList = declarator->ptrOpList(); for (int i = 0; i < ptrOpList.size(); ++i) { text += ptrOpList.at(i)->text(); } QList arrays = declarator->arrayDimensionList(); for(int i = 0; i < arrays.size(); ++i) { text += arrays.at(i)->text().replace(QLatin1String(" "), QLatin1String("")); } return text; } void CppTree2Uml::parseBaseClause(BaseClauseAST * baseClause, UMLClassifier* klass) { QList l = baseClause->baseSpecifierList(); for (int i = 0; i < l.size(); ++i) { BaseSpecifierAST* baseSpecifier = l.at(i); if (baseSpecifier->name() == 0) { uDebug() << "baseSpecifier->name() is NULL"; continue; } QString baseName = baseSpecifier->name()->text(); // uDebug() << "CppTree2Uml::parseBaseClause : baseSpecifier is " << baseName; Import_Utils::putAtGlobalScope(true); UMLObject *c = Import_Utils::createUMLObject(UMLObject::ot_Class, baseName, m_currentNamespace[m_nsCnt], baseSpecifier->comment()); Import_Utils::putAtGlobalScope(false); Import_Utils::createGeneralization(klass, c->asUMLClassifier()); } } QStringList CppTree2Uml::scopeOfName(NameAST* id, const QStringList& startScope) { QStringList scope = startScope; if (id && id->classOrNamespaceNameList().count()){ if (id->isGlobal()) scope.clear(); QList l = id->classOrNamespaceNameList(); for (int i = 0; i < l.size(); ++i) { if (l.at(i)->name()){ scope << l.at(i)->name()->text(); } } } return scope; } QStringList CppTree2Uml::scopeOfDeclarator(DeclaratorAST* d, const QStringList& startScope) { return scopeOfName(d->declaratorId(), startScope); } /** * Flush template parameters pending in m_templateParams to the klass. */ void CppTree2Uml::flushTemplateParams(UMLClassifier *klass) { if (m_templateParams.count()) { Model_Utils::NameAndType_ListIt it; for (it = m_templateParams.begin(); it != m_templateParams.end(); ++it) { const Model_Utils::NameAndType &nt = *it; uDebug() << "adding template param: " << nt.m_name; UMLTemplate *tmpl = klass->addTemplate(nt.m_name); tmpl->setType(nt.m_type); } m_templateParams.clear(); } }