summaryrefslogtreecommitdiffstats
path: root/umbrello/umbrello/codegenerators/tclwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'umbrello/umbrello/codegenerators/tclwriter.cpp')
-rw-r--r--umbrello/umbrello/codegenerators/tclwriter.cpp951
1 files changed, 951 insertions, 0 deletions
diff --git a/umbrello/umbrello/codegenerators/tclwriter.cpp b/umbrello/umbrello/codegenerators/tclwriter.cpp
new file mode 100644
index 00000000..d8607aa7
--- /dev/null
+++ b/umbrello/umbrello/codegenerators/tclwriter.cpp
@@ -0,0 +1,951 @@
+/***************************************************************************
+ 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 <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.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;
+ }
+ QFile fileh, filetcl;
+
+ // find an appropriate name for our file
+ QString 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, QFile & fileh)
+{
+ // open stream for writing
+ QTextStream stream(&fileh);
+ mStream = &stream;
+
+ // reset the indent level
+ m_indentLevel = 0;
+
+ // write header blurb
+ QString str = getHeadingFile(".tcl");
+ if (!str.isEmpty()) {
+ str.replace(QRegExp("%filename%"), classifierInfo->fileName);
+ str.replace(QRegExp("%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()) {
+ QString 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()) {
+ QString formalName = t->getName();
+ QString typeName = t->getTypeName();
+ writeCode(typeName + "# " + formalName);
+ }
+ }
+ // start my own class
+ writeCode("class " + mClass + " {");
+ m_indentLevel++;
+ if (classifierInfo->superclasses.count() > 0) {
+ QString 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, QFile & filetcl)
+{
+ // open stream for writing
+ QTextStream stream(&filetcl);
+ mStream = &stream;
+
+ // set the starting indentation at zero
+ m_indentLevel = 0;
+
+ //try to find a heading file (license, coments, etc)
+ QString str;
+ str = getHeadingFile(".tclbody");
+ if (!str.isEmpty()) {
+ str.replace(QRegExp("%filename%"), classifierInfo->fileName + "body");
+ str.replace(QRegExp("%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 QString &text)
+{
+ *mStream << getIndent() << text << m_endl;
+}
+
+void
+TclWriter::writeComm(const QString &text)
+{
+ QStringList lines = QStringList::split("\n", text, true);
+ for (uint i = 0; i < lines.count(); i++) {
+ *mStream << getIndent() << "# " << lines[i] << m_endl;
+ }
+}
+
+void
+TclWriter::writeDocu(const QString &text)
+{
+ QStringList lines = QStringList::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 QString &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)
+{
+ QString 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;
+
+ QString scope = visibility.toString();
+ QString 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
+ QString documentation;
+ for (UMLAttribute * at = list->first(); at; at = list->next()) {
+ documentation = at->getDoc();
+ QString varName = cleanName(at->getName());
+ QString 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 QString &/*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) {
+
+ QString 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) {
+ QString 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 QString &fieldClassName, const QString &roleName,
+ const QString &multi, const QString &doc, const QString &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(QRegExp("^[01]$"))) {
+ QString 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(QRegExp("^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 {
+ QString 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) {
+ QString 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)
+ QStringList::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++;
+ QString 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()) {
+ QString doc = "";
+ QString code = "";
+ QString methodReturnType = fixTypeName(op->getTypeName());
+ QString name = cleanName(op->getName());
+ QString 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++) {
+ QString typeName = fixTypeName(at->getTypeName());
+ QString 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()) {
+ QString code = "";
+ QString methodReturnType = fixTypeName(op->getTypeName());
+ QString 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++) {
+ QString 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()) {
+ QString 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) {
+
+ QString 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) {
+ QString 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 QString &fieldClassName,
+ const QString &roleName, const QString &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(QRegExp("^[01]$"))) {
+ QString 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 {
+ QString 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);
+}
+
+QString
+TclWriter::fixTypeName(const QString &string)
+{
+ if (string.isEmpty())
+ return "void";
+ return string;
+}
+
+// methods like this _shouldn't_ be needed IF we properly did things thruought the code.
+QString
+TclWriter::getUMLObjectName(UMLObject * obj)
+{
+ return (obj != 0) ? obj->getName() : QString("NULL");
+}
+
+const QStringList
+TclWriter::reservedKeywords() const
+{
+ static QStringList keywords;
+
+ if (keywords.isEmpty())
+ {
+ for (int i = 0; tclwords[i]; i++)
+ keywords.append(tclwords[i]);
+ }
+ return keywords;
+}
+