You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/umbrello/umbrello/codegenerators/cppwriter.cpp

1284 lines
48 KiB

/***************************************************************************
* 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 <tqfile.h>
#include <tqtextstream.h>
#include <tqregexp.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 = 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<CPPCodeGenerationPolicy*>(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.replace( 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.replace(TQRegExp("%filename%"),m_classifierInfo->fileName + ".h");
str.replace(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().replace(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.replace(TQRegExp("%filename%"),m_classifierInfo->fileName + ".cpp");
str.replace(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
<<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()) {
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.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 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.contains(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.contains(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.findIndex(fieldVarName) == -1 &&
multi.contains(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.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, 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.contains(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.replace(TQRegExp("%VARNAME%"),fieldVarName);
method.replace(TQRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName());
method.replace(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.replace(TQRegExp("%VARNAME%"),fieldVarName);
method.replace(TQRegExp("%VECTORTYPENAME%"), policyExt()->getVectorClassName());
method.replace(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.replace(TQRegExp("%VARNAME%"),fieldVarName);
method.replace(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.replace(TQRegExp("%VARNAME%"),fieldVarName);
method.replace(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<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
}
}
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();
}