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/tclwriter.cpp

952 lines
33 KiB

/***************************************************************************
begin : Thu Jul 26 2005
copyright : (C) 2005 by Rene Meyer
email : rene.meyer@sturmit.de
(C) 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 "tclwriter.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 "../umldoc.h"
#include "../classifier.h"
#include "../operation.h"
#include "../template.h"
#include "../umltemplatelist.h"
#include "../umlclassifierlistitemlist.h"
#include "../classifierlistitem.h"
#include "../model_utils.h"
static const char *tclwords[] = {
"body",
"break",
"case",
"class",
"common",
"concat",
"configbody",
"constructor",
"continue",
"default",
"destructor",
"else",
"elseif",
"for",
"foreach",
"global",
"if",
"incr",
"lappend",
"lindex",
"list",
"llength",
"load",
"lrange",
"lreplace",
"method",
"namespace",
"private",
"proc",
"protected",
"public",
"return",
"set",
"source",
"switch",
"then",
"upvar",
"variable",
"virtual",
"while",
0
};
TclWriter::TclWriter()
{
}
TclWriter::~TclWriter()
{
}
Uml::Programming_Language
TclWriter::getLanguage()
{
return Uml::pl_Tcl;
}
void
TclWriter::writeClass(UMLClassifier * c)
{
if (!c) {
kDebug() << "Cannot write class of NULL concept!\n";
return;
}
TQFile fileh, filetcl;
// find an appropriate name for our file
TQString fileName = findFileName(c, ".tcl");
if (fileName.isEmpty()) {
emit codeGenerated(c, false);
return;
}
if (!openFile(fileh, fileName)) {
emit codeGenerated(c, false);
return;
}
// preparations
classifierInfo = new ClassifierInfo(c);
classifierInfo->fileName = fileName;
classifierInfo->className = cleanName(c->getName());
mClass = cleanName(c->getName());
if (!c->getPackage().isEmpty()) {
mNamespace = "::" + cleanName(c->getPackage());
mClassGlobal = mNamespace + "::" + mClass;
} else {
mNamespace = "::";
mClassGlobal = "::" + mClass;
}
// 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 (!classifierInfo->isInterface) {
if (c->getBaseType() == Uml::ot_Enum)
need_impl = false;
}
if (need_impl) {
if (!openFile(filetcl, fileName + "body")) {
emit codeGenerated(c, false);
return;
}
// write Source file
writeSourceFile(c, filetcl);
filetcl.close();
}
// Wrap up: free classifierInfo, emit done code
classifierInfo = 0;
emit codeGenerated(c, true);
}
void
TclWriter::writeHeaderFile(UMLClassifier * c, TQFile & fileh)
{
// open stream for writing
TQTextStream stream(&fileh);
mStream = &stream;
// reset the indent level
m_indentLevel = 0;
// write header blurb
TQString str = getHeadingFile(".tcl");
if (!str.isEmpty()) {
str.replace(TQRegExp("%filename%"), classifierInfo->fileName);
str.replace(TQRegExp("%filepath%"), fileh.name());
writeCode(str);
}
// set current namespace
writeCode("namespace eval " + mNamespace + " {");
m_indentLevel++;
// check on already existing
writeComm("Do not load twice");
writeCode("if {[namespace exist " + mClass + "]} return");
// source used superclass files
UMLClassifierList superclasses = classifierInfo->superclasses;
if (superclasses.count() > 0) {
writeComm
("Source found and used class files and import class command if necessary");
for (UMLClassifier * classifier = superclasses.first(); classifier;
classifier = superclasses.next()) {
writeUse(classifier);
}
}
// write all "source" we need to include other classes, that arent us.
if (classifierInfo->hasAssociations) {
writeAssociationIncl(classifierInfo->plainAssociations, c->getID(),
"Associations");
writeAssociationIncl(classifierInfo->aggregations, c->getID(),
"Aggregations");
writeAssociationIncl(classifierInfo->compositions, c->getID(),
"Compositions");
}
//Write class Documentation
writeDocu("\n@class\t" + mClass + m_endl + c->getDoc());
//check if class is abstract and / or has abstract methods
if ((c->getAbstract() || classifierInfo->isInterface)
&& !hasAbstractOps(c)) {
writeComm("TODO abstract class" + mClass +
"\nInherit from it and create only objects from the derived classes");
}
// check on enum classes
if (!classifierInfo->isInterface) {
// use tcl-list for enum's
if (c->getBaseType() == Uml::ot_Enum) {
UMLClassifierListItemList litList =
c->getFilteredList(Uml::ot_EnumLiteral);
writeCode("set enum_" + mClass + " [list\\");
m_indentLevel++;
for (UMLClassifierListItem * lit = litList.first(); lit;
lit = litList.next()) {
TQString enumLiteral = cleanName(lit->getName());
writeCode(enumLiteral + "\\");
}
m_indentLevel--;
writeCode("];# end of enum");
m_indentLevel--;
writeCode("};# end of namespace");
return;
}
}
// Generate template parameters.
UMLTemplateList template_params = c->getTemplateList();
if (template_params.count()) {
writeCode("#TODO template<");
for (UMLTemplate * t = template_params.first(); t;
t = template_params.next()) {
TQString formalName = t->getName();
TQString typeName = t->getTypeName();
writeCode(typeName + "# " + formalName);
}
}
// start my own class
writeCode("class " + mClass + " {");
m_indentLevel++;
if (classifierInfo->superclasses.count() > 0) {
TQString code = "inherit";
for (UMLClassifier * superClass = classifierInfo->superclasses.first();
superClass; superClass = classifierInfo->superclasses.next()) {
/*
if (superClass->getAbstract() || superClass->isInterface())
stream << getIndent() << "virtual ";
*/
if (superClass->getPackage().isEmpty()) {
code += " ::" + cleanName(superClass->getName());
} else {
code +=
" ::" + cleanName(superClass->getPackage()) + "::" +
cleanName(superClass->getName());
}
}
writeCode(code);
}
//
//declarations of operations
//
// write out field and operations decl grouped by visibility
//
// PUBLIC attribs/methods
// for public: constructors are first ops we print out
if (!classifierInfo->isInterface) {
writeConstructorHeader();
writeDestructorHeader();
}
// attributes
writeAttributeDecl(Uml::Visibility::Public, true); // write static attributes first
writeAttributeDecl(Uml::Visibility::Public, false);
// associations
writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Public,
c->getID(), "Associations");
writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Public, c->getID(),
"Aggregations");
writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Public, c->getID(),
"Compositions");
//TODO writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, stream);
writeOperationHeader(c, Uml::Visibility::Public);
// PROTECTED attribs/methods
//
// attributes
writeAttributeDecl(Uml::Visibility::Protected, true); // write static attributes first
writeAttributeDecl(Uml::Visibility::Protected, false);
// associations
writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Protected,
c->getID(), "Association");
writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Protected,
c->getID(), "Aggregation");
writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Protected,
c->getID(), "Composition");
//TODO writeHeaderAccessorMethodDecl(c, Uml::Visibility::Protected, stream);
writeOperationHeader(c, Uml::Visibility::Protected);
// PRIVATE attribs/methods
//
// attributes
writeAttributeDecl(Uml::Visibility::Private, true); // write static attributes first
writeAttributeDecl(Uml::Visibility::Private, false);
// associations
writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Private,
c->getID(), "Associations");
writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Private, c->getID(),
"Aggregations");
writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Private, c->getID(),
"Compositions");
//TODO writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, stream);
writeOperationHeader(c, Uml::Visibility::Private);
writeInitAttributeHeader(); // this is always private, used by constructors to initialize class
// end of class header
m_indentLevel--;
writeCode("};# end of class");
// end of class namespace, if any
m_indentLevel--;
writeCode("};# end of namespace");
}
void
TclWriter::writeSourceFile(UMLClassifier * c, TQFile & filetcl)
{
// open stream for writing
TQTextStream stream(&filetcl);
mStream = &stream;
// set the starting indentation at zero
m_indentLevel = 0;
//try to find a heading file (license, coments, etc)
TQString str;
str = getHeadingFile(".tclbody");
if (!str.isEmpty()) {
str.replace(TQRegExp("%filename%"), classifierInfo->fileName + "body");
str.replace(TQRegExp("%filepath%"), filetcl.name());
writeCode(str);
}
// Start body of class
// constructors are first ops we print out
if (!classifierInfo->isInterface) {
writeConstructorSource();
writeDestructorSource();
}
// Public attributes have in tcl a configbody method
writeAttributeSource();
// Association access functions
writeAssociationSource(classifierInfo->plainAssociations, c->getID());
writeAssociationSource(classifierInfo->aggregations, c->getID());
writeAssociationSource(classifierInfo->compositions, c->getID());
// Procedures and methods
writeOperationSource(c, Uml::Visibility::Public);
writeOperationSource(c, Uml::Visibility::Protected);
writeOperationSource(c, Uml::Visibility::Private);
// Yep, bringing up the back of the bus, our initialization method for attributes
writeInitAttributeSource();
}
void
TclWriter::writeCode(const TQString &text)
{
*mStream << getIndent() << text << m_endl;
}
void
TclWriter::writeComm(const TQString &text)
{
TQStringList lines = TQStringList::split("\n", text, true);
for (uint i = 0; i < lines.count(); i++) {
*mStream << getIndent() << "# " << lines[i] << m_endl;
}
}
void
TclWriter::writeDocu(const TQString &text)
{
TQStringList lines = TQStringList::split("\n", text, true);
for (uint i = 0; i < lines.count(); i++) {
*mStream << getIndent() << "## " << lines[i] << m_endl;
}
}
// 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
TclWriter::writeAssociationIncl(UMLAssociationList list, Uml::IDType myId,
const TQString &type)
{
for (UMLAssociation * a = list.first(); a; a = list.next()) {
UMLClassifier *classifier = NULL;
writeComm(m_endl + type + m_endl + a->toString() + m_endl + a->getDoc());
// 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()) {
classifier = dynamic_cast < UMLClassifier * >(a->getObject(Uml::B));
writeUse(classifier);
} else if (a->getObjectId(Uml::B) == myId
&& !a->getRoleName(Uml::A).isEmpty()) {
classifier = dynamic_cast < UMLClassifier * >(a->getObject(Uml::A));
if (classifier->getPackage().isEmpty())
writeCode("namespace eval " + cleanName(classifier->getName()) +
" {}");
} else {
// CHECK: This crashes (classifier still NULL from above)
/*
writeCode("namespace eval " + cleanName(classifier->getPackage()) +
"::" + cleanName(classifier->getName()) + " {}");
*/
}
}
}
void
TclWriter::writeUse(UMLClassifier * c)
{
TQString myNs;
if (!c->getPackage().isEmpty()) {
myNs = cleanName(c->getPackage());
} else {
myNs = "";
}
// if different package
if (("::"+myNs) != mNamespace) {
if (c->getPackage().isEmpty()) {
writeCode("source " + findFileName(c, ".tcl"));
writeCode("namespace import ::" + cleanName(c->getName()));
} else {
writeCode("package require " + myNs);
writeCode("namespace import ::" + myNs + "::" +
cleanName(c->getName()));
}
} else {
// source the file
writeCode("source " + findFileName(c, ".tcl"));
}
}
void
TclWriter::writeConstructorHeader()
{
writeDocu
(m_endl + "@func constructor" + m_endl +
"@par args contain all configuration parameters" + m_endl);
writeCode("constructor {args} {}" + m_endl);
}
void
TclWriter::writeConstructorSource()
{
writeComm(mClassGlobal + "::constructor");
writeCode(mClassGlobal + "::constructor {args} {");
m_indentLevel++;
if (classifierInfo->hasAttributes) {
writeCode("initAttributes");
}
writeCode("eval configure $args");
m_indentLevel--;
writeCode('}' + m_endl);
}
void
TclWriter::writeDestructorHeader()
{
writeDocu(m_endl + "@func destructor" + m_endl);
writeCode("destructor {} {}");
}
void
TclWriter::writeDestructorSource()
{
writeComm(mClassGlobal + "::destructor");
writeCode(mClassGlobal + "::destructor {} {" + m_endl + '}' + m_endl);
}
void
TclWriter::writeAttributeDecl(Uml::Visibility visibility, bool writeStatic)
{
if (classifierInfo->isInterface)
return;
TQString scope = visibility.toString();
TQString type;
if (writeStatic) {
type = "common";
} else {
type = "variable";
}
UMLAttributeList *list = NULL;
switch (visibility) {
case Uml::Visibility::Private:
if (writeStatic) {
list = &(classifierInfo->static_atpriv);
} else {
list = &(classifierInfo->atpriv);
}
break;
case Uml::Visibility::Protected:
if (writeStatic) {
list = &(classifierInfo->static_atprot);
} else {
list = &(classifierInfo->atprot);
}
break;
case Uml::Visibility::Public:
if (writeStatic) {
list = &(classifierInfo->static_atpub);
} else {
list = &(classifierInfo->atpub);
}
break;
default:
break;
}
if (list && list->count() > 0) {
writeComm(m_endl + scope + ' ' + type + " attributes" + m_endl);
// write attrib declarations now
TQString documentation;
for (UMLAttribute * at = list->first(); at; at = list->next()) {
documentation = at->getDoc();
TQString varName = cleanName(at->getName());
TQString typeName = fixTypeName(at->getTypeName());
writeDocu(m_endl + "@var " + scope + ' ' + type + ' ' + typeName + ' ' +
varName + m_endl + documentation);
writeCode(scope + ' ' + type + ' ' + varName + m_endl);
}
}
}
void
TclWriter::writeAssociationDecl(UMLAssociationList associations,
Uml::Visibility permitScope, Uml::IDType id,
const TQString &/*type*/)
{
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 (!)
// 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),
permitScope.toString());
}
// 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),
permitScope.toString());
}
// reset for next association in our loop
printRoleA = false;
printRoleB = false;
}
}
}
void
TclWriter::writeAssociationRoleDecl(const TQString &fieldClassName, const TQString &roleName,
const TQString &multi, const TQString &doc, const TQString &scope)
{
// ONLY write out IF there is a rolename given
// otherwise its not meant to be declared in the code
if (roleName.isEmpty())
return;
// 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 = 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);
}
writeDocu(m_endl + "@var " + scope + " variable <" + fieldClassName +
"> " + fieldVarName + m_endl + doc);
writeCode(scope + " variable " + fieldVarName + m_endl);
} else {
TQString fieldVarName = roleName.lower();
// record unique occurrences for later when we want to check
// for initialization of this vector
if (VectorFieldVariables.findIndex(fieldVarName) == -1)
VectorFieldVariables.append(fieldVarName);
writeDocu(m_endl + "@var" + scope + " variable <" + fieldClassName +
"*> " + fieldVarName + m_endl + doc);
writeCode(scope + " variable " + fieldVarName + m_endl);
}
}
void
TclWriter::writeInitAttributeHeader()
{
if (classifierInfo->hasAttributes) {
writeDocu("@method private initAttributes" + m_endl +
"Initialize all internal variables");
writeCode("private method initAttributes {}");
}
}
void
TclWriter::writeInitAttributeSource()
{
// only need to do this under certain conditions
if (classifierInfo->hasAttributes) {
TQString varName;
writeComm(mClassGlobal + "::initAttributes");
writeCode("body " + mClassGlobal + "::initAttributes {} {");
m_indentLevel++;
// first, initiation of fields derived from attributes
UMLAttributeList atl = classifierInfo->getAttList();
for (UMLAttribute * at = atl.first(); at; at = atl.next()) {
if (!at->getInitialValue().isEmpty()) {
varName = cleanName(at->getName());
writeCode("set " + varName + ' ' + at->getInitialValue());
}
}
// Now initialize the association related fields (e.g. vectors)
TQStringList::Iterator it;
for (it = VectorFieldVariables.begin();
it != VectorFieldVariables.end(); ++it) {
varName = *it;
writeCode("set " + varName + " [list]");
}
for (it = ObjectFieldVariables.begin();
it != ObjectFieldVariables.end(); ++it) {
varName = *it;
it++;
TQString fieldClassName = *it;
writeCode("set " + varName + " [list]");
}
// clean up
ObjectFieldVariables.clear(); // shouldn't be needed?
VectorFieldVariables.clear(); // shouldn't be needed?
m_indentLevel--;
writeCode('}' + m_endl);
}
}
void
TclWriter::writeOperationHeader(UMLClassifier * c, Uml::Visibility permitScope)
{
UMLOperationList oplist;
UMLOperation *op;
UMLAttribute *at;
int j;
//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()) {
switch (op->getVisibility()) {
case Uml::Visibility::Public:
if (permitScope == Uml::Visibility::Public)
oplist.append(op);
break;
case Uml::Visibility::Protected:
if (permitScope == Uml::Visibility::Protected)
oplist.append(op);
break;
case Uml::Visibility::Private:
if (permitScope == Uml::Visibility::Private)
oplist.append(op);
break;
default:
break;
}
}
// generate method decl for each operation given
if (oplist.count() > 0) {
writeComm("Operations");
}
for (op = oplist.first(); op; op = oplist.next()) {
TQString doc = "";
TQString code = "";
TQString methodReturnType = fixTypeName(op->getTypeName());
TQString name = cleanName(op->getName());
TQString scope = permitScope.toString();
if (op->getAbstract() || classifierInfo->isInterface) {
//TODO declare abstract method as 'virtual'
// str += "virtual ";
}
// declaration for header file
if (op->getStatic()) {
doc = m_endl + "@fn " + scope + " proc " + name + m_endl;
code = scope + " proc " + name + " {";
} else {
doc = m_endl + "@fn " + scope + " method " + name + m_endl;
code = scope + " method " + name + " {";
}
// method parameters
UMLAttributeList atl = op->getParmList();
j = 0;
for (at = atl.first(); at; at = atl.next(), j++) {
TQString typeName = fixTypeName(at->getTypeName());
TQString atName = cleanName(at->getName());
if (at->getInitialValue().isEmpty()) {
doc +=
"@param " + typeName + ' ' + atName + m_endl + at->getDoc() +
m_endl;
code += ' ' + atName;
} else {
doc +=
"@param " + typeName + ' ' + atName + " (default=" +
at->getInitialValue() + ") " + m_endl + at->getDoc() + m_endl;
code += " {" + atName + ' ' + at->getInitialValue() + "} ";
}
}
if (methodReturnType != "void") {
doc += "@return " + methodReturnType + m_endl;
}
writeDocu(doc + op->getDoc());
writeCode(code + "} {}" + m_endl);
}
}
void
TclWriter::writeOperationSource(UMLClassifier * c, Uml::Visibility permitScope)
{
UMLOperationList oplist;
UMLOperation *op;
UMLAttribute *at;
int j;
//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()) {
switch (op->getVisibility()) {
case Uml::Visibility::Public:
if (permitScope == Uml::Visibility::Public)
oplist.append(op);
break;
case Uml::Visibility::Protected:
if (permitScope == Uml::Visibility::Protected)
oplist.append(op);
break;
case Uml::Visibility::Private:
if (permitScope == Uml::Visibility::Private)
oplist.append(op);
break;
default:
break;
}
}
// generate source for each operation given
for (op = oplist.first(); op; op = oplist.next()) {
TQString code = "";
TQString methodReturnType = fixTypeName(op->getTypeName());
TQString name;
// no code needed
if (op->getAbstract() || classifierInfo->isInterface) {
continue;
}
name = mClassGlobal + "::" + cleanName(op->getName());
writeComm(name);
code = "body " + name + " {";
// parameters
UMLAttributeList atl = op->getParmList();
j = 0;
for (at = atl.first(); at; at = atl.next(), j++) {
TQString atName = cleanName(at->getName());
if (at->getInitialValue().isEmpty()) {
code += ' ' + atName;
} else {
code += " {" + atName + ' ' + at->getInitialValue() + "} ";
}
}
writeCode(code += "} {");
m_indentLevel++;
if (methodReturnType != "void") {
writeCode("return " + methodReturnType);
} else {
writeCode("return");
}
m_indentLevel--;
writeCode('}' + m_endl);
}
}
void
TclWriter::writeAttributeSource()
{
UMLAttributeList *list = &(classifierInfo->atpub);
UMLAttribute *at;
for (at = list->first(); at; at = list->next()) {
TQString name = mClassGlobal + "::" + cleanName(at->getName());
writeComm(name);
writeCode("configbody " + name + " {} {" + m_endl + '}' + m_endl);
}
}
void
TclWriter::writeAssociationSource(UMLAssociationList associations,
Uml::IDType id)
{
if (associations.isEmpty()) {
return;
}
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;
// print RoleB source
if (printRoleB && a->getVisibility(Uml::B) == Uml::Visibility::Public) {
TQString fieldClassName =
cleanName(getUMLObjectName(a->getObject(Uml::B)));
writeAssociationRoleSource(fieldClassName, a->getRoleName(Uml::B),
a->getMulti(Uml::B));
}
// print RoleA source
if (printRoleA && a->getVisibility(Uml::A) == Uml::Visibility::Public) {
TQString fieldClassName =
cleanName(getUMLObjectName(a->getObject(Uml::A)));
writeAssociationRoleSource(fieldClassName, a->getRoleName(Uml::A),
a->getMulti(Uml::A));
}
// reset for next association in our loop
printRoleA = false;
printRoleB = false;
}
}
void
TclWriter::writeAssociationRoleSource(const TQString &fieldClassName,
const TQString &roleName, const TQString &multi)
{
// ONLY write out IF there is a rolename given
// otherwise its not meant to be declared in the code
if (roleName.isEmpty())
return;
// 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 = roleName.lower();
writeCode("configbody " + mClassGlobal + "::" + fieldVarName + " {} {");
m_indentLevel++;
writeCode("if {![$" + fieldVarName + " isa " + fieldClassName + "]} {");
m_indentLevel++;
writeCode("return -code error \"expected object of class: " +
fieldClassName + "\"");
m_indentLevel--;
writeCode("}");
m_indentLevel--;
} else {
TQString fieldVarName = roleName.lower();
writeCode("configbody " + mClassGlobal + "::" + fieldVarName + " {} {");
m_indentLevel++;
writeCode("foreach myObj $" + fieldVarName + " {");
m_indentLevel++;
writeCode("if {![$myObj isa " + fieldClassName + "]} {");
m_indentLevel++;
writeCode("return -code error \"expected object of class: " +
fieldClassName + "\"");
m_indentLevel--;
writeCode("}");
m_indentLevel--;
writeCode("}");
m_indentLevel--;
}
writeCode('}' + m_endl);
}
TQString
TclWriter::fixTypeName(const TQString &string)
{
if (string.isEmpty())
return "void";
return string;
}
// methods like this _shouldn't_ be needed IF we properly did things thruought the code.
TQString
TclWriter::getUMLObjectName(UMLObject * obj)
{
return (obj != 0) ? obj->getName() : TQString("NULL");
}
const TQStringList
TclWriter::reservedKeywords() const
{
static TQStringList keywords;
if (keywords.isEmpty())
{
for (int i = 0; tclwords[i]; i++)
keywords.append(tclwords[i]);
}
return keywords;
}