diff options
Diffstat (limited to 'umbrello/umbrello/codegenerators/cppwriter.cpp')
-rw-r--r-- | umbrello/umbrello/codegenerators/cppwriter.cpp | 1283 |
1 files changed, 1283 insertions, 0 deletions
diff --git a/umbrello/umbrello/codegenerators/cppwriter.cpp b/umbrello/umbrello/codegenerators/cppwriter.cpp new file mode 100644 index 00000000..b24c12b0 --- /dev/null +++ b/umbrello/umbrello/codegenerators/cppwriter.cpp @@ -0,0 +1,1283 @@ +/*************************************************************************** + * cppwriter.cpp - description * + * This is the "old" code generator that does not support code editing * + * in the Modeller but uses significantly less file space because the * + * source code is not replicated in the XMI file. * + * * + * copyright : (C) 2003 Brian Thomas brian.thomas@gsfc.nasa.gov * + * (C) 2004-2006 Umbrello UML Modeller Authors <uml-devel@uml.sf.net> * + * * + *************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// own header +#include "cppwriter.h" +// qt/kde includes +#include <qfile.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <kdebug.h> +// app includes +#include "classifierinfo.h" +#include "codegen_utils.h" +#include "../uml.h" +#include "../umldoc.h" +#include "../classifier.h" +#include "../operation.h" +#include "../template.h" +#include "../umltemplatelist.h" +#include "../umlclassifierlistitemlist.h" +#include "../classifierlistitem.h" +#include "../model_utils.h" +#include "../codegenerationpolicy.h" + +// 3-14-2003: this code developed from the javawriter with parts of the +// original cppwriter by Luis De la Parra Blum + +CppWriter::CppWriter() +{ + // Probably we could resolve this better through the use of templates, + // but its a quick n dirty fix for the timebeing.. until codegeneration + // template system is in place. + // You can insert code here. 3 general variables exist: "%VARNAME%" + // allows you to specify where the vector variable should be in your code, + // and "%ITEMCLASS%", if needed, where the class of the item is declared. + VECTOR_METHOD_APPEND = "%VARNAME%.push_back(add_object);"; // for std::vector + VECTOR_METHOD_REMOVE = "int i, size = %VARNAME%.size();\nfor ( i = 0; i < size; i++) {\n\t%ITEMCLASS% item = %VARNAME%.at(i);\n\tif(item == remove_object) {\n\t\tvector<%ITEMCLASS%>::iterator it = %VARNAME%.begin() + i;\n\t\t%VARNAME%.erase(it);\n\t\treturn;\n\t}\n }"; // for std::vector + VECTOR_METHOD_INIT = QString(); // nothing to be done + /* + VECTOR_METHOD_APPEND = "%VARNAME%.append(&add_object);"; // Qt lib implementation + VECTOR_METHOD_REMOVE = "%VARNAME%.removeRef(&remove_object);"; // Qt lib implementation + VECTOR_METHOD_INIT = "%VARNAME%.setAutoDelete(false);"; // Qt library + */ + + OBJECT_METHOD_INIT = "%VARNAME% = new %ITEMCLASS%( );"; // Qt library + + // boolean config params + INLINE_ASSOCIATION_METHODS = false; + +} + +CppWriter::~CppWriter() { } + +Uml::Programming_Language CppWriter::getLanguage() { + return Uml::pl_Cpp; +} + +CPPCodeGenerationPolicy *CppWriter::policyExt() { + return static_cast<CPPCodeGenerationPolicy*>(UMLApp::app()->getPolicyExt()); +} + +void CppWriter::writeClass(UMLClassifier *c) +{ + + if (!c) { + kDebug() << "Cannot write class of NULL concept!\n"; + return; + } + + QFile fileh, filecpp; + + // find an appropriate name for our file + QString fileName = findFileName(c, ".h"); + if (fileName.isEmpty()) { + emit codeGenerated(c, false); + return; + } + + // preparations + m_classifierInfo = new ClassifierInfo(c); + m_classifierInfo->fileName = fileName; + m_classifierInfo->className = cleanName(c->getName()); + + if (c->getVisibility() != Uml::Visibility::Implementation) { + if( !openFile(fileh, fileName)) { + emit codeGenerated(c, false); + return; + } + // write Header file + writeHeaderFile(c, fileh); + fileh.close(); + } + + // Determine whether the implementation file is required. + // (It is not required if the class is an enumeration.) + bool need_impl = true; + if (c->getBaseType() == Uml::ot_Enum) { + need_impl = false; + } + if (need_impl) { + fileName.replace( QRegExp(".h$"), ".cpp"); + if( !openFile(filecpp, fileName)) { + emit codeGenerated(c, false); + return; + } + // write Source file + writeSourceFile(c, filecpp); + filecpp.close(); + } + + // Wrap up: free m_classifierInfo, emit done code + m_classifierInfo = 0; + + emit codeGenerated(c, true); + +} + +void CppWriter::writeHeaderFile (UMLClassifier *c, QFile &fileh) { + + // open stream for writing + QTextStream h (&fileh); + + // up the indent level to one + m_indentLevel = 1; + + // write header blurb + QString str = getHeadingFile(".h"); + if(!str.isEmpty()) { + str.replace(QRegExp("%filename%"),m_classifierInfo->fileName + ".h"); + str.replace(QRegExp("%filepath%"),fileh.name()); + h << str<< m_endl; + } + + // Write the hash define stuff to prevent multiple parsing/inclusion of header + QString hashDefine = m_classifierInfo->className.upper().simplifyWhiteSpace().replace(QRegExp(" "), "_"); + writeBlankLine(h); + h << "#ifndef "<< hashDefine + "_H" << m_endl; + h << "#define "<< hashDefine + "_H" << m_endl; + + writeClassDecl(c, h); + + // last thing..close our hashdefine + h << m_endl << "#endif // " << hashDefine + "_H" << m_endl; + +} + +void CppWriter::writeHeaderAccessorMethodDecl(UMLClassifier *c, Uml::Visibility permitScope, QTextStream &stream) +{ + + // attributes + writeHeaderAttributeAccessorMethods(permitScope, true, stream); // write static attributes first + writeHeaderAttributeAccessorMethods(permitScope, false, stream); + + // associations + writeAssociationMethods(m_classifierInfo->plainAssociations, permitScope, + true, INLINE_ASSOCIATION_METHODS, true, c->getID(), stream); + writeAssociationMethods(m_classifierInfo->uniAssociations, permitScope, + true, INLINE_ASSOCIATION_METHODS, true, c->getID(), stream); + writeAssociationMethods(m_classifierInfo->aggregations, permitScope, + true, INLINE_ASSOCIATION_METHODS, true, c->getID(), stream); + writeAssociationMethods(m_classifierInfo->compositions, permitScope, + true, INLINE_ASSOCIATION_METHODS, false, c->getID(), stream); + + writeBlankLine(stream); + +} + +void CppWriter::writeHeaderFieldDecl(UMLClassifier *c, Uml::Visibility permitScope, QTextStream &stream) +{ + // attributes + writeAttributeDecls(permitScope, true, stream); // write static attributes first + writeAttributeDecls(permitScope, false, stream); + + // associations + writeAssociationDecls(m_classifierInfo->plainAssociations, permitScope, c->getID(), stream); + writeAssociationDecls(m_classifierInfo->uniAssociations, permitScope, c->getID(), stream); + writeAssociationDecls(m_classifierInfo->aggregations, permitScope, c->getID(), stream); + writeAssociationDecls(m_classifierInfo->compositions, permitScope, c->getID(), stream); + +} + +void CppWriter::writeSourceFile (UMLClassifier *c, QFile &filecpp ) { + + // open stream for writing + QTextStream cpp (&filecpp); + + // set the starting indentation at zero + m_indentLevel = 0; + + //try to find a heading file (license, coments, etc) + QString str; + str = getHeadingFile(".cpp"); + if(!str.isEmpty()) { + str.replace(QRegExp("%filename%"),m_classifierInfo->fileName + ".cpp"); + str.replace(QRegExp("%filepath%"),filecpp.name()); + cpp << str << m_endl; + } + + // IMPORT statements + // Q: Why all utils? Isnt just List and Vector the only classes we are using? + // Our import *should* also look at operations, and check that objects being + // used arent in another package (and thus need to be explicitly imported here). + cpp << "#include \"" << m_classifierInfo->className << ".h\"" << m_endl; + writeBlankLine(cpp); + + if (c->getVisibility() == Uml::Visibility::Implementation) { + writeClassDecl(c, cpp); + } + + // Start body of class + + // Constructors: anything we more we need to do here ? + // + if(!m_classifierInfo->isInterface) + writeConstructorMethods(cpp); + + // METHODS + // + + // write comment for section IF needed + QString indent = getIndent(); + if (forceDoc() || m_classifierInfo->hasAccessorMethods || m_classifierInfo->hasOperationMethods) + { + + writeComment(" ", indent, cpp); + writeComment("Methods", indent, cpp); + writeComment(" ", indent, cpp); + writeBlankLine(cpp); + writeBlankLine(cpp); + } + + // write comment for sub-section IF needed + if (forceDoc() || m_classifierInfo->hasAccessorMethods ) + { + writeComment("Accessor methods", indent, cpp); + writeComment(" ", indent, cpp); + writeBlankLine(cpp); + } + + // Accessor methods for attributes + const bool bInlineAccessors = policyExt()->getAccessorsAreInline(); + if (!bInlineAccessors && m_classifierInfo->hasAttributes) + { + writeAttributeMethods(&(m_classifierInfo->static_atpub), Uml::Visibility::Public, false, true, !bInlineAccessors, cpp); + writeAttributeMethods(&(m_classifierInfo->atpub), Uml::Visibility::Public, false, false, !bInlineAccessors, cpp); + writeAttributeMethods(&(m_classifierInfo->static_atprot), Uml::Visibility::Protected, false, true, !bInlineAccessors, cpp); + writeAttributeMethods(&(m_classifierInfo->atprot), Uml::Visibility::Protected, false, false, !bInlineAccessors, cpp); + writeAttributeMethods(&(m_classifierInfo->static_atpriv), Uml::Visibility::Private, false, true, !bInlineAccessors, cpp); + writeAttributeMethods(&(m_classifierInfo->atpriv), Uml::Visibility::Private, false, false, !bInlineAccessors, cpp); + } + + // accessor methods for associations + + // public + writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Public, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Public, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Public, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Public, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + + // protected + writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Protected, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Protected, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Protected, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Protected, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + + + // private + writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Private, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Private, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Private, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Private, false, + !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp); + writeBlankLine(cpp); + + // Other operation methods -- all other operations are now written + // + + // write comment for sub-section IF needed + if (forceDoc() || m_classifierInfo->hasOperationMethods) + { + writeComment("Other methods", indent, cpp); + writeComment(" ", indent, cpp); + writeBlankLine(cpp); + } + + if(!policyExt()->getOperationsAreInline()) + { + writeOperations(c,false,Uml::Visibility::Public,cpp); + writeOperations(c,false,Uml::Visibility::Protected,cpp); + writeOperations(c,false,Uml::Visibility::Private,cpp); + } + + // Yep, bringing up the back of the bus, our initialization method for attributes + writeInitAttibuteMethod (cpp); + + writeBlankLine(cpp); + +} + +void CppWriter::writeClassDecl(UMLClassifier *c, QTextStream &cpp) +{ + UMLClassifierList superclasses = m_classifierInfo->superclasses; + for(UMLClassifier *classifier = superclasses.first(); classifier ;classifier = superclasses.next()) { + QString headerName = findFileName(classifier, ".h"); + if (!headerName.isEmpty()) { + cpp << "#include \"" << headerName << "\"" << m_endl; + } + } + + writeBlankLine(cpp); + cpp << "#include " << policyExt()->getStringClassNameInclude() << m_endl; + if(m_classifierInfo->hasVectorFields) + { + cpp << "#include " << policyExt()->getVectorClassNameInclude() << m_endl; + writeBlankLine(cpp); + } + + if(m_classifierInfo->hasAssociations) + { + // write all includes we need to include other classes, that arent us. + printAssociationIncludeDecl (m_classifierInfo->plainAssociations, c->getID(), cpp); + printAssociationIncludeDecl (m_classifierInfo->uniAssociations, c->getID(), cpp); + printAssociationIncludeDecl (m_classifierInfo->aggregations, c->getID(), cpp); + printAssociationIncludeDecl (m_classifierInfo->compositions, c->getID(), cpp); + + writeBlankLine(cpp); + } + + + for(UMLClassifier *classifier = superclasses.first(); classifier ; classifier = superclasses.next()) { + if(classifier->getPackage()!=c->getPackage() && !classifier->getPackage().isEmpty()) { + cpp << "using " << cleanName(classifier->getPackage()) << "::" << cleanName(classifier->getName()) << ";" << m_endl; + } + } + + if(!c->getPackage().isEmpty() && policyExt()->getPackageIsNamespace()) + cpp << m_endl << "namespace " << cleanName(c->getPackage()) << " {" << m_endl << m_endl; + + //Write class Documentation if there is somthing or if force option + if(forceDoc() || !c->getDoc().isEmpty()) { + cpp << m_endl << "/**" << m_endl; + cpp << " * class " << m_classifierInfo->className << m_endl; + cpp << formatDoc(c->getDoc()," * "); + cpp << " */"; + writeBlankLine(cpp); + writeBlankLine(cpp); + } + + //check if class is abstract and / or has abstract methods + if((c->getAbstract() || m_classifierInfo->isInterface ) + && !hasAbstractOps(c)) + cpp << "/******************************* Abstract Class ****************************" << m_endl + <<m_classifierInfo->className << " does not have any pure virtual methods, but its author" << m_endl + <<" defined it as an abstract class, so you should not use it directly." << m_endl + <<" Inherit from it instead and create only objects from the derived classes" << m_endl + <<"*****************************************************************************/" << m_endl << m_endl; + + if (c->getBaseType() == Uml::ot_Enum) { + UMLClassifierListItemList litList = c->getFilteredList(Uml::ot_EnumLiteral); + uint i = 0; + cpp << "enum " << m_classifierInfo->className << " {" << m_endl; + for (UMLClassifierListItem *lit = litList.first(); lit; lit = litList.next()) { + QString enumLiteral = cleanName(lit->getName()); + cpp << getIndent() << enumLiteral; + if (++i < litList.count()) + cpp << ","; + cpp << m_endl; + } + cpp << m_endl << "};" << m_endl; // end of class header + if(!c->getPackage().isEmpty() && policyExt()->getPackageIsNamespace()) + cpp << "} // end of package namespace" << m_endl; + return; + } + + // Generate template parameters. + UMLTemplateList template_params = c->getTemplateList(); + if (template_params.count()) { + cpp << "template<"; + for (UMLTemplate *t = template_params.first(); t; ) { + QString formalName = t->getName(); + QString typeName = t->getTypeName(); + cpp << typeName << " " << formalName; + if ((t = template_params.next()) != NULL) + cpp << ", "; + } + cpp << ">" << m_endl; + } + + cpp << "class " << m_classifierInfo->className; + if (m_classifierInfo->superclasses.count() > 0) + cpp << " : "; + uint numOfSuperClasses = m_classifierInfo->superclasses.count(); + uint i = 0; + for (UMLClassifier *superClass = m_classifierInfo->superclasses.first(); + superClass ; superClass = m_classifierInfo->superclasses.next()) + { + i++; + if (superClass->getAbstract() || superClass->isInterface()) + cpp << "virtual "; + cpp << "public " << cleanName(superClass->getName()); + if (i < numOfSuperClasses) + cpp << ", "; + } + + cpp << m_endl << "{" << m_endl; // begin the body of the class + + + //declarations of operations + // + + // + // write out field and operations decl grouped by visibility + // + + // PUBLIC attribs/methods + cpp << "public:" << m_endl << m_endl; // print visibility decl. + // for public: constructors are first ops we print out + if(!m_classifierInfo->isInterface) + writeConstructorDecls(cpp); + writeHeaderFieldDecl(c,Uml::Visibility::Public, cpp); + writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, cpp); + writeOperations(c,true,Uml::Visibility::Public,cpp); + + // PROTECTED attribs/methods + // + cpp << "protected" << ":" << m_endl << m_endl; // print visibility decl. + writeHeaderFieldDecl(c,Uml::Visibility::Protected, cpp); + writeHeaderAccessorMethodDecl(c, Uml::Visibility::Protected, cpp); + writeOperations(c,true,Uml::Visibility::Protected,cpp); + + // PRIVATE attribs/methods + // + cpp << "private" << ":" << m_endl << m_endl; // print visibility decl. + writeHeaderFieldDecl(c,Uml::Visibility::Private, cpp); + writeHeaderAccessorMethodDecl(c, Uml::Visibility::Private, cpp); + writeOperations(c,true,Uml::Visibility::Private,cpp); + writeInitAttibuteDecl(cpp); // this is always private, used by constructors to initialize class + + // end of class header + cpp << m_endl << "};" << m_endl; + + // end of class namespace, if any + if(!c->getPackage().isEmpty() && policyExt()->getPackageIsNamespace()) + cpp << "}; // end of package namespace" << m_endl; + +} + +void CppWriter::writeAttributeDecls (Uml::Visibility visibility, bool writeStatic, QTextStream &stream ) +{ + + if(m_classifierInfo->isInterface) + return; + + UMLAttributeList * list; + switch (visibility) + { + case Uml::Visibility::Private: + if(writeStatic) + list = &(m_classifierInfo->static_atpriv); + else + list = &(m_classifierInfo->atpriv); + break; + + case Uml::Visibility::Protected: + if(writeStatic) + list = &(m_classifierInfo->static_atprot); + else + list = &(m_classifierInfo->atprot); + break; + + case Uml::Visibility::Public: + default: + if(writeStatic) + list = &(m_classifierInfo->static_atpub); + else + list = &(m_classifierInfo->atpub); + break; + } + + //write documentation + if(forceDoc() || list->count() > 0) + { + QString strVis = Codegen_Utils::capitalizeFirstLetter(visibility.toString()); + QString strStatic = writeStatic ? "Static ":""; + writeComment(strStatic + strVis + " attributes",getIndent(), stream); + writeComment(" ",getIndent(), stream); + writeBlankLine(stream); + } + + if (list->count() > 0) { + + // write attrib declarations now + bool isFirstAttrib = true; + QString documentation; + for(UMLAttribute *at=list->first(); at; at=list->next()) + { + + // bool noPriorDocExists = documentation.isEmpty(); + documentation = at->getDoc(); + + // add a line for code clarity IF PRIOR attrib has comment on it + // OR this line has documentation + // if(!isFirstAttrib && (!documentation.isEmpty()||!noPriorDocExists)) + // writeBlankLine(stream); + + isFirstAttrib = false; + + QString varName = getAttributeVariableName(at); + + QString staticValue = at->getStatic() ? "static " : ""; + QString typeName = fixTypeName(at->getTypeName()); + if(!documentation.isEmpty()) + writeComment(documentation, getIndent(), stream); + stream << getIndent() << staticValue << typeName << " " << varName << ";" << m_endl; + + } + + /* + if(list->count() > 0) + writeBlankLine(stream); + */ + + } + +} + +void CppWriter::writeHeaderAttributeAccessorMethods (Uml::Visibility visibility, bool writeStatic, QTextStream &stream ) +{ + // check the current policy about generate accessors as public + UMLAttributeList * list; + switch (visibility) + { + case Uml::Visibility::Private: + if(writeStatic) + list = &(m_classifierInfo->static_atpriv); + else + list = &(m_classifierInfo->atpriv); + break; + + case Uml::Visibility::Protected: + if(writeStatic) + list = &(m_classifierInfo->static_atprot); + else + list = &(m_classifierInfo->atprot); + break; + + case Uml::Visibility::Public: + default: + if(writeStatic) + list = &(m_classifierInfo->static_atpub); + else + list = &(m_classifierInfo->atpub); + break; + } + + // switch to public + if (visibility != Uml::Visibility::Public) + stream << "public:" << m_endl << m_endl; + + // write accessor methods for attribs we found + writeAttributeMethods(list, visibility, true, false, policyExt()->getAccessorsAreInline(), stream); + + // switch back to previous vis. + if (visibility != Uml::Visibility::Public) + stream << visibility.toString() << ":" << m_endl << m_endl; +} + +// this is for writing *source* or *header* file attribute methods +// +void CppWriter::writeAttributeMethods(UMLAttributeList *attribs, + Uml::Visibility visibility, bool isHeaderMethod, + bool isStatic, + bool writeMethodBody, QTextStream &stream) +{ + + if (!policyExt()->getAutoGenerateAccessors()) + return; + + if (forceDoc() || attribs->count() > 0) + { + QString strVis = Codegen_Utils::capitalizeFirstLetter(visibility.toString()); + QString strStatic = (isStatic ? " static" : ""); + writeBlankLine(stream); + writeComment(strVis + strStatic + " attribute accessor methods",getIndent(),stream); + writeComment(" ",getIndent(), stream); + writeBlankLine(stream); + } + + // return now if NO attributes to work on + if (attribs->count() == 0) + return; + + UMLAttribute *at; + for(at=attribs->first(); at; at=attribs->next()) + { + + QString varName = getAttributeVariableName(at); + QString methodBaseName = cleanName(at->getName()); + + // force capitalizing the field name, this is silly, + // from what I can tell, this IS the default behavior for + // cleanName. I dunno why its not working -b.t. + methodBaseName = methodBaseName.stripWhiteSpace(); + methodBaseName.replace(0,1,methodBaseName.at(0).upper()); + + writeSingleAttributeAccessorMethods(at->getTypeName(), varName, + methodBaseName, at->getDoc(), Uml::chg_Changeable, isHeaderMethod, + at->getStatic(), writeMethodBody, stream); + } + +} + +void CppWriter::writeComment(const QString &comment, const QString &myIndent, QTextStream &cpp) +{ + // in the case we have several line comment.. + // NOTE: this part of the method has the problem of adopting UNIX newline, + // need to resolve for using with MAC/WinDoze eventually I assume + if (comment.contains(QRegExp("\n"))) { + + QStringList lines = QStringList::split( "\n", comment); + for(uint i= 0; i < lines.count(); i++) + { + cpp << myIndent << "// " << lines[i] << m_endl; + } + } else { + // this should be more fancy in the future, breaking it up into 80 char + // lines so that it doesn't look too bad + cpp << myIndent << "// "<< comment << m_endl; + } +} + +void CppWriter::writeDocumentation(QString header, QString body, QString end, QTextStream &cpp) +{ + writeBlankLine(cpp); + QString indent = getIndent(); + + cpp << indent << "/**" << m_endl; + if (!header.isEmpty()) + cpp << formatDoc(header, indent + " * "); + if (!body.isEmpty()) + cpp << formatDoc(body, indent + " * "); + if (!end.isEmpty()) + { + QStringList lines = QStringList::split( "\n", end); + for(uint i= 0; i < lines.count(); i++) + cpp << formatDoc(lines[i], indent + " * "); + } + cpp << indent << " */" << m_endl; +} + +void CppWriter::writeAssociationDecls(UMLAssociationList associations, Uml::Visibility permitScope, Uml::IDType id, QTextStream &h) +{ + + if( forceSections() || !associations.isEmpty() ) + { + bool printRoleA = false, printRoleB = false; + for(UMLAssociation *a = associations.first(); a; a = associations.next()) + { + + // it may seem counter intuitive, but you want to insert the role of the + // *other* class into *this* class. + if (a->getObjectId(Uml::A) == id && !a->getRoleName(Uml::B).isEmpty()) + printRoleB = true; + + if (a->getObjectId(Uml::B) == id && !a->getRoleName(Uml::A).isEmpty()) + printRoleA = true; + + // First: we insert documentaion for association IF it has either role AND some documentation (!) + if ((printRoleA || printRoleB) && !(a->getDoc().isEmpty())) + writeComment(a->getDoc(), getIndent(), h); + + // print RoleB decl + if (printRoleB && a->getVisibility(Uml::B) == permitScope) + { + + QString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::B))); + writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::B), a->getMulti(Uml::B), a->getRoleDoc(Uml::B), h); + } + + // print RoleA decl + if (printRoleA && a->getVisibility(Uml::A) == permitScope) + { + QString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::A))); + writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::A), a->getMulti(Uml::A), a->getRoleDoc(Uml::A), h); + } + + // reset for next association in our loop + printRoleA = false; + printRoleB = false; + } + } +} + +void CppWriter::writeAssociationRoleDecl(QString fieldClassName, QString roleName, QString multi, + QString doc, QTextStream &stream) +{ + // ONLY write out IF there is a rolename given + // otherwise its not meant to be declared in the code + if (roleName.isEmpty()) + return; + + QString indent = getIndent(); + + // always put space between this and prior decl, if any + writeBlankLine(stream); + + if (!doc.isEmpty()) + writeComment(doc, indent, stream); + + // declare the association based on whether it is this a single variable + // or a List (Vector). One day this will be done correctly with special + // multiplicity object that we don't have to figure out what it means via regex. + if(multi.isEmpty() || multi.contains(QRegExp("^[01]$"))) + { + QString fieldVarName = "m_" + roleName.lower(); + + // record this for later consideration of initialization IF the + // multi value requires 1 of these objects + if(ObjectFieldVariables.findIndex(fieldVarName) == -1 && + multi.contains(QRegExp("^1$")) + ) + { + // ugh. UGLY. Storing variable name and its class in pairs. + ObjectFieldVariables.append(fieldVarName); + ObjectFieldVariables.append(fieldClassName); + } + + stream << indent << fieldClassName << " * " << fieldVarName << ";" << m_endl; + } + else + { + QString fieldVarName = "m_" + roleName.lower() + "Vector"; + + // record unique occurrences for later when we want to check + // for initialization of this vector + if(VectorFieldVariables.findIndex(fieldVarName) == -1) + VectorFieldVariables.append(fieldVarName); + + stream << indent << policyExt()->getVectorClassName() <<"<" << fieldClassName << "*"; + stream << "> " << fieldVarName << ";" << m_endl; + } +} + +// for either source or header files +void CppWriter::writeAssociationMethods (UMLAssociationList associations, + Uml::Visibility permitVisib, + bool isHeaderMethod, + bool writeMethodBody, + bool writePointerVar, + Uml::IDType myID, QTextStream &stream) +{ + if( forceSections() || !associations.isEmpty() ) + { + for(UMLAssociation *a = associations.first(); a; a = associations.next()) + { + + // insert the methods to access the role of the other + // class in the code of this one + if (a->getObjectId(Uml::A) == myID && a->getVisibility(Uml::B) == permitVisib) + { + // only write out IF there is a rolename given + if(!a->getRoleName(Uml::B).isEmpty()) { + QString fieldClassName = getUMLObjectName(a->getObject(Uml::B)) + (writePointerVar ? " *":""); + writeAssociationRoleMethod(fieldClassName, + isHeaderMethod, + writeMethodBody, + a->getRoleName(Uml::B), + a->getMulti(Uml::B), a->getRoleDoc(Uml::B), + a->getChangeability(Uml::B), stream); + } + } + + if (a->getObjectId(Uml::B) == myID && a->getVisibility(Uml::A) == permitVisib) + { + // only write out IF there is a rolename given + if(!a->getRoleName(Uml::A).isEmpty()) { + QString fieldClassName = getUMLObjectName(a->getObject(Uml::A)) + (writePointerVar ? " *":""); + writeAssociationRoleMethod(fieldClassName, + isHeaderMethod, + writeMethodBody, + a->getRoleName(Uml::A), + a->getMulti(Uml::A), + a->getRoleDoc(Uml::A), + a->getChangeability(Uml::A), + stream); + } + } + + } + } +} + +void CppWriter::writeAssociationRoleMethod (const QString &fieldClassName, + bool isHeaderMethod, + bool writeMethodBody, + const QString &roleName, const QString &multi, + const QString &description, Uml::Changeability_Type change, + QTextStream &stream) +{ + if(multi.isEmpty() || multi.contains(QRegExp("^[01]$"))) + { + QString fieldVarName = "m_" + roleName.lower(); + writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, + description, change, isHeaderMethod, false, writeMethodBody, stream); + } + else + { + QString fieldVarName = "m_" + roleName.lower() + "Vector"; + writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, + description, change, isHeaderMethod, writeMethodBody, stream); + } +} + +void CppWriter::writeVectorAttributeAccessorMethods ( + const QString &fieldClassName, const QString &fieldVarName, + const QString &fieldName, const QString &description, + Uml::Changeability_Type changeType, + bool isHeaderMethod, + bool writeMethodBody, + QTextStream &stream) +{ + + QString className = fixTypeName(fieldClassName); + QString fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); + QString indent = getIndent(); + + // ONLY IF changeability is NOT Frozen + if (changeType != Uml::chg_Frozen) + { + writeDocumentation("Add a " + fldName + " object to the " + fieldVarName + " List",description,"",stream); + stream << indent << "void "; + if(!isHeaderMethod) + stream << m_classifierInfo->className << "::"; + stream << "add" << fldName << " ( " << className << " add_object )"; + if (writeMethodBody) { + QString method = VECTOR_METHOD_APPEND; + method.replace(QRegExp("%VARNAME%"),fieldVarName); + method.replace(QRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); + method.replace(QRegExp("%ITEMCLASS%"),className); + stream << indent << " {" << m_endl; + m_indentLevel++; + printTextAsSeparateLinesWithIndent(method,getIndent(),stream); + m_indentLevel--; + stream << indent << "}" << m_endl; + } else + stream << ";" << m_endl; + } + + // ONLY IF changeability is Changeable + if (changeType == Uml::chg_Changeable) + { + writeDocumentation("Remove a " + fldName + " object from " + fieldVarName + " List", + description, "", stream); + stream << indent << "void "; + if(!isHeaderMethod) + stream << m_classifierInfo->className << "::"; + stream << "remove" << fldName << " ( " << className << " remove_object )"; + if (writeMethodBody) { + QString method = VECTOR_METHOD_REMOVE; + method.replace(QRegExp("%VARNAME%"),fieldVarName); + method.replace(QRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); + method.replace(QRegExp("%ITEMCLASS%"),className); + stream << indent << " {" << m_endl; + m_indentLevel++; + printTextAsSeparateLinesWithIndent(method,getIndent(),stream); + m_indentLevel--; + stream << indent << "}" << m_endl; + } else + stream << ";" << m_endl; + } + + // always allow getting the list of stuff + QString returnVarName = policyExt()->getVectorClassName() + '<' + className + '>'; + writeDocumentation("Get the list of " + fldName + " objects held by " + fieldVarName, + description, + "@return " + returnVarName + " list of " + fldName + " objects held by " + fieldVarName, + stream); + stream << indent << returnVarName << " "; + if(!isHeaderMethod) + stream << m_classifierInfo->className << "::"; + stream << "get" << fldName << "List ( )"; + if(writeMethodBody) { + stream << indent << " {" << m_endl; + m_indentLevel++; + stream << getIndent() << "return " << fieldVarName << ";" << m_endl; + m_indentLevel--; + stream << indent << "}" << m_endl; + } else + stream << ";" << m_endl; + +} + + +void CppWriter::writeSingleAttributeAccessorMethods( + const QString& fieldClassName, const QString& fieldVarName, + const QString& fieldName, const QString &description, + Uml::Changeability_Type change, + bool isHeaderMethod, + bool isStatic, + bool writeMethodBody, + QTextStream &stream) +{ + + // DON'T write this IF its a source method AND writeMethodBody is "false" + if(!isHeaderMethod && !writeMethodBody) + return; + + QString className = fixTypeName(fieldClassName); + QString fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); + QString indent = getIndent(); + + // set method + if (change == Uml::chg_Changeable && !isStatic) { + writeDocumentation("Set the value of " + fieldVarName,description,"@param new_var the new value of " + fieldVarName,stream); + stream << indent << "void "; + if(!isHeaderMethod) + stream << m_classifierInfo->className << "::"; + stream << "set" << fldName << " ( " << className << " new_var )"; + + if(writeMethodBody) { + stream << indent << " {" << m_endl; + m_indentLevel++; + stream << getIndent() << indent; + m_indentLevel--; + if(isStatic) + stream << m_classifierInfo->className << "::"; + stream << fieldVarName << " = new_var;" << m_endl; + stream << indent << "}" << m_endl; + } else + stream << ";" << m_endl; + } + + // get method + writeDocumentation("Get the value of " + fieldVarName,description,"@return the value of " + fieldVarName,stream); + stream << indent << className << " "; + if(!isHeaderMethod) + stream << m_classifierInfo->className << "::"; + stream << "get" << fldName << " ( )"; + + if(writeMethodBody) { + stream << indent << " {" << m_endl; + m_indentLevel++; + stream << getIndent() << "return "; + m_indentLevel--; + if(isStatic) + stream << m_classifierInfo->className << "::"; + stream << fieldVarName << ";" << m_endl; + stream << indent << "}"; + } else + stream << ";" << m_endl; + + writeBlankLine(stream); +} + +// one day, this should print out non-empty constructor operations too. +void CppWriter::writeConstructorDecls(QTextStream &stream) +{ + const bool generateEmptyConstructors = + UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors(); + if (forceDoc() || generateEmptyConstructors) + { + writeComment("Constructors/Destructors", getIndent(), stream); + writeComment(" ", getIndent(), stream); + writeBlankLine(stream); + } + if (!generateEmptyConstructors) + return; + + writeDocumentation("", "Empty Constructor", "", stream); + stream << getIndent() << m_classifierInfo->className << " ( );" << m_endl; + writeDocumentation("", "Empty Destructor", "", stream); + stream << getIndent(); + stream << "virtual ~" << m_classifierInfo->className << " ( );" << m_endl; + writeBlankLine(stream); +} + +void CppWriter::writeInitAttibuteDecl (QTextStream &stream) +{ + if (UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors() && + m_classifierInfo->hasAttributes) + stream << getIndent() << "void initAttributes ( ) ;" << m_endl; +} + +void CppWriter::writeInitAttibuteMethod (QTextStream &stream) +{ + // only need to do this under certain conditions + if (!UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors() || + !m_classifierInfo->hasAttributes) + return; + + QString className = m_classifierInfo->className; + QString indent = getIndent(); + + stream << indent << "void " << className << "::" << "initAttributes ( ) {" << m_endl; + + m_indentLevel++; + // first, initiation of fields derived from attributes + UMLAttributeList atl = m_classifierInfo->getAttList(); + for(UMLAttribute *at = atl.first(); at ; at = atl.next()) { + if(!at->getInitialValue().isEmpty()) { + QString varName = getAttributeVariableName(at); + stream << getIndent() << varName << " = " << at->getInitialValue() << ";" << m_endl; + } + } + // Now initialize the association related fields (e.g. vectors) + if (!VECTOR_METHOD_INIT.isEmpty()) { + QStringList::Iterator it; + for( it = VectorFieldVariables.begin(); it != VectorFieldVariables.end(); ++it ) { + QString fieldVarName = *it; + QString method = VECTOR_METHOD_INIT; + method.replace(QRegExp("%VARNAME%"),fieldVarName); + method.replace(QRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); + stream << getIndent() << method << m_endl; + } + } + + if (!OBJECT_METHOD_INIT.isEmpty()) { + QStringList::Iterator it; + for( it = ObjectFieldVariables.begin(); it != ObjectFieldVariables.end(); ++it ) { + QString fieldVarName = *it; + it++; + QString fieldClassName = *it; + QString method = OBJECT_METHOD_INIT; + method.replace(QRegExp("%VARNAME%"),fieldVarName); + method.replace(QRegExp("%ITEMCLASS%"),fieldClassName); + stream << getIndent() << method << m_endl; + } + } + + // clean up + ObjectFieldVariables.clear(); // shouldn't be needed? + VectorFieldVariables.clear(); // shouldn't be needed? + + m_indentLevel--; + + stream << indent << "}" << m_endl; +} + +// one day, this should print out non-empty constructor operations too. +void CppWriter::writeConstructorMethods(QTextStream &stream) +{ + const bool generateEmptyConstructors = + UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors(); + + if (forceDoc() || generateEmptyConstructors) { + writeComment("Constructors/Destructors", getIndent(), stream); + writeComment(" ", getIndent(), stream); + writeBlankLine(stream); + } + if (!generateEmptyConstructors) + return; + + QString className = m_classifierInfo->className; + // empty constructor + QString indent = getIndent(); + stream << indent << className << "::" << className << " ( ) {" << m_endl; + if(m_classifierInfo->hasAttributes) + stream << indent << indent << "initAttributes();" << m_endl; + stream << indent << "}" << m_endl; + writeBlankLine(stream); + + // empty destructor + stream << getIndent() << className << "::~" << className << " ( ) { }" << m_endl; + writeBlankLine(stream); +} + +// IF the type is "string" we need to declare it as +// the Java Object "String" (there is no string primative in Java). +QString CppWriter::fixTypeName(const QString &string) +{ + if (string.isEmpty()) + return "void"; + if (string == "string") + return policyExt()->getStringClassName(); + return string; +} + +void CppWriter::writeOperations(UMLClassifier *c, bool isHeaderMethod, + Uml::Visibility permitScope, QTextStream &cpp) { + + UMLOperationList oplist; + + //sort operations by scope first and see if there are abstract methods + UMLOperationList inputlist = c->getOpList(); + for (UMLOperation *op = inputlist.first(); op; op = inputlist.next()) { + if (op->getVisibility() == permitScope) { + oplist.append(op); + } + } + + // do people REALLY want these comments? Hmm. + /* + if(forceSections() || oppub.count()) + { + writeComment("public operations",getIndent(),cpp); + writeBlankLine(cpp); + } + */ + writeOperations(oplist,isHeaderMethod, cpp); + +} + +// write operation in either header or +// a source file +void CppWriter::writeOperations(UMLOperationList &oplist, bool isHeaderMethod, QTextStream &cpp) { + QString className = m_classifierInfo->className; + const bool generateEmptyConstructors = + UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors(); + + // generate method decl for each operation given + for (UMLOperation *op = oplist.first(); op; op = oplist.next()) { + + QString returnStr; // buffer for documentation + QString methodReturnType; + UMLAttributeList atl = op->getParmList(); // method parameters + + if (op->isConstructorOperation()) { + if (generateEmptyConstructors && atl.count() == 0) + continue; // it's already been written, see writeConstructor{Decls,Methods} + } else if (op->isDestructorOperation()) { + if (generateEmptyConstructors) + continue; // it's already been written, see writeConstructor{Decls,Methods} + } else { + methodReturnType = fixTypeName(op->getTypeName()); + if(methodReturnType != "void") + returnStr += "@return " + methodReturnType + '\n'; + } + + QString str; + if (op->getAbstract() || m_classifierInfo->isInterface) { + if (isHeaderMethod) { + // declare abstract method as 'virtual' + str += "virtual "; + } + } + + // static declaration for header file + if (isHeaderMethod && op->getStatic()) + str += "static "; + + // returntype of method + str += methodReturnType + ' '; + + if (!isHeaderMethod) + str += className + "::"; + + str += cleanName(op->getName()) + " ("; + + // generate parameters + uint j = 0; + for (UMLAttribute *at = atl.first(); at; at = atl.next(), j++) { + QString typeName = fixTypeName(at->getTypeName()); + QString atName = cleanName(at->getName()); + str += typeName + ' ' + atName; + const QString initVal = at->getInitialValue(); + if (! initVal.isEmpty()) + str += " = " + initVal; + if (j < atl.count() - 1) + str += ", "; + returnStr += "@param " + atName + ' ' + at->getDoc() + '\n'; + } + str += " )"; + + if (op->getConst()) + str += " const"; + + // method body : only gets IF its not in a header + if (isHeaderMethod && !policyExt()->getOperationsAreInline()) + str += ';'; // terminate now + else + str +=getIndent() + " {\n\n" + getIndent() + '}'; // empty method body + + // write it out + writeDocumentation("", op->getDoc(), returnStr, cpp); + cpp << getIndent() << str << m_endl; + writeBlankLine(cpp); + } +} + +// To prevent circular including when both classifiers on either end +// of an association have roles we need to have forward declaration of +// the other class...but only IF its not THIS class (as could happen +// in self-association relationship). +void CppWriter::printAssociationIncludeDecl (UMLAssociationList list, Uml::IDType myId, QTextStream &stream) +{ + + for (UMLAssociation *a = list.first(); a; a = list.next()) { + UMLClassifier *current = NULL; + bool isFirstClass = true; + + // only use OTHER classes (e.g. we don't need to write includes for ourselves!! + // AND only IF the roleName is defined, otherwise, its not meant to be noticed. + if (a->getObjectId(Uml::A) == myId && !a->getRoleName(Uml::B).isEmpty()) { + current = dynamic_cast<UMLClassifier*>(a->getObject(Uml::B)); + } else if (a->getObjectId(Uml::B) == myId && !a->getRoleName(Uml::A).isEmpty()) { + current = dynamic_cast<UMLClassifier*>(a->getObject(Uml::A)); + isFirstClass = false; + } + + // as header doc for this method indicates, we need to be a bit sophisticated on + // how to declare some associations. + if( current ) + if( !isFirstClass && !a->getRoleName(Uml::A).isEmpty() && !a->getRoleName(Uml::B).isEmpty()) + stream << "class " << current->getName() << ";" << m_endl; // special case: use forward declaration + else + stream << "#include \"" << current->getName() << ".h\"" << m_endl; // just the include statement + } +} + +QString CppWriter::fixInitialStringDeclValue(const QString &value, const QString &type) +{ + QString val = value; + // check for strings only + if (!val.isEmpty() && type == policyExt()->getStringClassName()) { + if (!val.startsWith("\"")) + val.prepend("\""); + if (!val.endsWith("\"")) + val.append("\""); + } + return val; +} + +// methods like this _shouldn't_ be needed IF we properly did things thruought the code. +QString CppWriter::getUMLObjectName(UMLObject *obj) +{ + return(obj!=0)?obj->getName():QString("NULL"); +} + +void CppWriter::writeBlankLine(QTextStream &stream) +{ + stream << m_endl; +} + +void CppWriter::printTextAsSeparateLinesWithIndent (const QString &text, const QString &indent, QTextStream &stream) +{ + if(text.isEmpty()) + return; + + QStringList lines = QStringList::split( "\n", text); + for(uint i= 0; i < lines.count(); i++) + stream << indent << lines[i] << m_endl; +} + +QString CppWriter::getAttributeVariableName (UMLAttribute *at) +{ + QString fieldName = "m_" + cleanName(at->getName()); + return fieldName; +} + +QStringList CppWriter::defaultDatatypes() { + return Codegen_Utils::cppDatatypes(); +} + +const QStringList CppWriter::reservedKeywords() const { + return Codegen_Utils::reservedCppKeywords(); +} + |