/*************************************************************************** * copyright (C) 2003-2005 * * 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. * * * ***************************************************************************/ #include "idlwriter.h" #include #include #include #include #include #include "../umldoc.h" #include "../classifier.h" #include "../enum.h" #include "../classifierlistitem.h" #include "../umlclassifierlistitemlist.h" #include "../package.h" #include "../association.h" #include "../attribute.h" #include "../operation.h" #include "../umlnamespace.h" IDLWriter::IDLWriter() : SimpleCodeGenerator(false) { } IDLWriter::~IDLWriter() {} bool IDLWriter::isOOClass(UMLClassifier *c) { TQString stype = c->getStereotype(); if (stype == "CORBAConstant" || stype == "CORBAEnum" || stype == "CORBAStruct" || stype == "CORBAUnion" || stype == "CORBASequence" || stype == "CORBAArray" || stype == "CORBATypedef") return false; // CORBAValue, CORBAInterface, and all empty/unknown stereotypes are // assumed to be OO classes. return true; } bool IDLWriter::assocTypeIsMappableToAttribute(Uml::Association_Type at) { return (at == Uml::at_Aggregation || at == Uml::at_Association || at == Uml::at_Composition || at == Uml::at_UniAssociation); } /** * returns "IDL" */ Uml::Programming_Language IDLWriter::getLanguage() { return Uml::pl_IDL; } void IDLWriter::computeAssocTypeAndRole (UMLAssociation *a, UMLClassifier *c, TQString& typeName, TQString& roleName) { // Determine which is the "remote" end of the association: bool IAmRoleA = true; UMLObject *other = a->getObject(Uml::B); Uml::Association_Type at = a->getAssocType(); if (c->getName() == other->getName()) { if (at == Uml::at_Aggregation || at == Uml::at_Composition || at == Uml::at_UniAssociation) { // Assuming unidirectional association, and we are // at the "wrong" side. // Returning roleName = TQString() tells caller to // skip this association at this side. roleName = TQString(); return; } IAmRoleA = false; other = a->getObject(Uml::A); } // Construct the type name: typeName = cleanName(other->getName()); TQString multiplicity; if (IAmRoleA) multiplicity = a->getMulti(Uml::B); else multiplicity = a->getMulti(Uml::A); if (!multiplicity.isEmpty() && multiplicity != "1") typeName.append("Vector"); // Construct the member name: if (IAmRoleA) roleName = a->getRoleName(Uml::B); else roleName = a->getRoleName(Uml::A); if (roleName.isEmpty()) { roleName = a->getName(); if (roleName.isEmpty()) { roleName = "m_" + typeName; } } } void IDLWriter::writeClass(UMLClassifier *c) { if (!c) { kDebug() << "Cannot write class of NULL concept!" << endl; return; } const bool isClass = !c->isInterface(); TQString classname = cleanName(c->getName()); //find an appropriate name for our file TQString fileName = findFileName(c, ".idl"); if (fileName.isEmpty()) { emit codeGenerated(c, false); return; } TQFile file; if (!openFile(file, fileName)) { emit codeGenerated(c, false); return; } // Start generating the code. TQTextStream idl(&file); //try to find a heading file(license, comments, etc) TQString str; str = getHeadingFile(".idl"); if (!str.isEmpty()) { str.replace(TQRegExp("%filename%"), fileName); str.replace(TQRegExp("%filepath%"), file.name()); idl << str << m_endl; } // Write includes. UMLPackageList includes; findObjectsRelated(c, includes); if (includes.count()) { for (UMLPackage *conc = includes.first(); conc; conc = includes.next()) { if (conc->getBaseType() == Uml::ot_Datatype) continue; TQString incName = findFileName(conc, ".idl"); if (!incName.isEmpty()) idl << "#include \"" << incName << "\"" << m_endl; } idl << m_endl; } // Generate the module declaration(s) for the package(s) in which // we are embedded. UMLPackageList pkgList = c->getPackages(); UMLPackage *pkg; for (pkg = pkgList.first(); pkg != NULL; pkg = pkgList.next()) { idl << getIndent() << "module " << pkg->getName() << " {" << m_endl << m_endl; m_indentLevel++; } // Write class Documentation if non-empty or if force option set. if (forceDoc() || !c->getDoc().isEmpty()) { idl << "//" << m_endl; idl << "// class " << classname << m_endl; idl << formatDoc(c->getDoc(), "// "); idl << m_endl; } if (c->getBaseType() == Uml::ot_Enum) { UMLClassifierListItemList litList = c->getFilteredList(Uml::ot_EnumLiteral); uint i = 0; idl << getIndent() << "enum " << classname << " {" << m_endl; m_indentLevel++; for (UMLClassifierListItem *lit = litList.first(); lit; lit = litList.next()) { TQString enumLiteral = cleanName(lit->getName()); idl << getIndent() << enumLiteral; if (++i < litList.count()) idl << ","; idl << m_endl; } m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; // Close the modules inside which we might be nested. for (pkg = pkgList.first(); pkg != NULL; pkg = pkgList.next()) { m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; } return; } if (! isOOClass(c)) { TQString stype = c->getStereotype(); if (stype == "CORBAConstant") { kError() << "The stereotype " << stype << " cannot be applied to " << c->getName() << ", but only to attributes." << endl; return; } if (!isClass) { kError() << "The stereotype " << stype << " cannot be applied to " << c->getName() << ", but only to classes." << endl; return; } if (stype == "CORBAEnum") { UMLAttributeList atl = c->getAttributeList(); UMLAttribute *at; idl << getIndent() << "enum " << classname << " {" << m_endl; m_indentLevel++; uint i = 0; for (at = atl.first(); at; at = atl.next()) { TQString enumLiteral = cleanName(at->getName()); idl << getIndent() << enumLiteral; if (++i < atl.count()) idl << ","; idl << m_endl; } m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; } else if (stype == "CORBAStruct") { UMLAttributeList atl = c->getAttributeList(); UMLAttribute *at; idl << getIndent() << "struct " << classname << " {" << m_endl; m_indentLevel++; for (at = atl.first(); at; at = atl.next()) { TQString name = cleanName(at->getName()); idl << getIndent() << at->getTypeName() << " " << name << ";" << m_endl; // Initial value not possible in IDL. } UMLAssociationList compositions = c->getCompositions(); if (!compositions.isEmpty()) { idl << getIndent() << "// Compositions." << m_endl; for (UMLAssociation *a = compositions.first(); a; a = compositions.next()) { TQString memberType, memberName; computeAssocTypeAndRole(a, c, memberType, memberName); idl << getIndent() << memberType << " " << memberName << ";" << m_endl; } } UMLAssociationList aggregations = c->getAggregations(); if (!aggregations.isEmpty()) { idl << getIndent() << "// Aggregations." << m_endl; for (UMLAssociation *a = aggregations.first(); a; a = aggregations.next()) { TQString memberType, memberName; computeAssocTypeAndRole(a, c, memberType, memberName); idl << getIndent() << memberType << " " << memberName << ";" << m_endl; } } m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; } else if (stype == "CORBAUnion") { idl << getIndent() << "// " << stype << " " << c->getName() << " is Not Yet Implemented" << m_endl << m_endl; } else if (stype == "CORBATypedef") { UMLClassifierList superclasses = c->getSuperClasses(); UMLClassifier* firstParent = superclasses.first(); idl << getIndent() << "typedef " << firstParent->getName() << " " << c->getName() << ";" << m_endl << m_endl; } else { idl << getIndent() << "// " << stype << ": Unknown stereotype" << m_endl << m_endl; } // Close the modules inside which we might be nested. for (pkg = pkgList.first(); pkg != NULL; pkg = pkgList.next()) { m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; } return; } idl << getIndent(); if (c->getAbstract()) idl << "abstract "; bool isValuetype = (c->getStereotype() == "CORBAValue"); if (isValuetype) idl << "valuetype "; else idl << "interface "; idl << c->getName(); UMLClassifierList superclasses = c->getSuperClasses(); if (! superclasses.isEmpty()) { idl << " : "; UMLClassifier *parent = superclasses.first(); int n_parents = superclasses.count(); while (n_parents--) { idl << parent->getFullyQualifiedName("::"); if (n_parents) idl << ", "; parent = superclasses.next(); } } idl << " {" << m_endl << m_endl; m_indentLevel++; // Generate auxiliary declarations for multiplicity of associations UMLAssociation *a; bool didComment = false; UMLAssociationList assocs = c->getAssociations(); for (a = assocs.first(); a; a = assocs.next()) { if (! assocTypeIsMappableToAttribute(a->getAssocType())) continue; TQString multiplicity = a->getMulti(Uml::A); if (multiplicity.isEmpty() || multiplicity == "1") continue; if (!didComment) { idl << getIndent() << "// Types for association multiplicities" << m_endl << m_endl; didComment = true; } UMLClassifier* other = (UMLClassifier*)a->getObject(Uml::A); TQString bareName = cleanName(other->getName()); idl << getIndent() << "typedef sequence<" << other->getFullyQualifiedName("::") << "> " << bareName << "Vector;" << m_endl << m_endl; } // Generate public attributes. if (isClass) { UMLAttributeList atl = c->getAttributeList(); if (forceSections() || atl.count()) { idl << getIndent() << "// Attributes:" << m_endl << m_endl; for (UMLAttribute *at = atl.first(); at; at = atl.next()) { TQString attName = cleanName(at->getName()); Uml::Visibility scope = at->getVisibility(); idl << getIndent(); if (isValuetype) { if (scope == Uml::Visibility::Public) idl << "public "; else idl << "private "; } else { if (scope != Uml::Visibility::Public) { idl << "// visibility should be: " << scope.toString() << m_endl; idl << getIndent(); } idl << "attribute "; } idl << at->getTypeName() << " " << attName << ";" << m_endl << m_endl; } } } // Generate public operations. UMLOperationList opl(c->getOpList()); UMLOperationList oppub; UMLOperation *op; for (op = opl.first(); op; op = opl.next()) { if (op->getVisibility() == Uml::Visibility::Public) oppub.append(op); } if (forceSections() || oppub.count()) { idl << getIndent() << "// Public methods:" << m_endl << m_endl; for (op = oppub.first(); op; op = oppub.next()) writeOperation(op, idl); idl << m_endl; } if (forceSections() || !assocs.isEmpty()) { idl << getIndent() << "// Associations:" << m_endl << m_endl; for (a = assocs.first(); a; a = assocs.next()) { Uml::Association_Type at = a->getAssocType(); if (! assocTypeIsMappableToAttribute(at)) continue; TQString typeName, roleName; computeAssocTypeAndRole(a, c, typeName, roleName); if (roleName.isEmpty()) // presumably because we are at the "wrong" end continue; idl << getIndent() << "// " << UMLAssociation::typeAsString(at) << m_endl; idl << getIndent(); if (isValuetype) idl << "public "; else idl << "attribute "; idl << typeName << " " << roleName << ";" << m_endl; } idl << m_endl; } m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; // Close the modules inside which we might be nested. for (pkg = pkgList.first(); pkg != NULL; pkg = pkgList.next()) { m_indentLevel--; idl << getIndent() << "};" << m_endl << m_endl; } file.close(); emit codeGenerated(c, true); } void IDLWriter::writeOperation(UMLOperation *op, TQTextStream &idl, bool is_comment) { UMLAttributeList atl = op->getParmList(); TQString rettype = op->getTypeName(); if (rettype.isEmpty()) rettype = "void"; idl << getIndent(); if (is_comment) idl << "// "; idl << rettype << " " << cleanName(op->getName()) << " ("; if (atl.count()) { idl << m_endl; m_indentLevel++; uint i = 0; for (UMLAttribute *at = atl.first(); at; at = atl.next()) { idl << getIndent(); if (is_comment) idl << "// "; Uml::Parameter_Direction pk = at->getParmKind(); if (pk == Uml::pd_Out) idl << "out "; else if (pk == Uml::pd_InOut) idl << "inout "; else idl << "in "; idl << at->getTypeName() << " " << cleanName(at->getName()); if (++i < atl.count()) idl << "," << m_endl; } m_indentLevel--; } idl << ");" << m_endl << m_endl; } TQStringList IDLWriter::defaultDatatypes() { TQStringList l; l.append("boolean"); l.append("char"); l.append("octet"); l.append("short"); l.append("unsigned short"); l.append("long"); l.append("unsigned long"); l.append("float"); l.append("double"); l.append("string"); l.append("any"); return l; } const TQStringList IDLWriter::reservedKeywords() const { static TQStringList keywords; if (keywords.isEmpty()) { keywords << "any" << "attribute" << "boolean" << "case" << "char" << "const" << "context" << "default" << "double" << "enum" << "exception" << "FALSE" << "float" << "in" << "inout" << "interface" << "long" << "module" << "octet" << "oneway" << "out" << "raises" << "readonly" << "sequence" << "short" << "string" << "struct" << "switch" << "TRUE" << "typedef" << "union" << "unsigned" << "void"; } return keywords; }