You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
16 KiB
483 lines
16 KiB
/***************************************************************************
|
|
* 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 <tqfile.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtextstream.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) {
|
|
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;
|
|
}
|
|
|
|
|