umbrello API Documentation

tclwriter.cpp

00001 /***************************************************************************
00002     begin               : Thu Jul 26 2005
00003     copyright           : (C) 2005 by Rene Meyer
00004     email               : rene.meyer@sturmit.de
00005       (C) 2006  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>
00006  ***************************************************************************/
00007 
00008 /***************************************************************************
00009  *                                                                       *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU General Public License as published by  *
00012  *   the Free Software Foundation; either version 2 of the License, or     *
00013  *   (at your option) any later version.                                   *
00014  *                                                                       *
00015  ***************************************************************************/
00016 
00017 // own header
00018 #include "tclwriter.h"
00019 // qt/kde includes
00020 #include <qfile.h>
00021 #include <qtextstream.h>
00022 #include <qregexp.h>
00023 #include <kdebug.h>
00024 // app includes
00025 #include "classifierinfo.h"
00026 #include "codegen_utils.h"
00027 #include "../umldoc.h"
00028 #include "../classifier.h"
00029 #include "../operation.h"
00030 #include "../template.h"
00031 #include "../umltemplatelist.h"
00032 #include "../umlclassifierlistitemlist.h"
00033 #include "../classifierlistitem.h"
00034 #include "../model_utils.h"
00035 
00036 static const char *tclwords[] = {
00037                                  "body",
00038                                  "break",
00039                                  "case",
00040                                  "class",
00041                                  "common",
00042                                  "concat",
00043                                  "configbody",
00044                                  "constructor",
00045                                  "continue",
00046                                  "default",
00047                                  "destructor",
00048                                  "else",
00049                                  "elseif",
00050                                  "for",
00051                                  "foreach",
00052                                  "global",
00053                                  "if",
00054                                  "incr",
00055                                  "lappend",
00056                                  "lindex",
00057                                  "list",
00058                                  "llength",
00059                                  "load",
00060                                  "lrange",
00061                                  "lreplace",
00062                                  "method",
00063                                  "namespace",
00064                                  "private",
00065                                  "proc",
00066                                  "protected",
00067                                  "public",
00068                                  "return",
00069                                  "set",
00070                                  "source",
00071                                  "switch",
00072                                  "then",
00073                                  "upvar",
00074                                  "variable",
00075                                  "virtual",
00076                                  "while",
00077                                  0
00078                              };
00079 
00080 TclWriter::TclWriter()
00081 {
00082 }
00083 
00084 TclWriter::~TclWriter()
00085 {
00086 }
00087 
00088 Uml::Programming_Language
00089 TclWriter::getLanguage()
00090 {
00091     return Uml::pl_Tcl;
00092 }
00093 
00094 void
00095 TclWriter::writeClass(UMLClassifier * c)
00096 {
00097 
00098     if (!c) {
00099         kDebug() << "Cannot write class of NULL concept!\n";
00100         return;
00101     }
00102     QFile           fileh, filetcl;
00103 
00104     // find an appropriate name for our file
00105     QString         fileName = findFileName(c, ".tcl");
00106     if (fileName.isEmpty()) {
00107         emit            codeGenerated(c, false);
00108         return;
00109     }
00110 
00111     if (!openFile(fileh, fileName)) {
00112         emit            codeGenerated(c, false);
00113         return;
00114     }
00115     // preparations
00116     classifierInfo = new ClassifierInfo(c, m_doc);
00117     classifierInfo->fileName = fileName;
00118     classifierInfo->className = cleanName(c->getName());
00119     mClass = cleanName(c->getName());
00120     if (!c->getPackage().isEmpty()) {
00121         mNamespace = "::" + cleanName(c->getPackage());
00122         mClassGlobal = mNamespace + "::" + mClass;
00123     } else {
00124         mNamespace = "::";
00125         mClassGlobal = "::" + mClass;
00126     }
00127 
00128     // write Header file
00129     writeHeaderFile(c, fileh);
00130     fileh.close();
00131 
00132     // Determine whether the implementation file is required.
00133     // (It is not required if the class is an enumeration.)
00134     bool            need_impl = true;
00135     if (!classifierInfo->isInterface) {
00136         if (c->getBaseType() == Uml::ot_Enum)
00137             need_impl = false;
00138     }
00139     if (need_impl) {
00140         if (!openFile(filetcl, fileName + "body")) {
00141             emit            codeGenerated(c, false);
00142             return;
00143         }
00144         // write Source file
00145         writeSourceFile(c, filetcl);
00146         filetcl.close();
00147     }
00148     // Wrap up: free classifierInfo, emit done code
00149     classifierInfo = 0;
00150 
00151     emit            codeGenerated(c, true);
00152 
00153 }
00154 
00155 void
00156 TclWriter::writeHeaderFile(UMLClassifier * c, QFile & fileh)
00157 {
00158     // open stream for writing
00159     QTextStream     stream(&fileh);
00160     mStream = &stream;
00161 
00162     // reset the indent level
00163     m_indentLevel = 0;
00164 
00165     // write header blurb
00166     QString         str = getHeadingFile(".tcl");
00167     if (!str.isEmpty()) {
00168         str.replace(QRegExp("%filename%"), classifierInfo->fileName);
00169         str.replace(QRegExp("%filepath%"), fileh.name());
00170         writeCode(str);
00171     }
00172     // set current namespace
00173     writeCode("namespace eval " + mNamespace + " {");
00174     m_indentLevel++;
00175 
00176     // check on already existing
00177     writeComm("Do not load twice");
00178     writeCode("if {[namespace exist " + mClass + "]} return");
00179 
00180     // source used superclass files
00181     UMLClassifierList superclasses = classifierInfo->superclasses;
00182     if (superclasses.count() > 0) {
00183         writeComm
00184         ("Source found and used class files and import class command if necessary");
00185 
00186         for (UMLClassifier * classifier = superclasses.first(); classifier;
00187                 classifier = superclasses.next()) {
00188             writeUse(classifier);
00189         }
00190     }
00191     // write all "source" we need to include other classes, that arent us.
00192     if (classifierInfo->hasAssociations) {
00193         writeAssociationIncl(classifierInfo->plainAssociations, c->getID(),
00194                              "Associations");
00195         writeAssociationIncl(classifierInfo->aggregations, c->getID(),
00196                              "Aggregations");
00197         writeAssociationIncl(classifierInfo->compositions, c->getID(),
00198                              "Compositions");
00199     }
00200     //Write class Documentation
00201     writeDocu("\n@class\t" + mClass + m_endl + c->getDoc());
00202 
00203     //check if class is abstract and / or has abstract methods
00204     if ((c->getAbstract() || classifierInfo->isInterface)
00205             && !hasAbstractOps(c)) {
00206         writeComm("TODO abstract class" + mClass +
00207                   "\nInherit from it and create only objects from the derived classes");
00208     }
00209     // check on enum classes
00210     if (!classifierInfo->isInterface) {
00211         // use tcl-list for enum's
00212         if (c->getBaseType() == Uml::ot_Enum) {
00213             UMLClassifierListItemList litList =
00214                 c->getFilteredList(Uml::ot_EnumLiteral);
00215             writeCode("set enum_" + mClass + " [list\\");
00216             m_indentLevel++;
00217             for (UMLClassifierListItem * lit = litList.first(); lit;
00218                     lit = litList.next()) {
00219                 QString         enumLiteral = cleanName(lit->getName());
00220                 writeCode(enumLiteral + "\\");
00221             }
00222             m_indentLevel--;
00223             writeCode("];# end of enum");
00224             m_indentLevel--;
00225             writeCode("};# end of namespace");
00226             return;
00227         }
00228     }
00229     // Generate template parameters.
00230     UMLTemplateList template_params = c->getTemplateList();
00231     if (template_params.count()) {
00232         writeCode("#TODO template<");
00233         for (UMLTemplate * t = template_params.first(); t;
00234                            t = template_params.next()) {
00235             QString         formalName = t->getName();
00236             QString         typeName = t->getTypeName();
00237             writeCode(typeName + "# " + formalName);
00238         }
00239     }
00240     // start my own class
00241     writeCode("class " + mClass + " {");
00242     m_indentLevel++;
00243     if (classifierInfo->superclasses.count() > 0) {
00244         QString         code = "inherit";
00245         for (UMLClassifier * superClass = classifierInfo->superclasses.first();
00246                 superClass; superClass = classifierInfo->superclasses.next()) {
00247             /*
00248             if (superClass->getAbstract() || superClass->isInterface())
00249                 stream << getIndent() << "virtual ";
00250             */
00251             if (superClass->getPackage().isEmpty()) {
00252                 code += " ::" + cleanName(superClass->getName());
00253             } else {
00254                 code +=
00255                     " ::" + cleanName(superClass->getPackage()) + "::" +
00256                     cleanName(superClass->getName());
00257             }
00258         }
00259         writeCode(code);
00260     }
00261     //
00262     //declarations of operations
00263     //
00264     // write out field and operations decl grouped by visibility
00265     //
00266 
00267     // PUBLIC attribs/methods
00268     // for public: constructors are first ops we print out
00269     if (!classifierInfo->isInterface) {
00270         writeConstructorHeader();
00271         writeDestructorHeader();
00272     }
00273     // attributes
00274     writeAttributeDecl(Uml::Visibility::Public, true);      // write static attributes first
00275     writeAttributeDecl(Uml::Visibility::Public, false);
00276     // associations
00277     writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Public,
00278                          c->getID(), "Associations");
00279     writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Public, c->getID(),
00280                          "Aggregations");
00281     writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Public, c->getID(),
00282                          "Compositions");
00283     //TODO  writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, stream);
00284     writeOperationHeader(c, Uml::Visibility::Public);
00285 
00286     // PROTECTED attribs/methods
00287     //
00288     // attributes
00289     writeAttributeDecl(Uml::Visibility::Protected, true);   // write static attributes first
00290     writeAttributeDecl(Uml::Visibility::Protected, false);
00291     // associations
00292     writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Protected,
00293                          c->getID(), "Association");
00294     writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Protected,
00295                          c->getID(), "Aggregation");
00296     writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Protected,
00297                          c->getID(), "Composition");
00298     //TODO  writeHeaderAccessorMethodDecl(c, Uml::Visibility::Protected, stream);
00299     writeOperationHeader(c, Uml::Visibility::Protected);
00300 
00301     // PRIVATE attribs/methods
00302     //
00303     // attributes
00304     writeAttributeDecl(Uml::Visibility::Private, true);     // write static attributes first
00305     writeAttributeDecl(Uml::Visibility::Private, false);
00306     // associations
00307     writeAssociationDecl(classifierInfo->plainAssociations, Uml::Visibility::Private,
00308                          c->getID(), "Associations");
00309     writeAssociationDecl(classifierInfo->aggregations, Uml::Visibility::Private, c->getID(),
00310                          "Aggregations");
00311     writeAssociationDecl(classifierInfo->compositions, Uml::Visibility::Private, c->getID(),
00312                          "Compositions");
00313     //TODO  writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, stream);
00314     writeOperationHeader(c, Uml::Visibility::Private);
00315     writeInitAttributeHeader(); // this is always private, used by constructors to initialize class
00316 
00317     // end of class header
00318     m_indentLevel--;
00319     writeCode("};# end of class");
00320 
00321     // end of class namespace, if any
00322     m_indentLevel--;
00323     writeCode("};# end of namespace");
00324 }
00325 
00326 void
00327 TclWriter::writeSourceFile(UMLClassifier * c, QFile & filetcl)
00328 {
00329     // open stream for writing
00330     QTextStream     stream(&filetcl);
00331     mStream = &stream;
00332 
00333     // set the starting indentation at zero
00334     m_indentLevel = 0;
00335 
00336     //try to find a heading file (license, coments, etc)
00337     QString         str;
00338     str = getHeadingFile(".tclbody");
00339     if (!str.isEmpty()) {
00340         str.replace(QRegExp("%filename%"), classifierInfo->fileName + "body");
00341         str.replace(QRegExp("%filepath%"), filetcl.name());
00342         writeCode(str);
00343     }
00344     // Start body of class
00345 
00346     // constructors are first ops we print out
00347     if (!classifierInfo->isInterface) {
00348         writeConstructorSource();
00349         writeDestructorSource();
00350     }
00351     // Public attributes have in tcl a configbody method
00352     writeAttributeSource();
00353     // Association access functions
00354     writeAssociationSource(classifierInfo->plainAssociations, c->getID());
00355     writeAssociationSource(classifierInfo->aggregations, c->getID());
00356     writeAssociationSource(classifierInfo->compositions, c->getID());
00357     // Procedures and methods
00358     writeOperationSource(c, Uml::Visibility::Public);
00359     writeOperationSource(c, Uml::Visibility::Protected);
00360     writeOperationSource(c, Uml::Visibility::Private);
00361     // Yep, bringing up the back of the bus, our initialization method for attributes
00362     writeInitAttributeSource();
00363 }
00364 
00365 void
00366 TclWriter::writeCode(const QString &text)
00367 {
00368     *mStream << getIndent() << text << m_endl;
00369 }
00370 
00371 void
00372 TclWriter::writeComm(const QString &text)
00373 {
00374     QStringList     lines = QStringList::split("\n", text, true);
00375     for (uint i = 0; i < lines.count(); i++) {
00376         *mStream << getIndent() << "# " << lines[i] << m_endl;
00377     }
00378 }
00379 
00380 void
00381 TclWriter::writeDocu(const QString &text)
00382 {
00383     QStringList     lines = QStringList::split("\n", text, true);
00384     for (uint i = 0; i < lines.count(); i++) {
00385         *mStream << getIndent() << "## " << lines[i] << m_endl;
00386     }
00387 }
00388 
00389 // To prevent circular including when both classifiers on either end
00390 // of an association have roles we need to have forward declaration of
00391 // the other class...but only IF its not THIS class (as could happen
00392 // in self-association relationship).
00393 void
00394 TclWriter::writeAssociationIncl(UMLAssociationList list, Uml::IDType myId,
00395                                 const QString &type)
00396 {
00397     for (UMLAssociation * a = list.first(); a; a = list.next()) {
00398         UMLClassifier  *classifier = NULL;
00399 
00400         writeComm(m_endl + type + m_endl + a->toString() + m_endl + a->getDoc());
00401         // only use OTHER classes (e.g. we don't need to write includes for ourselves!!
00402         // AND only IF the roleName is defined, otherwise, its not meant to be noticed.
00403         if (a->getObjectId(Uml::A) == myId && !a->getRoleName(Uml::B).isEmpty()) {
00404             classifier = dynamic_cast < UMLClassifier * >(a->getObject(Uml::B));
00405             writeUse(classifier);
00406         } else if (a->getObjectId(Uml::B) == myId
00407                    && !a->getRoleName(Uml::A).isEmpty()) {
00408             classifier = dynamic_cast < UMLClassifier * >(a->getObject(Uml::A));
00409             if (classifier->getPackage().isEmpty())
00410                 writeCode("namespace eval " + cleanName(classifier->getName()) +
00411                           " {}");
00412         } else {
00413             // CHECK: This crashes (classifier still NULL from above)
00414             /*
00415             writeCode("namespace eval " + cleanName(classifier->getPackage()) +
00416                       "::" + cleanName(classifier->getName()) + " {}");
00417              */
00418         }
00419     }
00420 }
00421 
00422 void
00423 TclWriter::writeUse(UMLClassifier * c)
00424 {
00425     QString         myNs;
00426 
00427     if (!c->getPackage().isEmpty()) {
00428         myNs = cleanName(c->getPackage());
00429     } else {
00430         myNs = "";
00431     }
00432     // if different package
00433     if (("::"+myNs) != mNamespace) {
00434         if (c->getPackage().isEmpty()) {
00435             writeCode("source " + findFileName(c, ".tcl"));
00436             writeCode("namespace import ::" + cleanName(c->getName()));
00437         } else {
00438             writeCode("package require " + myNs);
00439             writeCode("namespace import ::" + myNs + "::" +
00440                       cleanName(c->getName()));
00441         }
00442     } else {
00443         // source the file
00444         writeCode("source " + findFileName(c, ".tcl"));
00445     }
00446 }
00447 
00448 void
00449 TclWriter::writeConstructorHeader()
00450 {
00451 
00452     writeDocu
00453     (m_endl + "@func constructor" + m_endl +
00454      "@par args contain all configuration parameters" + m_endl);
00455 
00456     writeCode("constructor {args} {}" + m_endl);
00457 }
00458 
00459 void
00460 TclWriter::writeConstructorSource()
00461 {
00462     writeComm(mClassGlobal + "::constructor");
00463     writeCode(mClassGlobal + "::constructor {args} {");
00464     m_indentLevel++;
00465     if (classifierInfo->hasAttributes) {
00466         writeCode("initAttributes");
00467     }
00468     writeCode("eval configure $args");
00469     m_indentLevel--;
00470     writeCode('}' + m_endl);
00471 }
00472 
00473 void
00474 TclWriter::writeDestructorHeader()
00475 {
00476 
00477     writeDocu(m_endl + "@func destructor" + m_endl);
00478 
00479     writeCode("destructor {} {}");
00480 }
00481 
00482 void
00483 TclWriter::writeDestructorSource()
00484 {
00485     writeComm(mClassGlobal + "::destructor");
00486     writeCode(mClassGlobal + "::destructor {} {" + m_endl + '}' + m_endl);
00487 }
00488 
00489 void
00490 TclWriter::writeAttributeDecl(Uml::Visibility visibility, bool writeStatic)
00491 {
00492     if (classifierInfo->isInterface)
00493         return;
00494 
00495     QString scope = visibility.toString();
00496     QString type;
00497     if (writeStatic) {
00498         type = "common";
00499     } else {
00500         type = "variable";
00501     }
00502     UMLAttributeList *list = NULL;
00503     switch (visibility) {
00504     case Uml::Visibility::Private:
00505         if (writeStatic) {
00506             list = &(classifierInfo->static_atpriv);
00507         } else {
00508             list = &(classifierInfo->atpriv);
00509         }
00510         break;
00511 
00512     case Uml::Visibility::Protected:
00513         if (writeStatic) {
00514             list = &(classifierInfo->static_atprot);
00515         } else {
00516             list = &(classifierInfo->atprot);
00517         }
00518         break;
00519 
00520     case Uml::Visibility::Public:
00521         if (writeStatic) {
00522             list = &(classifierInfo->static_atpub);
00523         } else {
00524             list = &(classifierInfo->atpub);
00525         }
00526         break;
00527     default:
00528         break;
00529     }
00530 
00531     if (list && list->count() > 0) {
00532         writeComm(m_endl + scope + ' ' + type + " attributes" + m_endl);
00533         // write attrib declarations now
00534         QString         documentation;
00535         for (UMLAttribute * at = list->first(); at; at = list->next()) {
00536             documentation = at->getDoc();
00537             QString         varName = cleanName(at->getName());
00538             QString         typeName = fixTypeName(at->getTypeName());
00539             writeDocu(m_endl + "@var " + scope + ' ' + type + ' ' + typeName + ' ' +
00540                       varName + m_endl + documentation);
00541             writeCode(scope + ' ' + type + ' ' + varName + m_endl);
00542         }
00543     }
00544 }
00545 
00546 void
00547 TclWriter::writeAssociationDecl(UMLAssociationList associations,
00548                                 Uml::Visibility permitScope, Uml::IDType id,
00549                                 const QString &/*type*/)
00550 {
00551     if (forceSections() || !associations.isEmpty()) {
00552         bool            printRoleA = false, printRoleB = false;
00553         for (UMLAssociation * a = associations.first(); a;
00554                 a = associations.next()) {
00555 
00556             // it may seem counter intuitive, but you want to insert the role of the
00557             // *other* class into *this* class.
00558             if (a->getObjectId(Uml::A) == id && !a->getRoleName(Uml::B).isEmpty())
00559                 printRoleB = true;
00560 
00561             if (a->getObjectId(Uml::B) == id && !a->getRoleName(Uml::A).isEmpty())
00562                 printRoleA = true;
00563 
00564             // First: we insert documentaion for association IF it has either role AND some documentation (!)
00565             // print RoleB decl
00566             if (printRoleB && a->getVisibility(Uml::B) == permitScope) {
00567 
00568                 QString         fieldClassName =
00569                     cleanName(getUMLObjectName(a->getObject(Uml::B)));
00570                 writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::B),
00571                                          a->getMulti(Uml::B), a->getRoleDoc(Uml::B),
00572                                          permitScope.toString());
00573             }
00574             // print RoleA decl
00575             if (printRoleA && a->getVisibility(Uml::A) == permitScope) {
00576                 QString         fieldClassName =
00577                     cleanName(getUMLObjectName(a->getObject(Uml::A)));
00578                 writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::A),
00579                                          a->getMulti(Uml::A), a->getRoleDoc(Uml::A),
00580                                          permitScope.toString());
00581             }
00582             // reset for next association in our loop
00583             printRoleA = false;
00584             printRoleB = false;
00585         }
00586     }
00587 }
00588 
00589 void
00590 TclWriter::writeAssociationRoleDecl(const QString &fieldClassName, const QString &roleName,
00591                                     const QString &multi, const QString &doc, const QString &scope)
00592 {
00593     // ONLY write out IF there is a rolename given
00594     // otherwise its not meant to be declared in the code
00595     if (roleName.isEmpty())
00596         return;
00597 
00598 
00599     // declare the association based on whether it is this a single variable
00600     // or a List (Vector). One day this will be done correctly with special
00601     // multiplicity object that we don't have to figure out what it means via regex.
00602     if (multi.isEmpty() || multi.contains(QRegExp("^[01]$"))) {
00603         QString         fieldVarName = roleName.lower();
00604 
00605         // record this for later consideration of initialization IF the
00606         // multi value requires 1 of these objects
00607         if (ObjectFieldVariables.findIndex(fieldVarName) == -1 &&
00608                 multi.contains(QRegExp("^1$"))
00609            ) {
00610             // ugh. UGLY. Storing variable name and its class in pairs.
00611             ObjectFieldVariables.append(fieldVarName);
00612             ObjectFieldVariables.append(fieldClassName);
00613         }
00614         writeDocu(m_endl + "@var " + scope + " variable <" + fieldClassName +
00615                   "> " + fieldVarName + m_endl + doc);
00616         writeCode(scope + " variable " + fieldVarName + m_endl);
00617     } else {
00618         QString         fieldVarName = roleName.lower();
00619 
00620         // record unique occurrences for later when we want to check
00621         // for initialization of this vector
00622         if (VectorFieldVariables.findIndex(fieldVarName) == -1)
00623             VectorFieldVariables.append(fieldVarName);
00624         writeDocu(m_endl + "@var" + scope + " variable <" + fieldClassName +
00625                   "*> " + fieldVarName + m_endl + doc);
00626         writeCode(scope + " variable " + fieldVarName + m_endl);
00627     }
00628 }
00629 
00630 void
00631 TclWriter::writeInitAttributeHeader()
00632 {
00633     if (classifierInfo->hasAttributes) {
00634         writeDocu("@method private initAttributes" + m_endl +
00635                   "Initialize all internal variables");
00636         writeCode("private method initAttributes {}");
00637     }
00638 }
00639 
00640 void
00641 TclWriter::writeInitAttributeSource()
00642 {
00643     // only need to do this under certain conditions
00644     if (classifierInfo->hasAttributes) {
00645         QString         varName;
00646 
00647         writeComm(mClassGlobal + "::initAttributes");
00648         writeCode("body " + mClassGlobal + "::initAttributes {} {");
00649         m_indentLevel++;
00650 
00651         // first, initiation of fields derived from attributes
00652         UMLAttributeList *atl = classifierInfo->getAttList();
00653         for (UMLAttribute * at = atl->first(); at; at = atl->next()) {
00654             if (!at->getInitialValue().isEmpty()) {
00655                 varName = cleanName(at->getName());
00656                 writeCode("set " + varName + ' ' + at->getInitialValue());
00657             }
00658         }
00659         // Now initialize the association related fields (e.g. vectors)
00660         QStringList::Iterator it;
00661         for (it = VectorFieldVariables.begin();
00662                 it != VectorFieldVariables.end(); ++it) {
00663             varName = *it;
00664             writeCode("set " + varName + " [list]");
00665         }
00666 
00667         for (it = ObjectFieldVariables.begin();
00668                 it != ObjectFieldVariables.end(); ++it) {
00669             varName = *it;
00670             it++;
00671             QString         fieldClassName = *it;
00672             writeCode("set " + varName + " [list]");
00673         }
00674         // clean up
00675         ObjectFieldVariables.clear();   // shouldn't be needed?
00676         VectorFieldVariables.clear();   // shouldn't be needed?
00677 
00678         m_indentLevel--;
00679         writeCode('}' + m_endl);
00680     }
00681 }
00682 
00683 void
00684 TclWriter::writeOperationHeader(UMLClassifier * c, Uml::Visibility permitScope)
00685 {
00686 
00687     UMLOperationList oplist;
00688     UMLOperation   *op;
00689     UMLAttribute   *at;
00690     int             j;
00691 
00692     //sort operations by scope first and see if there are abstract methods
00693     UMLOperationList inputlist = c->getOpList();
00694     for (UMLOperation * op = inputlist.first(); op; op = inputlist.next()) {
00695         switch (op->getVisibility()) {
00696         case Uml::Visibility::Public:
00697             if (permitScope == Uml::Visibility::Public)
00698                 oplist.append(op);
00699             break;
00700         case Uml::Visibility::Protected:
00701             if (permitScope == Uml::Visibility::Protected)
00702                 oplist.append(op);
00703             break;
00704         case Uml::Visibility::Private:
00705             if (permitScope == Uml::Visibility::Private)
00706                 oplist.append(op);
00707             break;
00708         default:
00709             break;
00710         }
00711     }
00712 
00713     // generate method decl for each operation given
00714     if (oplist.count() > 0) {
00715         writeComm("Operations");
00716     }
00717     for (op = oplist.first(); op; op = oplist.next()) {
00718         QString         doc = "";
00719         QString         code = "";
00720         QString         methodReturnType = fixTypeName(op->getTypeName());
00721         QString         name = cleanName(op->getName());
00722         QString         scope = permitScope.toString();
00723         if (op->getAbstract() || classifierInfo->isInterface) {
00724             //TODO declare abstract method as 'virtual'
00725             // str += "virtual ";
00726         }
00727         // declaration for header file
00728         if (op->getStatic()) {
00729             doc = m_endl + "@fn " + scope + " proc " + name + m_endl;
00730             code = scope + " proc " + name + " {";
00731         } else {
00732             doc = m_endl + "@fn " + scope + " method " + name + m_endl;
00733             code = scope + " method " + name + " {";
00734         }
00735         // method parameters
00736         UMLAttributeList atl = op->getParmList();
00737         j = 0;
00738         for (at = atl.first(); at; at = atl.next(), j++) {
00739             QString         typeName = fixTypeName(at->getTypeName());
00740             QString         atName = cleanName(at->getName());
00741             if (at->getInitialValue().isEmpty()) {
00742                 doc +=
00743                     "@param " + typeName + ' ' + atName + m_endl + at->getDoc() +
00744                     m_endl;
00745                 code += ' ' + atName;
00746             } else {
00747                 doc +=
00748                     "@param " + typeName + ' ' + atName + " (default=" +
00749                     at->getInitialValue() + ") " + m_endl + at->getDoc() + m_endl;
00750                 code += " {" + atName + ' ' + at->getInitialValue() + "} ";
00751             }
00752         }
00753         if (methodReturnType != "void") {
00754             doc += "@return     " + methodReturnType + m_endl;
00755         }
00756         writeDocu(doc + op->getDoc());
00757         writeCode(code + "} {}" + m_endl);
00758     }
00759 }
00760 
00761 void
00762 TclWriter::writeOperationSource(UMLClassifier * c, Uml::Visibility permitScope)
00763 {
00764 
00765     UMLOperationList oplist;
00766     UMLOperation   *op;
00767     UMLAttribute   *at;
00768     int             j;
00769 
00770     //sort operations by scope first and see if there are abstract methods
00771     UMLOperationList inputlist = c->getOpList();
00772     for (UMLOperation * op = inputlist.first(); op; op = inputlist.next()) {
00773         switch (op->getVisibility()) {
00774         case Uml::Visibility::Public:
00775             if (permitScope == Uml::Visibility::Public)
00776                 oplist.append(op);
00777             break;
00778         case Uml::Visibility::Protected:
00779             if (permitScope == Uml::Visibility::Protected)
00780                 oplist.append(op);
00781             break;
00782         case Uml::Visibility::Private:
00783             if (permitScope == Uml::Visibility::Private)
00784                 oplist.append(op);
00785             break;
00786         default:
00787             break;
00788         }
00789     }
00790 
00791     // generate source for each operation given
00792     for (op = oplist.first(); op; op = oplist.next()) {
00793         QString         code = "";
00794         QString         methodReturnType = fixTypeName(op->getTypeName());
00795         QString         name;
00796         // no code needed
00797         if (op->getAbstract() || classifierInfo->isInterface) {
00798             continue;
00799         }
00800         name = mClassGlobal + "::" + cleanName(op->getName());
00801         writeComm(name);
00802         code = "body " + name + " {";
00803         // parameters
00804         UMLAttributeList atl = op->getParmList();
00805         j = 0;
00806         for (at = atl.first(); at; at = atl.next(), j++) {
00807             QString         atName = cleanName(at->getName());
00808             if (at->getInitialValue().isEmpty()) {
00809                 code += ' ' + atName;
00810             } else {
00811                 code += " {" + atName + ' ' + at->getInitialValue() + "} ";
00812             }
00813         }
00814         writeCode(code += "} {");
00815         m_indentLevel++;
00816         if (methodReturnType != "void") {
00817             writeCode("return " + methodReturnType);
00818         } else {
00819             writeCode("return");
00820         }
00821         m_indentLevel--;
00822         writeCode('}' + m_endl);
00823     }
00824 }
00825 
00826 void
00827 TclWriter::writeAttributeSource()
00828 {
00829     UMLAttributeList *list = &(classifierInfo->atpub);
00830     UMLAttribute   *at;
00831     for (at = list->first(); at; at = list->next()) {
00832         QString         name = mClassGlobal + "::" + cleanName(at->getName());
00833 
00834         writeComm(name);
00835         writeCode("configbody " + name + " {} {" + m_endl + '}' + m_endl);
00836     }
00837 }
00838 
00839 void
00840 TclWriter::writeAssociationSource(UMLAssociationList associations,
00841                                   Uml::IDType id)
00842 {
00843     if (associations.isEmpty()) {
00844         return;
00845     }
00846 
00847     bool            printRoleA = false, printRoleB = false;
00848     for (UMLAssociation * a = associations.first(); a; a = associations.next()) {
00849 
00850         // it may seem counter intuitive, but you want to insert the role of the
00851         // *other* class into *this* class.
00852         if (a->getObjectId(Uml::A) == id && !a->getRoleName(Uml::B).isEmpty())
00853             printRoleB = true;
00854 
00855         if (a->getObjectId(Uml::B) == id && !a->getRoleName(Uml::A).isEmpty())
00856             printRoleA = true;
00857 
00858         // print RoleB source
00859         if (printRoleB && a->getVisibility(Uml::B) == Uml::Visibility::Public) {
00860 
00861             QString         fieldClassName =
00862                 cleanName(getUMLObjectName(a->getObject(Uml::B)));
00863             writeAssociationRoleSource(fieldClassName, a->getRoleName(Uml::B),
00864                                        a->getMulti(Uml::B));
00865         }
00866         // print RoleA source
00867         if (printRoleA && a->getVisibility(Uml::A) == Uml::Visibility::Public) {
00868             QString         fieldClassName =
00869                 cleanName(getUMLObjectName(a->getObject(Uml::A)));
00870             writeAssociationRoleSource(fieldClassName, a->getRoleName(Uml::A),
00871                                        a->getMulti(Uml::A));
00872         }
00873         // reset for next association in our loop
00874         printRoleA = false;
00875         printRoleB = false;
00876     }
00877 }
00878 
00879 void
00880 TclWriter::writeAssociationRoleSource(const QString &fieldClassName,
00881                                       const QString &roleName, const QString &multi)
00882 {
00883     // ONLY write out IF there is a rolename given
00884     // otherwise its not meant to be declared in the code
00885     if (roleName.isEmpty())
00886         return;
00887 
00888     // declare the association based on whether it is this a single variable
00889     // or a List (Vector). One day this will be done correctly with special
00890     // multiplicity object that we don't have to figure out what it means via regex.
00891     if (multi.isEmpty() || multi.contains(QRegExp("^[01]$"))) {
00892         QString         fieldVarName = roleName.lower();
00893 
00894         writeCode("configbody " + mClassGlobal + "::" + fieldVarName + " {} {");
00895         m_indentLevel++;
00896         writeCode("if {![$" + fieldVarName + " isa " + fieldClassName + "]} {");
00897         m_indentLevel++;
00898         writeCode("return -code error \"expected object of class: " +
00899                   fieldClassName + "\"");
00900         m_indentLevel--;
00901         writeCode("}");
00902         m_indentLevel--;
00903 
00904     } else {
00905         QString         fieldVarName = roleName.lower();
00906 
00907         writeCode("configbody " + mClassGlobal + "::" + fieldVarName + " {} {");
00908         m_indentLevel++;
00909         writeCode("foreach myObj $" + fieldVarName + " {");
00910         m_indentLevel++;
00911         writeCode("if {![$myObj isa " + fieldClassName + "]} {");
00912         m_indentLevel++;
00913         writeCode("return -code error \"expected object of class: " +
00914                   fieldClassName + "\"");
00915         m_indentLevel--;
00916         writeCode("}");
00917         m_indentLevel--;
00918         writeCode("}");
00919         m_indentLevel--;
00920     }
00921     writeCode('}' + m_endl);
00922 }
00923 
00924 QString
00925 TclWriter::fixTypeName(const QString &string)
00926 {
00927     if (string.isEmpty())
00928         return "void";
00929     return string;
00930 }
00931 
00932 // methods like this _shouldn't_ be needed IF we properly did things thruought the code.
00933 QString
00934 TclWriter::getUMLObjectName(UMLObject * obj)
00935 {
00936     return (obj != 0) ? obj->getName() : QString("NULL");
00937 }
00938 
00939 const           QStringList
00940 TclWriter::reservedKeywords() const
00941 {
00942     static QStringList keywords;
00943 
00944     if              (keywords.isEmpty())
00945     {
00946         for (int i = 0; tclwords[i]; i++)
00947             keywords.append(tclwords[i]);
00948     }
00949     return          keywords;
00950 }
00951 
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:08:00 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003