/*************************************************************************** begin : Thu Jul 26 2005 copyright : (C) 2005 by Rene Meyer email : rene.meyer@sturmit.de (C) 2006 Umbrello UML Modeller Authors ***************************************************************************/ /*************************************************************************** * * * 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 #include #include #include // 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; }