diff --git a/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp b/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp index a9abc5d2a..86cb0f878 100644 --- a/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp +++ b/umbrello/codeimport/kdevcppparser/cpptree2uml.cpp @@ -1,768 +1,773 @@ /*************************************************************************** * 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 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; + QList l = funSpec->nodeList(); + for (int i = 0; i < l.size(); ++i) { + QString text = l.at(i)->text(); + if (text == QLatin1String("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 (isVirtual) + m->setVirtual(true); 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 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; } } Import_Utils::insertAttribute(c, m_currentAccess, id, typeName, m_comment, isStatic); 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 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; + QList l = funSpec->nodeList(); + for (int i = 0; i < l.size(); ++i) { + QString text = l.at(i)->text(); + 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 (isVirtual) + m->setVirtual(true); // 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(); } } diff --git a/umbrello/dialogs/umloperationdialog.cpp b/umbrello/dialogs/umloperationdialog.cpp index 55010baca..67f33a69b 100644 --- a/umbrello/dialogs/umloperationdialog.cpp +++ b/umbrello/dialogs/umloperationdialog.cpp @@ -1,447 +1,451 @@ /*************************************************************************** * 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) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "umloperationdialog.h" //app includes #include "debug_utils.h" #include "uml.h" #include "umldoc.h" #include "operation.h" #include "classifier.h" #include "template.h" #include "listpopupmenu.h" #include "umlattributelist.h" #include "umldatatypewidget.h" #include "umlstereotypewidget.h" #include "classifierlistitem.h" #include "documentationwidget.h" #include "umlclassifierlistitemlist.h" #include "dialog_utils.h" #include "parameterpropertiesdialog.h" #include "stereotype.h" #include "uniqueid.h" #include "visibilityenumwidget.h" //kde includes #if QT_VERSION < 0x050000 #include #endif #include #include #include #include //qt includes #include #include #include #include #include #include #include #include #include #include #include #include /** * Constructor. */ UMLOperationDialog::UMLOperationDialog(QWidget * parent, UMLOperation * pOperation) : SinglePageDialogBase(parent), m_pOverrideCB(0) { setCaption(i18n("Operation Properties")); m_operation = pOperation; m_doc = UMLApp::app()->document(); m_menu = 0; setupDialog(); } /** * Destructor. */ UMLOperationDialog::~UMLOperationDialog() { } /** * Sets up the dialog. */ void UMLOperationDialog::setupDialog() { QFrame *frame = new QFrame(this); setMainWidget(frame); int margin = fontMetrics().height(); QVBoxLayout * topLayout = new QVBoxLayout(frame); m_pGenGB = new QGroupBox(i18n("General Properties"), frame); QGridLayout * genLayout = new QGridLayout(m_pGenGB); genLayout->setColumnStretch(1, 1); genLayout->setColumnStretch(3, 1); genLayout->addItem(new QSpacerItem(200, 0), 0, 1); genLayout->addItem(new QSpacerItem(200, 0), 0, 3); genLayout->setMargin(margin); genLayout->setSpacing(10); Dialog_Utils::makeLabeledEditField(genLayout, 0, m_pNameL, i18nc("operation name", "&Name:"), m_pNameLE, m_operation->name()); m_datatypeWidget = new UMLDatatypeWidget(m_operation); m_datatypeWidget->addToLayout(genLayout, 0, 2); m_stereotypeWidget = new UMLStereotypeWidget(m_operation); m_stereotypeWidget->addToLayout(genLayout, 1); m_pAbstractCB = new QCheckBox(i18n("&Abstract operation"), m_pGenGB); m_pAbstractCB->setChecked(m_operation->isAbstract()); genLayout->addWidget(m_pAbstractCB, 2, 0); m_pStaticCB = new QCheckBox(i18n("Classifier &scope (\"static\")"), m_pGenGB); m_pStaticCB->setChecked(m_operation->isStatic()); genLayout->addWidget(m_pStaticCB, 2, 1); m_pQueryCB = new QCheckBox(i18n("&Query (\"const\")"), m_pGenGB); m_pQueryCB->setChecked(m_operation->getConst()); genLayout->addWidget(m_pQueryCB, 2, 2); + m_virtualCB = new QCheckBox(i18n("&virtual"), m_pGenGB); + m_virtualCB->setChecked(m_operation->isVirtual()); + genLayout->addWidget(m_virtualCB, 2, 3); if (Settings::optionState().codeImportState.supportCPP11) { m_pOverrideCB = new QCheckBox(i18n("&Override"), m_pGenGB); m_pOverrideCB->setChecked(m_operation->getOverride()); - genLayout->addWidget(m_pOverrideCB, 2, 3); + genLayout->addWidget(m_pOverrideCB, 2, 4); } m_visibilityEnumWidget = new VisibilityEnumWidget(m_operation, this); m_docWidget = new DocumentationWidget(m_operation, this); m_pParmsGB = new QGroupBox(i18n("Parameters"), frame); QVBoxLayout* parmsLayout = new QVBoxLayout(m_pParmsGB); parmsLayout->setMargin(margin); parmsLayout->setSpacing(10); // horizontal box contains the list box and the move up/down buttons QHBoxLayout* parmsHBoxLayout = new QHBoxLayout(); m_pParmsLW = new QListWidget(m_pParmsGB); m_pParmsLW->setContextMenuPolicy(Qt::CustomContextMenu); // the move up/down buttons (another vertical box) QVBoxLayout* buttonLayout = new QVBoxLayout(); m_pUpButton = new QToolButton(m_pParmsGB); m_pUpButton->setArrowType(Qt::UpArrow); m_pUpButton->setEnabled(false); buttonLayout->addWidget(m_pUpButton); m_pDownButton = new QToolButton(m_pParmsGB); m_pDownButton->setArrowType(Qt::DownArrow); m_pDownButton->setEnabled(false); buttonLayout->addWidget(m_pDownButton); #if QT_VERSION >= 0x050000 QDialogButtonBox* buttonBox = new QDialogButtonBox(m_pParmsGB); QPushButton* newParam = buttonBox->addButton(i18n("Ne&w Parameter..."), QDialogButtonBox::ActionRole); connect(newParam, SIGNAL(clicked()), this, SLOT(slotNewParameter())); m_pDeleteButton = buttonBox->addButton(i18n("&Delete"), QDialogButtonBox::ActionRole); connect(m_pDeleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteParameter())); m_pPropertiesButton = buttonBox->addButton(i18n("&Properties"), QDialogButtonBox::ActionRole); connect(m_pPropertiesButton, SIGNAL(clicked()), this, SLOT(slotParameterProperties())); #else KDialogButtonBox* buttonBox = new KDialogButtonBox(m_pParmsGB); buttonBox->addButton(i18n("Ne&w Parameter..."), KDialogButtonBox::ActionRole, this, SLOT(slotNewParameter())); m_pDeleteButton = buttonBox->addButton(i18n("&Delete"), KDialogButtonBox::ActionRole, this, SLOT(slotDeleteParameter())); m_pPropertiesButton = buttonBox->addButton(i18n("&Properties"), KDialogButtonBox::ActionRole, this, SLOT(slotParameterProperties())); #endif parmsHBoxLayout->addWidget(m_pParmsLW); parmsHBoxLayout->addLayout(buttonLayout); parmsLayout->addLayout(parmsHBoxLayout); parmsLayout->addWidget(buttonBox); topLayout->addWidget(m_pGenGB); topLayout->addWidget(m_visibilityEnumWidget); topLayout->addWidget(m_docWidget); topLayout->addWidget(m_pParmsGB); m_pDeleteButton->setEnabled(false); m_pPropertiesButton->setEnabled(false); m_pUpButton->setEnabled(false); m_pDownButton->setEnabled(false); // fill in parm list box UMLAttributeList list = m_operation->getParmList(); foreach (UMLAttribute* pAtt, list) { m_pParmsLW->addItem(pAtt->toString(Uml::SignatureType::SigNoVis)); } // setup parm list box signals connect(m_pUpButton, SIGNAL(clicked()), this, SLOT(slotParameterUp())); connect(m_pDownButton, SIGNAL(clicked()), this, SLOT(slotParameterDown())); connect(m_pParmsLW, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotParamsBoxClicked(QListWidgetItem*))); connect(m_pParmsLW, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotParmRightButtonPressed(QPoint))); connect(m_pParmsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotParmDoubleClick(QListWidgetItem*))); m_pNameLE->setFocus(); connect(m_pNameLE, SIGNAL(textChanged(QString)), SLOT(slotNameChanged(QString))); slotNameChanged(m_pNameLE->text()); } void UMLOperationDialog::slotNameChanged(const QString &_text) { enableButtonOk(!_text.isEmpty()); } void UMLOperationDialog::slotParmRightButtonPressed(const QPoint &p) { ListPopupMenu::MenuType type = ListPopupMenu::mt_Undefined; QListWidgetItem* item = m_pParmsLW->itemAt(p); if (item) // pressed on an item { type = ListPopupMenu::mt_Parameter_Selected; } else // pressed into fresh air { type = ListPopupMenu::mt_New_Parameter; } if (m_menu) { m_menu->hide(); disconnect(m_menu, SIGNAL(triggered(QAction*)), this, SLOT(slotMenuSelection(QAction*))); delete m_menu; m_menu = 0; } ListPopupMenu popup(this, type); QAction *triggered = popup.exec(m_pParmsLW->mapToGlobal(p)); slotMenuSelection(triggered); } void UMLOperationDialog::slotParmDoubleClick(QListWidgetItem *item) { if (!item) { return; } // this happens, when there was no right click in the list widget ListPopupMenu popup(this, ListPopupMenu::mt_Parameter_Selected); QAction* action = popup.getAction(ListPopupMenu::mt_Properties); slotMenuSelection(action); } void UMLOperationDialog::slotMenuSelection(QAction* action) { ListPopupMenu::MenuType id = ListPopupMenu::typeFromAction(action); if(id == ListPopupMenu::mt_Rename || id == ListPopupMenu::mt_Properties) { slotParameterProperties(); } else if(id == ListPopupMenu::mt_New_Parameter) { slotNewParameter(); } else if(id == ListPopupMenu::mt_Delete) { slotDeleteParameter(); } } void UMLOperationDialog::slotNewParameter() { UMLAttribute* pAtt = 0; QString currentName = m_operation->getUniqueParameterName(); UMLAttribute* newAttribute = new UMLAttribute(m_operation, currentName, Uml::ID::Reserved); QPointer dlg = new ParameterPropertiesDialog(this, m_doc, newAttribute); if (dlg->exec()) { pAtt = m_operation->findParm(newAttribute->name()); if (!pAtt) { newAttribute->setID(UniqueID::gen()); m_operation->addParm(newAttribute); m_pParmsLW->addItem(newAttribute->toString(Uml::SignatureType::SigNoVis)); m_doc->setModified(true); } else { KMessageBox::sorry(this, i18n("The parameter name you have chosen\nis already being used in this operation."), i18n("Parameter Name Not Unique"), 0); delete newAttribute; } } else { delete newAttribute; } delete dlg; } void UMLOperationDialog::slotDeleteParameter() { UMLAttribute* pOldAtt = m_operation->getParmList().at(m_pParmsLW->row(m_pParmsLW->currentItem())); m_operation->removeParm(pOldAtt); m_pParmsLW->takeItem(m_pParmsLW->currentRow()); m_doc->setModified(true); m_pDeleteButton->setEnabled(false); m_pPropertiesButton->setEnabled(false); m_pUpButton->setEnabled(false); m_pDownButton->setEnabled(false); } void UMLOperationDialog::slotParameterProperties() { UMLAttribute* pAtt = 0, * pOldAtt = 0; int position = m_pParmsLW->row(m_pParmsLW->currentItem()); pOldAtt = m_operation->getParmList().at(position); if (!pOldAtt) { uDebug() << "THE impossible has occurred for:" << m_pParmsLW->currentItem()->text(); return; } // should never occur QString oldAttName = pOldAtt->name(); UMLAttribute* tempAttribute = pOldAtt->clone()->asUMLAttribute(); // create a clone of the parameter // send the clone to the properties dialog box. it will fill in the new parameters. QPointer dlg = new ParameterPropertiesDialog(this, m_doc, tempAttribute); if (dlg->exec()) { bool namingConflict = false; QString newName = tempAttribute->name(); pAtt = m_operation->findParm(newName); // search whether a parameter with this name already exists if(pAtt && pAtt != pOldAtt) { KMessageBox::error(this, i18n("The parameter name you have chosen is already being used in this operation."), i18n("Parameter Name Not Unique"), 0); namingConflict = true; } tempAttribute->copyInto(pOldAtt); // copy all attributes from the clone if (namingConflict) { pOldAtt->setName(oldAttName); // reset the name if there was a naming conflict } QListWidgetItem* item = m_pParmsLW->currentItem(); item->setText(pOldAtt->toString(Uml::SignatureType::SigNoVis)); m_doc->setModified(true); } delete tempAttribute; delete dlg; } void UMLOperationDialog::slotParameterUp() { int row = m_pParmsLW->currentRow(); QListWidgetItem* item = m_pParmsLW->currentItem(); if (item) { UMLAttribute* pOldAtt = m_operation->getParmList().at(m_pParmsLW->row(item)); m_operation->moveParmLeft(pOldAtt); m_pParmsLW->takeItem(row); m_pParmsLW->insertItem(row - 1, item); m_doc->setModified(true); slotParamsBoxClicked(item); } else { uDebug() << "No current item in list widget!?"; } } void UMLOperationDialog::slotParameterDown() { int row = m_pParmsLW->currentRow(); QListWidgetItem* item = m_pParmsLW->currentItem(); if (item) { UMLAttribute* pOldAtt = m_operation->getParmList().at(m_pParmsLW->row(item)); m_operation->moveParmRight(pOldAtt); m_pParmsLW->takeItem(row); m_pParmsLW->insertItem(row + 1, item); m_doc->setModified(true); slotParamsBoxClicked(item); } else { uDebug() << "No current item in list widget!?"; } } /** * Enables or disables buttons. */ void UMLOperationDialog::slotParamsBoxClicked(QListWidgetItem* parameterItem) { if (parameterItem) { m_pDeleteButton->setEnabled(true); m_pPropertiesButton->setEnabled(true); int row = m_pParmsLW->row(parameterItem); bool hasNext = (row < m_pParmsLW->count() - 1); bool hasPrev = (row > 0); m_pUpButton->setEnabled(hasPrev); m_pDownButton->setEnabled(hasNext); } else { m_pDeleteButton->setEnabled(false); m_pPropertiesButton->setEnabled(false); m_pUpButton->setEnabled(false); m_pDownButton->setEnabled(false); } } /** * Checks if changes are valid and applies them if they are, * else returns false. */ bool UMLOperationDialog::apply() { QString name = m_pNameLE->text(); if(name.length() == 0) { KMessageBox::error(this, i18n("You have entered an invalid operation name."), i18n("Operation Name Invalid"), 0); m_pNameLE->setText(m_operation->name()); return false; } UMLClassifier *classifier = m_operation->umlParent()->asUMLClassifier(); if(classifier != 0 && classifier->checkOperationSignature(name, m_operation->getParmList(), m_operation)) { QString msg = i18n("An operation with that signature already exists in %1.\n", classifier->name()) + i18n("Choose a different name or parameter list."); KMessageBox::error(this, msg, i18n("Operation Name Invalid"), 0); return false; } m_operation->setName(name); m_visibilityEnumWidget->apply(); m_datatypeWidget->apply(); m_stereotypeWidget->apply(); bool isAbstract = m_pAbstractCB->isChecked(); m_operation->setAbstract(isAbstract); if (isAbstract) { /* If any operation is abstract then the owning class needs to be made abstract. The inverse is not true: The fact that no operation is abstract does not mean that the class must be non-abstract. */ if (classifier) { classifier->setAbstract(true); } } m_operation->setStatic(m_pStaticCB->isChecked()); m_operation->setConst(m_pQueryCB->isChecked()); + m_operation->setVirtual(m_virtualCB->isChecked()); if (m_pOverrideCB) m_operation->setOverride(m_pOverrideCB->isChecked()); m_docWidget->apply(); return true; } diff --git a/umbrello/dialogs/umloperationdialog.h b/umbrello/dialogs/umloperationdialog.h index e44eae0d3..0288e8ec3 100644 --- a/umbrello/dialogs/umloperationdialog.h +++ b/umbrello/dialogs/umloperationdialog.h @@ -1,97 +1,98 @@ /*************************************************************************** * 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) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef UMLOPERATIONDIALOG_H #define UMLOPERATIONDIALOG_H //kde includes #include "singlepagedialogbase.h" class DocumentationWidget; class KComboBox; class ListPopupMenu; class QAbstractButton; class QGroupBox; class QListWidget; class QListWidgetItem; class QLabel; class QRadioButton; class QPushButton; class QCheckBox; class QToolButton; class KLineEdit; class UMLDoc; class UMLOperation; class UMLDatatypeWidget; class UMLStereotypeWidget; class VisibilityEnumWidget; /** * @author Paul Hensgen * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ class UMLOperationDialog : public SinglePageDialogBase { Q_OBJECT public: UMLOperationDialog(QWidget * parent, UMLOperation * pOperation); ~UMLOperationDialog(); protected: void setupDialog(); bool apply(); void insertTypesSorted(const QString& type = QString()); UMLOperation* m_operation; ///< The operation to represent. UMLDoc* m_doc; ///< The UMLDocument where all objects live. ListPopupMenu* m_menu; ///< Menu used in parameter list box. //GUI widgets QGroupBox* m_pParmsGB; QGroupBox* m_pGenGB; QListWidget* m_pParmsLW; QGroupBox* m_pScopeGB; QRadioButton* m_pPublicRB; QRadioButton* m_pPrivateRB; QRadioButton* m_pProtectedRB; QRadioButton* m_pImplementationRB; QLabel* m_pNameL; UMLDatatypeWidget* m_datatypeWidget; UMLStereotypeWidget* m_stereotypeWidget; KLineEdit* m_pNameLE; QCheckBox* m_pAbstractCB; QCheckBox* m_pStaticCB; QCheckBox* m_pQueryCB; + QCheckBox* m_virtualCB; QCheckBox* m_pOverrideCB; QPushButton* m_pDeleteButton; QPushButton* m_pPropertiesButton; QToolButton* m_pUpButton; QToolButton* m_pDownButton; DocumentationWidget* m_docWidget; VisibilityEnumWidget* m_visibilityEnumWidget; public slots: void slotParmRightButtonPressed(const QPoint &p); void slotParmDoubleClick(QListWidgetItem *item); void slotMenuSelection(QAction* action); void slotNewParameter(); void slotDeleteParameter(); void slotParameterProperties(); void slotParameterUp(); void slotParameterDown(); void slotParamsBoxClicked(QListWidgetItem* parameterItem); void slotNameChanged(const QString &); }; #endif diff --git a/umbrello/uml1model/operation.cpp b/umbrello/uml1model/operation.cpp index baec24fd7..79e6f4b8e 100644 --- a/umbrello/uml1model/operation.cpp +++ b/umbrello/uml1model/operation.cpp @@ -1,645 +1,664 @@ /*************************************************************************** * 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) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "operation.h" // app includes #include "attribute.h" #include "classifier.h" #include "model_utils.h" #include "debug_utils.h" #include "uml.h" #include "umldoc.h" #include "uniqueid.h" #include "umloperationdialog.h" #include "codegenerator.h" #include "codedocument.h" #include "codeblock.h" // kde includes #include // qt includes #include /** * Constructs an UMLOperation. * Not intended for general use: The operation is not tied in with * umbrello's Qt signalling for object creation. * If you want to create an Operation use the method in UMLDoc instead. * * @param parent the parent to this operation * @param name the name of the operation * @param id the id of the operation * @param s the visibility of the operation * @param rt the return type of the operation */ UMLOperation::UMLOperation(UMLClassifier *parent, const QString& name, Uml::ID::Type id, Uml::Visibility::Enum s, UMLObject *rt) : UMLClassifierListItem(parent, name, id) { if (rt) m_returnId = UniqueID::gen(); else m_returnId = Uml::ID::None; m_pSecondary = rt; m_visibility = s; m_BaseType = UMLObject::ot_Operation; m_bConst = false; m_Override = false; m_Code.clear(); } /** * Constructs an UMLOperation. * Not intended for general use: The operation is not tied in with * umbrello's Qt signalling for object creation. * If you want to create an Operation use the method in UMLDoc instead. * * @param parent the parent to this operation */ UMLOperation::UMLOperation(UMLClassifier * parent) : UMLClassifierListItem (parent) { m_BaseType = UMLObject::ot_Operation; m_bConst = false; m_Override = false; m_Code.clear(); } /** * Destructor. */ UMLOperation::~UMLOperation() { } /** * Reimplement method from UMLClassifierListItem. * * @param type pointer to the type object */ void UMLOperation::setType(UMLObject* type) { UMLClassifierListItem::setType(type); if (m_returnId == Uml::ID::None) m_returnId = UniqueID::gen(); } /** * Move a parameter one position to the left. * * @param a the parameter to move */ void UMLOperation::moveParmLeft(UMLAttribute * a) { if (a == 0) { uDebug() << "called on NULL attribute"; return; } uDebug() << "called for " << a->name(); disconnect(a, SIGNAL(modified()), this, SIGNAL(modified())); int idx; if ((idx=m_List.indexOf(a)) == -1) { uDebug() << "Error move parm left " << a->name(); return; } if (idx == 0) return; m_List.removeAll(a); m_List.insert(idx-1, a); } /** * Move a parameter one position to the right. * * @param a the parameter to move */ void UMLOperation::moveParmRight(UMLAttribute * a) { if (a == 0) { uDebug() << "called on NULL attribute"; return; } uDebug() << "called for " << a->name(); disconnect(a, SIGNAL(modified()), this, SIGNAL(modified())); int idx; if ((idx=m_List.indexOf(a)) == -1) { uDebug() << "Error move parm right " << a->name(); return; } int count = m_List.count(); if (idx == count-1) return; m_List.removeAll(a); m_List.insert(idx+1, a); } /** * Remove a parameter from the operation. * * @param a the parameter to remove * @param emitModifiedSignal whether to emit the "modified" signal * which creates an entry in the Undo stack for the * removal, default: true */ void UMLOperation::removeParm(UMLAttribute * a, bool emitModifiedSignal /* =true */) { if (a == 0) { uDebug() << "called on NULL attribute"; return; } uDebug() << "called for " << a->name(); disconnect(a, SIGNAL(modified()), this, SIGNAL(modified())); if(!m_List.removeAll(a)) uDebug() << "Error removing parm " << a->name(); if (emitModifiedSignal) emit modified(); } /** * Returns a list of parameters. * * @return a list of the parameters in the operation */ UMLAttributeList UMLOperation::getParmList() const { return m_List; } /** * Finds a parameter of the operation. * * @param name the parameter name to search for * @return the found parameter, 0 if not found */ UMLAttribute* UMLOperation::findParm(const QString &name) { UMLAttribute * obj=0; foreach (obj, m_List) { if (obj->name() == name) return obj; } return 0; } /** * Returns a string representation of the operation. * * @param sig what type of operation string to show * @return the string representation of the operation */ QString UMLOperation::toString(Uml::SignatureType::Enum sig, bool withStereotype) { QString s; if (sig == Uml::SignatureType::ShowSig || sig == Uml::SignatureType::NoSig) s = Uml::Visibility::toString(m_visibility, true) + QLatin1Char(' '); s += name(); Uml::ProgrammingLanguage::Enum pl = UMLApp::app()->activeLanguage(); bool parameterlessOpNeedsParentheses = (pl != Uml::ProgrammingLanguage::Pascal && pl != Uml::ProgrammingLanguage::Ada); if (sig == Uml::SignatureType::NoSig || sig == Uml::SignatureType::NoSigNoVis) { if (parameterlessOpNeedsParentheses) s.append(QLatin1String("()")); if (withStereotype) { QString st = stereotype(true); if (!st.isEmpty()) s += QLatin1Char(' ') + st; } return s; } int last = m_List.count(); if (last) { s.append(QLatin1String("(")); int i = 0; foreach (UMLAttribute *param, m_List) { i++; s.append(param->toString(Uml::SignatureType::SigNoVis, withStereotype)); if (i < last) s.append(QLatin1String(", ")); } s.append(QLatin1String(")")); } else if (parameterlessOpNeedsParentheses) { s.append(QLatin1String("()")); } UMLClassifier *ownParent = umlParent()->asUMLClassifier(); QString returnType; UMLClassifier *retType = UMLClassifierListItem::getType(); if (retType) { UMLPackage *retVisibility = retType->umlPackage(); if (retVisibility != ownParent && retVisibility != ownParent->umlPackage()) returnType = retType->fullyQualifiedName(); else returnType = retType->name(); } if (returnType.length() > 0 && returnType != QLatin1String("void")) { s.append(QLatin1String(" : ")); if (returnType.startsWith(QLatin1String(QLatin1String("virtual ")))) { s += returnType.mid(8); } else { s += returnType; } } if (withStereotype) { QString st = stereotype(true); if (!st.isEmpty()) s += QLatin1Char(' ') + st; } return s; } /** * Add a parameter to the operation. * * @param parameter the parameter to add * @param position the position in the parameter list. * If position = -1 the parameter will be * appended to the list. */ void UMLOperation::addParm(UMLAttribute *parameter, int position) { if(position >= 0 && position <= (int)m_List.count()) m_List.insert(position, parameter); else m_List.append(parameter); UMLObject::emitModified(); connect(parameter, SIGNAL(modified()), this, SIGNAL(modified())); } /** * Returns an unused parameter name for a new parameter. */ QString UMLOperation::getUniqueParameterName() { QString currentName = i18n("new_parameter"); QString name = currentName; for (int number = 1; findParm(name); ++number) { name = currentName + QLatin1Char('_') + QString::number(number); } return name; } /** * Overloaded '==' operator. */ bool UMLOperation::operator==(const UMLOperation & rhs) const { if (this == &rhs) return true; if (!UMLObject::operator==(rhs)) return false; if (getTypeName() != rhs.getTypeName()) return false; if (m_List.count() != rhs.m_List.count()) return false; if (!(m_List == rhs.m_List)) return false; return true; } /** * Copy the internal presentation of this object into the new * object. */ void UMLOperation::copyInto(UMLObject *lhs) const { UMLOperation *target = lhs->asUMLOperation(); UMLClassifierListItem::copyInto(target); m_List.copyInto(&(target->m_List)); } /** * Make a clone of this object. */ UMLObject* UMLOperation::clone() const { //FIXME: The new operation should be slaved to the NEW parent not the old. UMLOperation *clone = new UMLOperation(umlParent()->asUMLClassifier()); copyInto(clone); return clone; } /** * Calls resolveRef() on all parameters. * Needs to be called after all UML objects are loaded from file. * * @return true for success */ bool UMLOperation::resolveRef() { bool overallSuccess = UMLObject::resolveRef(); // See remark on iteration style in UMLClassifier::resolveRef() foreach (UMLAttribute* pAtt, m_List) { if (! pAtt->resolveRef()) overallSuccess = false; } return overallSuccess; } /** * Returns whether this operation is a constructor. * * @return true if this operation is a constructor */ bool UMLOperation::isConstructorOperation() { // if an operation has the stereotype constructor // return true if (stereotype() == QLatin1String("constructor")) return true; UMLClassifier * c = umlParent()->asUMLClassifier(); if (!c) return false; QString cName = c->name(); QString opName = name(); // It's a constructor operation if the operation name // matches that of the parent classifier. return (cName == opName); } /** * Returns whether this operation is a destructor. * * @return true if this operation is a destructor */ bool UMLOperation::isDestructorOperation() { if (stereotype() == QLatin1String("destructor")) return true; UMLClassifier * c = umlParent()->asUMLClassifier(); if (!c) return false; QString cName = c->name(); QString opName = name(); // Special support for C++ syntax: // It's a destructor operation if the operation name begins // with "~" followed by the name of the parent classifier. if (! opName.startsWith(QLatin1Char('~'))) return false; opName.remove(QRegExp(QLatin1String("^~\\s*"))); return (cName == opName); } /** * Shortcut for (isConstructorOperation() || isDestructorOperation()). * * @return true if this operation is a constructor or destructor */ bool UMLOperation::isLifeOperation() { return (isConstructorOperation() || isDestructorOperation()); } /** * Sets whether this operation is a query (C++ "const"). */ void UMLOperation::setConst(bool b) { m_bConst = b; } /** * Returns whether this operation is a query (C++ "const"). */ bool UMLOperation::getConst() const { return m_bConst; } /** * Sets whether this operation has override flag. */ void UMLOperation::setOverride(bool b) { m_Override = b; } /** * Returns whether this operation has override flag. */ bool UMLOperation::getOverride() const { return m_Override; } +/** + * Sets whether this operation is a virtual method. + */ +void UMLOperation::setVirtual(bool b) +{ + m_virtual = b; +} + +/** + * Returns whether this operation is a virtual method. + */ +bool UMLOperation::isVirtual() const +{ + return m_virtual; +} + /** * Display the properties configuration dialog for the template. * * @param parent the parent for the dialog */ bool UMLOperation::showPropertiesDialog(QWidget* parent) { UMLOperationDialog dialog(parent, this); return dialog.exec(); } /** * Sets the source code for this operation. * * @param code the body of this operation */ void UMLOperation::setSourceCode(const QString& code) { m_Code = code; } /** * Returns the source code for this operation. */ QString UMLOperation::getSourceCode() const { return m_Code; } /** * Saves to the XMI element. */ void UMLOperation::saveToXMI1(QDomDocument & qDoc, QDomElement & qElement) { QDomElement operationElement = UMLObject::save1(QLatin1String("UML:Operation"), qDoc); operationElement.setAttribute(QLatin1String("isQuery"), m_bConst ? QLatin1String("true") : QLatin1String("false")); operationElement.setAttribute(QLatin1String("isOverride"), m_Override ? QLatin1String("true") : QLatin1String("false")); + operationElement.setAttribute(QLatin1String("isVirtual"), m_virtual ? QLatin1String("true") : QLatin1String("false")); QDomElement featureElement = qDoc.createElement(QLatin1String("UML:BehavioralFeature.parameter")); if (m_pSecondary) { QDomElement retElement = qDoc.createElement(QLatin1String("UML:Parameter")); if (m_returnId == Uml::ID::None) { uDebug() << name() << ": m_returnId is not set, setting it now."; m_returnId = UniqueID::gen(); } retElement.setAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_returnId)); retElement.setAttribute(QLatin1String("type"), Uml::ID::toString(m_pSecondary->id())); retElement.setAttribute(QLatin1String("kind"), QLatin1String("return")); featureElement.appendChild(retElement); } else { uDebug() << "m_SecondaryId is " << m_SecondaryId; } //save each attribute here, type different foreach(UMLAttribute* pAtt, m_List) { QDomElement attElement = pAtt->UMLObject::save1(QLatin1String("UML:Parameter"), qDoc); UMLClassifier *attrType = pAtt->getType(); if (attrType) { attElement.setAttribute(QLatin1String("type"), Uml::ID::toString(attrType->id())); } else { attElement.setAttribute(QLatin1String("type"), pAtt->getTypeName()); } attElement.setAttribute(QLatin1String("value"), pAtt->getInitialValue()); Uml::ParameterDirection::Enum kind = pAtt->getParmKind(); if (kind == Uml::ParameterDirection::Out) attElement.setAttribute(QLatin1String("kind"), QLatin1String("out")); else if (kind == Uml::ParameterDirection::InOut) attElement.setAttribute(QLatin1String("kind"), QLatin1String("inout")); // The default for the parameter kind is "in". featureElement.appendChild(attElement); } if (featureElement.hasChildNodes()) { operationElement.appendChild(featureElement); } qElement.appendChild(operationElement); } /** * Loads a XMI element. */ bool UMLOperation::load1(QDomElement & element) { m_SecondaryId = element.attribute(QLatin1String("type")); QString isQuery = element.attribute(QLatin1String("isQuery")); if (!isQuery.isEmpty()) { // We need this extra test for isEmpty() because load() might have been // called again by the processing for BehavioralFeature.parameter (see below) m_bConst = (isQuery == QLatin1String("true")); } QString isOverride = element.attribute(QLatin1String("isOverride")); m_Override = (isOverride == QLatin1String("true")); + QString isVirtual = element.attribute(QLatin1String("isVirtual")); + m_virtual = (isVirtual == QLatin1String("true")); QDomNode node = element.firstChild(); if (node.isComment()) node = node.nextSibling(); QDomElement attElement = node.toElement(); while (!attElement.isNull()) { QString tag = attElement.tagName(); if (UMLDoc::tagEq(tag, QLatin1String("BehavioralFeature.parameter")) || UMLDoc::tagEq(tag, QLatin1String("Element.ownedElement"))) { // Embarcadero's Describe if (! load1(attElement)) return false; } else if (UMLDoc::tagEq(tag, QLatin1String("Parameter"))) { QString kind = attElement.attribute(QLatin1String("kind")); if (kind.isEmpty()) { kind = attElement.attribute(QLatin1String("direction")); // Embarcadero's Describe if (kind.isEmpty()) { // Perhaps the kind is stored in a child node: for (QDomNode n = attElement.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) continue; QDomElement tempElement = n.toElement(); QString tag = tempElement.tagName(); if (!UMLDoc::tagEq(tag, QLatin1String("kind"))) continue; kind = tempElement.attribute(QLatin1String("xmi.value")); break; } } if (kind.isEmpty()) { kind = QLatin1String("in"); } } if (kind == QLatin1String("return") || kind == QLatin1String("result")) { // Embarcadero's Describe QString returnId = Model_Utils::getXmiId(attElement); if (!returnId.isEmpty()) m_returnId = Uml::ID::fromString(returnId); m_SecondaryId = attElement.attribute(QLatin1String("type")); if (m_SecondaryId.isEmpty()) { // Perhaps the type is stored in a child node: QDomNode node = attElement.firstChild(); while (!node.isNull()) { if (node.isComment()) { node = node.nextSibling(); continue; } QDomElement tempElement = node.toElement(); QString tag = tempElement.tagName(); if (!UMLDoc::tagEq(tag, QLatin1String("type"))) { node = node.nextSibling(); continue; } m_SecondaryId = Model_Utils::getXmiId(tempElement); if (m_SecondaryId.isEmpty()) m_SecondaryId = tempElement.attribute(QLatin1String("xmi.idref")); if (m_SecondaryId.isEmpty()) { QDomNode inner = node.firstChild(); QDomElement tmpElem = inner.toElement(); m_SecondaryId = Model_Utils::getXmiId(tmpElem); if (m_SecondaryId.isEmpty()) m_SecondaryId = tmpElem.attribute(QLatin1String("xmi.idref")); } break; } if (m_SecondaryId.isEmpty()) { uError() << name() << ": cannot find return type."; } } // Use deferred xmi.id resolution. m_pSecondary = 0; } else { UMLAttribute * pAtt = new UMLAttribute(this); if(!pAtt->loadFromXMI1(attElement)) { delete pAtt; return false; } if (kind == QLatin1String("out")) pAtt->setParmKind(Uml::ParameterDirection::Out); else if (kind == QLatin1String("inout")) pAtt->setParmKind(Uml::ParameterDirection::InOut); else pAtt->setParmKind(Uml::ParameterDirection::In); m_List.append(pAtt); } } node = node.nextSibling(); if (node.isComment()) node = node.nextSibling(); attElement = node.toElement(); // loading the source code which was entered in the 'classpropdlg' dialog is not done // with the following code, because there is no CodeDocument. /* CodeGenerator* codegen = UMLApp::app()->getGenerator(); if (codegen) { uDebug() << "CodeDocument searching with id=" << Uml::ID::toString(UMLObject::ID()); CodeDocument* codeDoc = codegen->findCodeDocumentByID(Uml::ID::toString(UMLObject::ID())); if (codeDoc) { uDebug() << "CodeDocument found:\n" << codeDoc; } } */ // it is done in the code generators by calling CodeGenerator::loadFromXMI1(...). }//end while return true; } diff --git a/umbrello/uml1model/operation.h b/umbrello/uml1model/operation.h index 9fc0bd093..f50e93406 100644 --- a/umbrello/uml1model/operation.h +++ b/umbrello/uml1model/operation.h @@ -1,87 +1,90 @@ /*************************************************************************** * 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) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef OPERATION_H #define OPERATION_H #include "umlattributelist.h" #include "classifierlistitem.h" class UMLClassifier; /** * This class represents an operation in the UML model. * * Bugs and comments to umbrello-devel@kde.org or http://bugs.kde.org */ class UMLOperation : public UMLClassifierListItem { Q_OBJECT public: UMLOperation(UMLClassifier * parent, const QString& name, Uml::ID::Type id = Uml::ID::None, Uml::Visibility::Enum s = Uml::Visibility::Public, UMLObject *rt = 0); explicit UMLOperation(UMLClassifier * parent); virtual ~UMLOperation(); bool operator==(const UMLOperation & rhs) const; virtual void copyInto(UMLObject *lhs) const; virtual UMLObject* clone() const; void setType(UMLObject* type); void moveParmLeft(UMLAttribute *a); void moveParmRight(UMLAttribute *a); void removeParm(UMLAttribute *a, bool emitModifiedSignal = true); UMLAttributeList getParmList() const; UMLAttribute * findParm(const QString &name); QString toString(Uml::SignatureType::Enum sig = Uml::SignatureType::NoSig, bool withStereotype=false); void addParm(UMLAttribute *parameter, int position = -1); bool resolveRef(); QString getUniqueParameterName(); virtual bool showPropertiesDialog(QWidget* parent = 0); bool isConstructorOperation(); bool isDestructorOperation(); bool isLifeOperation(); void setConst(bool b); bool getConst() const; void setOverride(bool b); bool getOverride() const; + void setVirtual(bool b); + bool isVirtual() const; void setSourceCode(const QString& code); QString getSourceCode() const; void saveToXMI1(QDomDocument & qDoc, QDomElement & qElement); protected: bool load1(QDomElement & element); private: Uml::ID::Type m_returnId; ///< Holds the xmi.id of the UMLAttributeList m_List; ///< Parameter list bool m_bConst; ///< Holds the isQuery attribute of the bool m_Override; ///< Holds the override attribute of the + bool m_virtual; ///< Holds the virtual attribute of the QString m_Code; ///< Holds the entered source code }; #endif diff --git a/umbrello/version.h b/umbrello/version.h index 595630afb..7cee1eb3b 100644 --- a/umbrello/version.h +++ b/umbrello/version.h @@ -1,38 +1,38 @@ /*************************************************************************** * 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) 2002-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ #ifndef UMBRELLO_VERSION_H #define UMBRELLO_VERSION_H #if QT_VERSION < 0x050000 #include #endif inline QByteArray umbrelloVersion() { #ifdef UMBRELLO_VERSION_STRING QString versionStr = QString::fromLatin1(UMBRELLO_VERSION_STRING); #else QString versionStr = QString::fromLatin1("%1.%2.%3") .arg(KDE::versionMajor()-2) .arg(KDE::versionMinor()) .arg(KDE::versionRelease()); #endif return versionStr.toLatin1(); } // Update this version and dtd's in doc/xml when changing the XMI file format #if defined(ENABLE_WIDGET_SHOW_DOC) || defined(ENABLE_XMIRESOLUTION) -#define XMI_FILE_VERSION "1.6.15" +#define XMI_FILE_VERSION "1.6.16" #else -#define XMI_FILE_VERSION "1.6.14" +#define XMI_FILE_VERSION "1.6.15" #endif #endif