/*************************************************************************** * 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 * * * *************************************************************************** * * * 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 #include #include #include // 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 = TQString(); // nothing to be done /* VECTOR_METHOD_APPEND = "%VARNAME%.append(&add_object);"; // TQt lib implementation VECTOR_METHOD_REMOVE = "%VARNAME%.removeRef(&remove_object);"; // TQt lib implementation VECTOR_METHOD_INIT = "%VARNAME%.setAutoDelete(false);"; // TQt library */ OBJECT_METHOD_INIT = "%VARNAME% = new %ITEMCLASS%( );"; // TQt 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(UMLApp::app()->getPolicyExt()); } void CppWriter::writeClass(UMLClassifier *c) { if (!c) { kDebug() << "Cannot write class of NULL concept!\n"; return; } TQFile fileh, filecpp; // find an appropriate name for our file TQString 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.tqreplace( TQRegExp(".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, TQFile &fileh) { // open stream for writing TQTextStream h (&fileh); // up the indent level to one m_indentLevel = 1; // write header blurb TQString str = getHeadingFile(".h"); if(!str.isEmpty()) { str.tqreplace(TQRegExp("%filename%"),m_classifierInfo->fileName + ".h"); str.tqreplace(TQRegExp("%filepath%"),fileh.name()); h << str<< m_endl; } // Write the hash define stuff to prevent multiple parsing/inclusion of header TQString hashDefine = m_classifierInfo->className.upper().simplifyWhiteSpace().tqreplace(TQRegExp(" "), "_"); 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, TQTextStream &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, TQTextStream &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, TQFile &filecpp ) { // open stream for writing TQTextStream cpp (&filecpp); // set the starting indentation at zero m_indentLevel = 0; //try to find a heading file (license, coments, etc) TQString str; str = getHeadingFile(".cpp"); if(!str.isEmpty()) { str.tqreplace(TQRegExp("%filename%"),m_classifierInfo->fileName + ".cpp"); str.tqreplace(TQRegExp("%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 TQString 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, TQTextStream &cpp) { UMLClassifierList superclasses = m_classifierInfo->superclasses; for(UMLClassifier *classifier = superclasses.first(); classifier ;classifier = superclasses.next()) { TQString 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 <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()) { TQString 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; ) { TQString formalName = t->getName(); TQString 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, TQTextStream &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) { TQString strVis = Codegen_Utils::capitalizeFirstLetter(visibility.toString()); TQString 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; TQString 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; TQString varName = getAttributeVariableName(at); TQString staticValue = at->getStatic() ? "static " : ""; TQString 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, TQTextStream &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, TQTextStream &stream) { if (!policyExt()->getAutoGenerateAccessors()) return; if (forceDoc() || attribs->count() > 0) { TQString strVis = Codegen_Utils::capitalizeFirstLetter(visibility.toString()); TQString 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()) { TQString varName = getAttributeVariableName(at); TQString 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.tqreplace(0,1,methodBaseName.tqat(0).upper()); writeSingleAttributeAccessorMethods(at->getTypeName(), varName, methodBaseName, at->getDoc(), Uml::chg_Changeable, isHeaderMethod, at->getStatic(), writeMethodBody, stream); } } void CppWriter::writeComment(const TQString &comment, const TQString &myIndent, TQTextStream &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.tqcontains(TQRegExp("\n"))) { TQStringList lines = TQStringList::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(TQString header, TQString body, TQString end, TQTextStream &cpp) { writeBlankLine(cpp); TQString indent = getIndent(); cpp << indent << "/**" << m_endl; if (!header.isEmpty()) cpp << formatDoc(header, indent + " * "); if (!body.isEmpty()) cpp << formatDoc(body, indent + " * "); if (!end.isEmpty()) { TQStringList lines = TQStringList::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, TQTextStream &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) { TQString 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) { TQString 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(TQString fieldClassName, TQString roleName, TQString multi, TQString doc, TQTextStream &stream) { // ONLY write out IF there is a rolename given // otherwise its not meant to be declared in the code if (roleName.isEmpty()) return; TQString 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.tqcontains(TQRegExp("^[01]$"))) { TQString fieldVarName = "m_" + roleName.lower(); // record this for later consideration of initialization IF the // multi value requires 1 of these objects if(ObjectFieldVariables.tqfindIndex(fieldVarName) == -1 && multi.tqcontains(TQRegExp("^1$")) ) { // ugh. UGLY. Storing variable name and its class in pairs. ObjectFieldVariables.append(fieldVarName); ObjectFieldVariables.append(fieldClassName); } stream << indent << fieldClassName << " * " << fieldVarName << ";" << m_endl; } else { TQString fieldVarName = "m_" + roleName.lower() + "Vector"; // record unique occurrences for later when we want to check // for initialization of this vector if(VectorFieldVariables.tqfindIndex(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, TQTextStream &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()) { TQString 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()) { TQString 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 TQString &fieldClassName, bool isHeaderMethod, bool writeMethodBody, const TQString &roleName, const TQString &multi, const TQString &description, Uml::Changeability_Type change, TQTextStream &stream) { if(multi.isEmpty() || multi.tqcontains(TQRegExp("^[01]$"))) { TQString fieldVarName = "m_" + roleName.lower(); writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, change, isHeaderMethod, false, writeMethodBody, stream); } else { TQString fieldVarName = "m_" + roleName.lower() + "Vector"; writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName, description, change, isHeaderMethod, writeMethodBody, stream); } } void CppWriter::writeVectorAttributeAccessorMethods ( const TQString &fieldClassName, const TQString &fieldVarName, const TQString &fieldName, const TQString &description, Uml::Changeability_Type changeType, bool isHeaderMethod, bool writeMethodBody, TQTextStream &stream) { TQString className = fixTypeName(fieldClassName); TQString fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); TQString 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) { TQString method = VECTOR_METHOD_APPEND; method.tqreplace(TQRegExp("%VARNAME%"),fieldVarName); method.tqreplace(TQRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); method.tqreplace(TQRegExp("%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) { TQString method = VECTOR_METHOD_REMOVE; method.tqreplace(TQRegExp("%VARNAME%"),fieldVarName); method.tqreplace(TQRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); method.tqreplace(TQRegExp("%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 TQString 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 TQString& fieldClassName, const TQString& fieldVarName, const TQString& fieldName, const TQString &description, Uml::Changeability_Type change, bool isHeaderMethod, bool isStatic, bool writeMethodBody, TQTextStream &stream) { // DON'T write this IF its a source method AND writeMethodBody is "false" if(!isHeaderMethod && !writeMethodBody) return; TQString className = fixTypeName(fieldClassName); TQString fldName = Codegen_Utils::capitalizeFirstLetter(fieldName); TQString 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(TQTextStream &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 (TQTextStream &stream) { if (UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors() && m_classifierInfo->hasAttributes) stream << getIndent() << "void initAttributes ( ) ;" << m_endl; } void CppWriter::writeInitAttibuteMethod (TQTextStream &stream) { // only need to do this under certain conditions if (!UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors() || !m_classifierInfo->hasAttributes) return; TQString className = m_classifierInfo->className; TQString 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()) { TQString varName = getAttributeVariableName(at); stream << getIndent() << varName << " = " << at->getInitialValue() << ";" << m_endl; } } // Now initialize the association related fields (e.g. vectors) if (!VECTOR_METHOD_INIT.isEmpty()) { TQStringList::Iterator it; for( it = VectorFieldVariables.begin(); it != VectorFieldVariables.end(); ++it ) { TQString fieldVarName = *it; TQString method = VECTOR_METHOD_INIT; method.tqreplace(TQRegExp("%VARNAME%"),fieldVarName); method.tqreplace(TQRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName()); stream << getIndent() << method << m_endl; } } if (!OBJECT_METHOD_INIT.isEmpty()) { TQStringList::Iterator it; for( it = ObjectFieldVariables.begin(); it != ObjectFieldVariables.end(); ++it ) { TQString fieldVarName = *it; it++; TQString fieldClassName = *it; TQString method = OBJECT_METHOD_INIT; method.tqreplace(TQRegExp("%VARNAME%"),fieldVarName); method.tqreplace(TQRegExp("%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(TQTextStream &stream) { const bool generateEmptyConstructors = UMLApp::app()->getCommonPolicy()->getAutoGenerateConstructors(); if (forceDoc() || generateEmptyConstructors) { writeComment("Constructors/Destructors", getIndent(), stream); writeComment(" ", getIndent(), stream); writeBlankLine(stream); } if (!generateEmptyConstructors) return; TQString className = m_classifierInfo->className; // empty constructor TQString 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). TQString CppWriter::fixTypeName(const TQString &string) { if (string.isEmpty()) return "void"; if (string == "string") return policyExt()->getStringClassName(); return string; } void CppWriter::writeOperations(UMLClassifier *c, bool isHeaderMethod, Uml::Visibility permitScope, TQTextStream &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, TQTextStream &cpp) { TQString 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()) { TQString returnStr; // buffer for documentation TQString 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'; } TQString 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++) { TQString typeName = fixTypeName(at->getTypeName()); TQString atName = cleanName(at->getName()); str += typeName + ' ' + atName; const TQString 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, TQTextStream &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(a->getObject(Uml::B)); } else if (a->getObjectId(Uml::B) == myId && !a->getRoleName(Uml::A).isEmpty()) { current = dynamic_cast(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 } } TQString CppWriter::fixInitialStringDeclValue(const TQString &value, const TQString &type) { TQString 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. TQString CppWriter::getUMLObjectName(UMLObject *obj) { return(obj!=0)?obj->getName():TQString("NULL"); } void CppWriter::writeBlankLine(TQTextStream &stream) { stream << m_endl; } void CppWriter::printTextAsSeparateLinesWithIndent (const TQString &text, const TQString &indent, TQTextStream &stream) { if(text.isEmpty()) return; TQStringList lines = TQStringList::split( "\n", text); for(uint i= 0; i < lines.count(); i++) stream << indent << lines[i] << m_endl; } TQString CppWriter::getAttributeVariableName (UMLAttribute *at) { TQString fieldName = "m_" + cleanName(at->getName()); return fieldName; } TQStringList CppWriter::defaultDatatypes() { return Codegen_Utils::cppDatatypes(); } const TQStringList CppWriter::reservedKeywords() const { return Codegen_Utils::reservedCppKeywords(); }