diff options
Diffstat (limited to 'umbrello/umbrello/codegenerators/idlwriter.cpp')
-rw-r--r-- | umbrello/umbrello/codegenerators/idlwriter.cpp | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/umbrello/umbrello/codegenerators/idlwriter.cpp b/umbrello/umbrello/codegenerators/idlwriter.cpp new file mode 100644 index 00000000..a893acc3 --- /dev/null +++ b/umbrello/umbrello/codegenerators/idlwriter.cpp @@ -0,0 +1,482 @@ +/*************************************************************************** + * copyright (C) 2003-2005 * + * 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. * + * * + ***************************************************************************/ + +#include "idlwriter.h" + +#include <kdebug.h> +#include <kmessagebox.h> +#include <qfile.h> +#include <qregexp.h> +#include <qtextstream.h> + +#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) { + QString 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, QString& typeName, QString& 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 = QString::null tells caller to + // skip this association at this side. + roleName = QString(); + return; + } + IAmRoleA = false; + other = a->getObject(Uml::A); + } + // Construct the type name: + typeName = cleanName(other->getName()); + QString 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(); + QString classname = cleanName(c->getName()); + + //find an appropriate name for our file + QString fileName = findFileName(c, ".idl"); + if (fileName.isEmpty()) { + emit codeGenerated(c, false); + return; + } + + QFile file; + if (!openFile(file, fileName)) { + emit codeGenerated(c, false); + return; + } + + // Start generating the code. + + QTextStream idl(&file); + //try to find a heading file(license, comments, etc) + QString str; + str = getHeadingFile(".idl"); + if (!str.isEmpty()) { + str.replace(QRegExp("%filename%"), fileName); + str.replace(QRegExp("%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; + QString 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()) { + QString 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)) { + QString 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()) { + QString 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()) { + QString 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()) { + QString 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()) { + QString 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; + QString 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); + QString 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()) { + QString 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; + QString 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, QTextStream &idl, bool is_comment) { + UMLAttributeList atl = op->getParmList(); + QString 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; +} + +QStringList IDLWriter::defaultDatatypes() { + QStringList 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 QStringList IDLWriter::reservedKeywords() const { + + static QStringList 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; +} + + |