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.
952 lines
33 KiB
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;
|
|
}
|
|
|