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.
971 lines
32 KiB
971 lines
32 KiB
|
|
/***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
* copyright (C) 2007 Jari-Matti Mäkelä <jmjm@iki.fi> *
|
|
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
This is the "old" code generator that does not support code editing
|
|
in the Modeller but uses significantly less file space because the
|
|
source code is not replicated in the XMI file.
|
|
***************************************************************************/
|
|
|
|
// own header
|
|
#include "dwriter.h"
|
|
// qt includes
|
|
#include <tqfile.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqregexp.h>
|
|
// kde includes
|
|
#include <kdebug.h>
|
|
// app includes
|
|
#include "../umldoc.h"
|
|
#include "../classifier.h"
|
|
#include "../operation.h"
|
|
#include "../attribute.h"
|
|
#include "../association.h"
|
|
#include "../template.h"
|
|
#include "../umltemplatelist.h"
|
|
#include "codegen_utils.h"
|
|
|
|
DWriter::DWriter() {
|
|
startline = m_endl + m_indentation;
|
|
}
|
|
|
|
DWriter::~DWriter() {}
|
|
|
|
Uml::Programming_Language DWriter::getLanguage() {
|
|
return Uml::pl_D;
|
|
}
|
|
|
|
// FIXME: doesn't work yet
|
|
void DWriter::writeModuleDecl(UMLClassifier *c, TQTextStream &d) {
|
|
if(!c->getPackage().isEmpty())
|
|
d << "module " << c->getPackage() << ";" << m_endl;
|
|
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
void DWriter::writeModuleImports(UMLClassifier *c, TQTextStream &d) {
|
|
// another preparation, determine what we have
|
|
UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
|
|
UMLAssociationList uniAssociations = c->getUniAssociationToBeImplemented();
|
|
|
|
UMLAssociationList aggregations = c->getAggregations();
|
|
UMLAssociationList compositions = c->getCompositions();
|
|
|
|
bool hasAssociations = aggregations.count() + associations.count() +
|
|
compositions.count() + uniAssociations.count() > 0;
|
|
|
|
if (hasAssociations) {
|
|
// import tango, if that mode is set
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
//only import classes in a different package as this class
|
|
UMLPackageList imports;
|
|
findObjectsRelated(c, imports);
|
|
for (UMLPackage *con = imports.first(); con; con = imports.next()) {
|
|
if (con->getBaseType() == Uml::ot_Datatype)
|
|
continue;
|
|
TQString pkg = con->getPackage();
|
|
if (!pkg.isEmpty() && pkg != c->getPackage())
|
|
d << "import " << pkg << "." << cleanName(con->getName()) << ";"
|
|
<< m_endl;
|
|
}
|
|
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
void DWriter::writeClass(UMLClassifier *c) {
|
|
if (!c) {
|
|
kDebug()<<"Cannot write class of NULL concept!\n";
|
|
return;
|
|
}
|
|
|
|
isInterface = c->isInterface();
|
|
|
|
TQString fileName = cleanName(c->getName().lower());
|
|
|
|
//find an appropriate name for our file
|
|
fileName = findFileName(c, ".d");
|
|
if (fileName.isEmpty()) {
|
|
emit codeGenerated(c, false);
|
|
return;
|
|
}
|
|
|
|
// check that we may open that file for writing
|
|
TQFile file;
|
|
if ( !openFile(file, fileName) ) {
|
|
emit codeGenerated(c, false);
|
|
return;
|
|
}
|
|
|
|
// open text stream to file
|
|
TQTextStream d(&file);
|
|
|
|
//try to find a heading file (license, coments, etc)
|
|
TQString str;
|
|
str = getHeadingFile(".d");
|
|
if(!str.isEmpty()) {
|
|
str.replace(TQRegExp("%filename%"),fileName);
|
|
str.replace(TQRegExp("%filepath%"),file.name());
|
|
d<<str<<m_endl;
|
|
}
|
|
|
|
// source file begins with the module declaration
|
|
writeModuleDecl(c, d);
|
|
|
|
// imports
|
|
writeModuleImports(c, d);
|
|
|
|
// write the opening declaration for the class incl any documentation,
|
|
// interfaces and/or inheritence issues we have
|
|
writeClassDecl(c, d);
|
|
|
|
// start body of class
|
|
d << " {" << m_endl;
|
|
|
|
|
|
// Preparations
|
|
//
|
|
|
|
// sort attributes by Scope
|
|
UMLAttributeList atl;
|
|
UMLAttributeList atpub, atprot, atpriv, atpkg, atexport;
|
|
UMLAttributeList final_atpub, final_atprot, final_atpriv, final_atpkg, final_atexport;
|
|
|
|
atpub.setAutoDelete(false);
|
|
final_atpub.setAutoDelete(false);
|
|
atprot.setAutoDelete(false);
|
|
final_atprot.setAutoDelete(false);
|
|
atpriv.setAutoDelete(false);
|
|
final_atpriv.setAutoDelete(false);
|
|
atpkg.setAutoDelete(false);
|
|
final_atpkg.setAutoDelete(false);
|
|
atexport.setAutoDelete(false);
|
|
final_atexport.setAutoDelete(false);
|
|
|
|
if (!isInterface) {
|
|
UMLAttributeList atl = c->getAttributeList();
|
|
for (UMLAttribute *at = atl.first(); at ; at = atl.next()) {
|
|
switch(at->getVisibility())
|
|
{
|
|
case Uml::Visibility::Public:
|
|
if(at->getStatic())
|
|
final_atpub.append(at);
|
|
else
|
|
atpub.append(at);
|
|
break;
|
|
case Uml::Visibility::Protected:
|
|
if(at->getStatic())
|
|
final_atprot.append(at);
|
|
else
|
|
atprot.append(at);
|
|
break;
|
|
case Uml::Visibility::Private:
|
|
if(at->getStatic())
|
|
final_atpriv.append(at);
|
|
else
|
|
atpriv.append(at);
|
|
break;/* TODO: requires support from the gui & other structures
|
|
case Uml::Visibility::Package:
|
|
if(at->getStatic())
|
|
final_atpkg.append(at);
|
|
else
|
|
atpkg.append(at);
|
|
break;
|
|
case Uml::Visibility::Export:
|
|
if(at->getStatic())
|
|
final_atexport.append(at);
|
|
else
|
|
atexport.append(at);
|
|
break;*/
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// another preparation, determine what we have
|
|
UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
|
|
UMLAssociationList uniAssociations = c->getUniAssociationToBeImplemented();
|
|
|
|
UMLAssociationList aggregations = c->getAggregations();
|
|
UMLAssociationList compositions = c->getCompositions();
|
|
|
|
bool hasAssociations = aggregations.count() + associations.count() + compositions.count() + uniAssociations.count() > 0;
|
|
bool hasAttributes = atl.count() > 0;
|
|
bool hasAccessorMethods = hasAttributes || hasAssociations;
|
|
bool hasOperationMethods = c->getOpList().count() > 0;
|
|
|
|
// ATTRIBUTES
|
|
//
|
|
|
|
// write comment for section IF needed
|
|
if (forceDoc() || hasAccessorMethods)
|
|
{
|
|
writeComment("", m_indentation, d);
|
|
writeComment("Fields", m_indentation, d);
|
|
writeComment("", m_indentation, d);
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
writeAttributeDecls(final_atpub, final_atprot, final_atpriv, d);
|
|
writeAttributeDecls(atpub, atprot, atpriv, d);
|
|
|
|
writeAssociationDecls(associations, c->getID(), d);
|
|
writeAssociationDecls(uniAssociations, c->getID(), d);
|
|
writeAssociationDecls(aggregations, c->getID(), d);
|
|
writeAssociationDecls(compositions, c->getID(), d);
|
|
|
|
//FIXME: find constructors and write them here
|
|
|
|
// write constructors
|
|
if(!isInterface) writeConstructor(c, d);
|
|
|
|
|
|
// METHODS
|
|
//
|
|
|
|
// write comment for sub-section IF needed
|
|
if (forceDoc() || hasAccessorMethods ) {
|
|
writeComment("", m_indentation, d);
|
|
writeComment("Accessors", m_indentation, d);
|
|
writeComment("", m_indentation, d);
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
|
|
// Accessors for attributes
|
|
writeAttributeMethods(final_atpub, Uml::Visibility::Public, d);
|
|
writeAttributeMethods(final_atprot, Uml::Visibility::Protected, d);
|
|
writeAttributeMethods(final_atpriv, Uml::Visibility::Private, d);
|
|
writeAttributeMethods(atpub, Uml::Visibility::Public, d);
|
|
writeAttributeMethods(atprot, Uml::Visibility::Protected, d);
|
|
writeAttributeMethods(atpriv, Uml::Visibility::Private, d);
|
|
|
|
// accessor methods for associations
|
|
|
|
// first: determine the name of the other class
|
|
writeAssociationMethods(associations, c, d);
|
|
writeAssociationMethods(uniAssociations, c, d);
|
|
writeAssociationMethods(aggregations, c, d);
|
|
writeAssociationMethods(compositions, c, d);
|
|
|
|
// Other operation methods
|
|
// all other operations are now written
|
|
|
|
// write comment for sub-section IF needed
|
|
if (forceDoc() || hasOperationMethods) {
|
|
writeComment("", m_indentation, d);
|
|
writeComment("Other methods", m_indentation, d);
|
|
writeComment("", m_indentation, d);
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
writeOperations(c, d);
|
|
|
|
d << "}" << m_endl; // end class
|
|
|
|
file.close();
|
|
emit codeGenerated(c, true);
|
|
}
|
|
|
|
void DWriter::writeClassDecl(UMLClassifier *c, TQTextStream &d) {
|
|
|
|
// class documentation
|
|
if (!c->getDoc().isEmpty()) {
|
|
writeDocumentation("", c->getDoc(), "", "", d);
|
|
}
|
|
|
|
/*
|
|
* Class declaration
|
|
*
|
|
* (private) class foo(T, ..., Z) : class1, ..., classN, interface1, ..., interfaceN
|
|
* a b c d e f g
|
|
*/
|
|
|
|
// (a) visibility modifier
|
|
switch(c->getVisibility()) {
|
|
case Uml::Visibility::Private: d << "private "; break;
|
|
default: break;
|
|
}
|
|
|
|
// (b) keyword
|
|
// TODO what about structs?
|
|
if (isInterface) {
|
|
d << "interface ";
|
|
} else {
|
|
if (c->getAbstract()) {
|
|
d << "abstract ";
|
|
}
|
|
|
|
d << "class ";
|
|
}
|
|
|
|
// (c) class name
|
|
TQString classname = cleanName(c->getName()); // our class name
|
|
d << classname;
|
|
|
|
// (d) template parameters
|
|
UMLTemplateList template_params = c->getTemplateList();
|
|
if (template_params.count()) {
|
|
d << "(";
|
|
|
|
for (UMLTemplate *t = template_params.first(); t; ) {
|
|
// TODO: hm, leaving the type blank results in "class"
|
|
// so we omit it (also because "class" in this context is illegal)
|
|
if (t->getTypeName() != "class") {
|
|
d << t->getTypeName();
|
|
d << " ";
|
|
}
|
|
|
|
d << t->getName();
|
|
|
|
if ((t = template_params.next()) != NULL)
|
|
d << ", ";
|
|
}
|
|
|
|
d << ")";
|
|
}
|
|
|
|
// (e) inheritances
|
|
UMLClassifierList superclasses =
|
|
c->findSuperClassConcepts(UMLClassifier::CLASS);
|
|
UMLClassifierList superinterfaces =
|
|
c->findSuperClassConcepts(UMLClassifier::INTERFACE);
|
|
|
|
int count = superclasses.count() + superinterfaces.count();
|
|
|
|
if (count > 0) {
|
|
d << " : ";
|
|
|
|
// (f) base classes
|
|
for (UMLClassifier * concept= superclasses.first(); concept; concept = superclasses.next()) {
|
|
d << cleanName(concept->getName());
|
|
|
|
count--;
|
|
|
|
if (count>0) d << ", ";
|
|
}
|
|
|
|
// (g) interfaces
|
|
for (UMLClassifier * concept= superinterfaces.first(); concept; concept = superinterfaces.next()) {
|
|
d << cleanName(concept->getName());
|
|
|
|
count--;
|
|
|
|
if (count>0) d << ", ";
|
|
}
|
|
}
|
|
}
|
|
|
|
void DWriter::writeProtectionMod(Uml::Visibility visibility, TQTextStream &d) {
|
|
d << m_indentation << scopeToDDecl(visibility) << ":" << m_endl << m_endl;
|
|
}
|
|
|
|
void DWriter::writeAttributeDecl(Uml::Visibility visibility, UMLAttributeList &atlist, TQTextStream &d) {
|
|
if (atlist.count()==0) return;
|
|
|
|
writeProtectionMod(visibility, d);
|
|
|
|
for(UMLAttribute *at=atlist.first(); at; at=atlist.next()) {
|
|
// documentation
|
|
if (!at->getDoc().isEmpty()) {
|
|
writeComment(at->getDoc(), m_indentation, d, true);
|
|
}
|
|
|
|
d << m_indentation;
|
|
|
|
// static attribute?
|
|
if (at->getStatic()) d << "static ";
|
|
|
|
// method return type
|
|
d << fixTypeName(at->getTypeName()) << " ";
|
|
|
|
// TODO: find out whether this class has accessors or not
|
|
bool hasAccessorMethods = true;
|
|
|
|
// attribute name
|
|
if (hasAccessorMethods) {
|
|
d << "m_";
|
|
}
|
|
d << cleanName(at->getName());
|
|
|
|
// initial value
|
|
TQString initVal = fixInitialStringDeclValue(at->getInitialValue(), at->getTypeName());
|
|
if (!initVal.isEmpty()) d << " = " << initVal;
|
|
d << ";" << m_endl << m_endl;
|
|
}
|
|
}
|
|
|
|
void DWriter::writeAttributeDecls(UMLAttributeList &atpub, UMLAttributeList &atprot,
|
|
UMLAttributeList &atpriv, TQTextStream &d ) {
|
|
|
|
writeAttributeDecl(Uml::Visibility::Public, atpub, d);
|
|
writeAttributeDecl(Uml::Visibility::Protected, atprot, d);
|
|
writeAttributeDecl(Uml::Visibility::Private, atpriv, d);
|
|
//TODO: export and package
|
|
}
|
|
|
|
void DWriter::writeAttributeMethods(UMLAttributeList &atpub, Uml::Visibility visibility, TQTextStream &d) {
|
|
if (atpub.count()==0) return;
|
|
|
|
writeProtectionMod(visibility, d);
|
|
|
|
for(UMLAttribute *at=atpub.first(); at; at=atpub.next()) {
|
|
TQString fieldName = cleanName(at->getName());
|
|
writeSingleAttributeAccessorMethods(
|
|
at->getTypeName(), "m_" + fieldName, fieldName, at->getDoc(),
|
|
visibility, Uml::chg_Changeable, at->getStatic(), d);
|
|
}
|
|
}
|
|
|
|
void DWriter::writeComment(const TQString &comment, const TQString &myIndent,
|
|
TQTextStream &d, bool dDocStyle) {
|
|
if(dDocStyle) {
|
|
d << myIndent << "/**" << m_endl;
|
|
}
|
|
|
|
TQStringList lines = TQStringList::split("\n", comment);
|
|
|
|
if (lines.count() == 0) lines << comment;
|
|
|
|
for (uint i = 0; i < lines.count(); ++i) {
|
|
TQString tmp = lines[i];
|
|
|
|
while (tmp.length() > 77) {
|
|
uint l = tmp.left(77).findRev(' ');
|
|
if (l < 1) l = tmp.find(' ', 77);
|
|
if (l < 1 || l > tmp.length()) {
|
|
d << myIndent << (dDocStyle ? " * " : "// ") << tmp << m_endl;
|
|
break;
|
|
}
|
|
d << myIndent << (dDocStyle ? " * " : "// ") << tmp.left(l) << m_endl;
|
|
tmp = tmp.right(tmp.length() - l);
|
|
}
|
|
|
|
d << myIndent << (dDocStyle ? " * " : "// ") << tmp << m_endl;
|
|
}
|
|
|
|
if(dDocStyle) {
|
|
d << myIndent << " */" << m_endl;
|
|
}
|
|
}
|
|
|
|
void DWriter::writeDocumentation(TQString header, TQString body, TQString end, TQString indent, TQTextStream &d) {
|
|
d << indent << "/**" << m_endl;
|
|
if (!header.isEmpty())
|
|
d << formatDoc(header, indent+" * ");
|
|
if (!body.isEmpty())
|
|
d << formatDoc(body, indent+" * ");
|
|
if (!end.isEmpty())
|
|
{
|
|
TQStringList lines = TQStringList::split( "\n", end);
|
|
for (uint i= 0; i < lines.count(); i++)
|
|
d << formatDoc(lines[i], indent + " * ");
|
|
}
|
|
d<<indent<< " */" << m_endl;
|
|
}
|
|
|
|
void DWriter::writeAssociationDecls(UMLAssociationList associations, Uml::IDType id, TQTextStream &d) {
|
|
|
|
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)
|
|
printRoleB = true;
|
|
|
|
if (a->getObjectId(Uml::B) == id)
|
|
printRoleA = true;
|
|
|
|
// First: we insert documentaion for association IF it has either role AND some documentation (!)
|
|
if ((printRoleA || printRoleB) && !(a->getDoc().isEmpty()))
|
|
writeComment(a->getDoc(), m_indentation, d);
|
|
|
|
// print RoleB decl
|
|
if (printRoleB)
|
|
{
|
|
TQString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::B)));
|
|
writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::B), a->getMulti(Uml::B), a->getRoleDoc(Uml::B), a->getVisibility(Uml::B), d);
|
|
}
|
|
|
|
// print RoleA decl
|
|
if (printRoleA)
|
|
{
|
|
TQString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::A)));
|
|
writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::A), a->getMulti(Uml::A), a->getRoleDoc(Uml::A), a->getVisibility(Uml::A), d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DWriter::writeAssociationRoleDecl(TQString fieldClassName,
|
|
TQString roleName, TQString multi,
|
|
TQString doc, Uml::Visibility /*visib*/, TQTextStream &d) {
|
|
// ONLY write out IF there is a rolename given
|
|
// otherwise its not meant to be declared in the code
|
|
if (roleName.isEmpty()) return;
|
|
|
|
if (!doc.isEmpty()) {
|
|
writeComment(doc, m_indentation, d);
|
|
}
|
|
|
|
bool hasAccessors = true;
|
|
|
|
// 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]$"))) {
|
|
d << m_indentation << fieldClassName << " ";
|
|
|
|
if (hasAccessors) d << "m_";
|
|
|
|
d << deCapitaliseFirstLetter(roleName) << ";";
|
|
} else {
|
|
d << m_indentation << fieldClassName << "[] ";
|
|
//TODO: templated containers
|
|
|
|
if (hasAccessors) d << "m_";
|
|
|
|
d << pluralize(deCapitaliseFirstLetter(roleName)) << ";";
|
|
// from here we could initialize default values, or put in an init() section
|
|
// of the constructors
|
|
}
|
|
|
|
// always put space between this and following decl, if any
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
void DWriter::writeAssociationMethods (UMLAssociationList associations, UMLClassifier *thisClass, TQTextStream &d) {
|
|
if( forceSections() || !associations.isEmpty() ) {
|
|
for(UMLAssociation *a = associations.first(); a; a = associations.next()) {
|
|
// insert the methods to access the role of the other
|
|
// class in the code of this one
|
|
if (a->getObjectId(Uml::A) == thisClass->getID()) {
|
|
// only write out IF there is a rolename given
|
|
if(!a->getRoleName(Uml::B).isEmpty()) {
|
|
TQString fieldClassName = getUMLObjectName(a->getObject(Uml::B));
|
|
writeAssociationRoleMethod(fieldClassName,
|
|
a->getRoleName(Uml::B),
|
|
a->getMulti(Uml::B), a->getRoleDoc(Uml::B),
|
|
a->getVisibility(Uml::B),
|
|
a->getChangeability(Uml::B), d);
|
|
}
|
|
}
|
|
|
|
if (a->getObjectId(Uml::B) == thisClass->getID()) {
|
|
// only write out IF there is a rolename given
|
|
if(!a->getRoleName(Uml::A).isEmpty()) {
|
|
TQString fieldClassName = getUMLObjectName(a->getObject(Uml::A));
|
|
writeAssociationRoleMethod(fieldClassName, a->getRoleName(Uml::A),
|
|
a->getMulti(Uml::A),
|
|
a->getRoleDoc(Uml::A),
|
|
a->getVisibility(Uml::A),
|
|
a->getChangeability(Uml::A),
|
|
d);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DWriter::writeAssociationRoleMethod (TQString fieldClassName, TQString roleName, TQString multi,
|
|
TQString description, Uml::Visibility visib, Uml::Changeability_Type change,
|
|
TQTextStream &d) {
|
|
if(multi.isEmpty() || multi.contains(TQRegExp("^[01]$"))) {
|
|
TQString fieldVarName = "m_" + deCapitaliseFirstLetter(roleName);
|
|
|
|
writeSingleAttributeAccessorMethods(
|
|
fieldClassName, fieldVarName, roleName, description, visib, change, false, d);
|
|
} else {
|
|
TQString fieldVarName = "m_" + pluralize(deCapitaliseFirstLetter(roleName));
|
|
|
|
writeVectorAttributeAccessorMethods(
|
|
fieldClassName, fieldVarName, pluralize(roleName), description, visib, change, d);
|
|
}
|
|
}
|
|
|
|
void DWriter::writeVectorAttributeAccessorMethods (TQString fieldClassName, TQString fieldVarName,
|
|
TQString fieldName, TQString description,
|
|
Uml::Visibility /*visibility*/, Uml::Changeability_Type changeType,
|
|
TQTextStream &d) {
|
|
|
|
fieldClassName = fixTypeName(fieldClassName);
|
|
TQString fieldNameUP = unPluralize(fieldName);
|
|
TQString fieldNameUC = Codegen_Utils::capitalizeFirstLetter(fieldNameUP);
|
|
|
|
// ONLY IF changeability is NOT Frozen
|
|
if (changeType != Uml::chg_Frozen) {
|
|
writeDocumentation("Adds a " + fieldNameUP + " to the list of " +
|
|
fieldName + '.', description, "", m_indentation, d);
|
|
|
|
d << m_indentation << "void add" << fieldNameUC << "(";
|
|
d << fieldClassName << " new" << fieldNameUC << ") {";
|
|
d << startline << m_indentation << fieldVarName << " ~= new" << fieldNameUC << ";";
|
|
d << startline << "}" << m_endl << m_endl;
|
|
}
|
|
|
|
// ONLY IF changeability is Changeable
|
|
if (changeType == Uml::chg_Changeable) {
|
|
writeDocumentation("Removes a " + fieldNameUP + " from the list of " +
|
|
fieldName + '.', description, "", m_indentation, d);
|
|
|
|
d << m_indentation << "void remove" << fieldNameUC << "(";
|
|
d << fieldClassName << " " << fieldNameUP << ") {" << startline;
|
|
d << m_indentation << "int idx = " << fieldVarName << ".length;" << startline;
|
|
d << m_indentation << "foreach(i, o; " << fieldVarName << ")" << startline;
|
|
d << m_indentation << m_indentation << "if (o && o == " << fieldNameUP << ") {" << startline;
|
|
d << m_indentation << m_indentation << m_indentation << "idx = i;" << startline;
|
|
d << m_indentation << m_indentation << m_indentation << "break;" << startline;
|
|
d << m_indentation << m_indentation << "}" << m_endl << startline;
|
|
d << m_indentation << fieldVarName << " = " << fieldVarName;
|
|
d << "[0..idx] ~ " << fieldVarName << "[idx..$];" << startline;
|
|
d << "}" << m_endl << m_endl;
|
|
}
|
|
|
|
// always allow getting the list of stuff
|
|
writeDocumentation("Returns the list of " + fieldName + '.',
|
|
description, "@return List of " + fieldName + '.',
|
|
m_indentation, d);
|
|
|
|
d << m_indentation << fieldClassName << "[] get" << fieldName << "() {";
|
|
d << startline << m_indentation << "return " << fieldVarName << ";";
|
|
d << startline << "}" << m_endl << m_endl;
|
|
}
|
|
|
|
|
|
void DWriter::writeSingleAttributeAccessorMethods(TQString fieldClassName,
|
|
TQString fieldVarName, TQString fieldName, TQString description, Uml::Visibility /*visibility*/,
|
|
Uml::Changeability_Type change, bool isFinal, TQTextStream &d) {
|
|
|
|
fieldClassName = fixTypeName(fieldClassName);
|
|
TQString fieldNameUC = Codegen_Utils::capitalizeFirstLetter(fieldName);
|
|
if (fieldName.left(2) == "m_") fieldName = fieldName.right(fieldName.length()-2);
|
|
|
|
// set method
|
|
if (change == Uml::chg_Changeable && !isFinal) {
|
|
writeDocumentation("Sets the value of " + fieldName + '.', description,
|
|
"@param new" + fieldNameUC + " The new value of " + fieldName + '.',
|
|
m_indentation, d);
|
|
|
|
d << m_indentation << fieldClassName << " " << fieldName << "(";
|
|
d << fieldClassName << " new" << fieldNameUC << ") {";
|
|
d << startline << m_indentation << "return " << fieldVarName << " = new" << fieldNameUC << ";";
|
|
d << startline << "}" << m_endl << m_endl;
|
|
}
|
|
|
|
// get method
|
|
writeDocumentation("Returns the value of " + fieldName + '.', description,
|
|
"@return The value of " + fieldName + '.',
|
|
m_indentation, d);
|
|
|
|
d << m_indentation << fieldClassName << " " << fieldName << "() {";
|
|
d << startline << m_indentation << "return " << fieldVarName << ";";
|
|
d << startline << "}" << m_endl << m_endl;
|
|
}
|
|
|
|
void DWriter::writeConstructor(UMLClassifier *c, TQTextStream &d) {
|
|
|
|
if (forceDoc())
|
|
{
|
|
d<<startline;
|
|
writeComment("", m_indentation, d);
|
|
writeComment("Constructors", m_indentation, d);
|
|
writeComment("", m_indentation, d);
|
|
writeBlankLine(d);
|
|
}
|
|
|
|
// write the first constructor
|
|
TQString className = cleanName(c->getName());
|
|
d << m_indentation << "public this("<<") { }";
|
|
|
|
}
|
|
|
|
// IF the type is "string" we need to declare it as
|
|
// the D Object "String" (there is no string primative in D).
|
|
// Same thing again for "bool" to "boolean"
|
|
TQString DWriter::fixTypeName(const TQString& string) {
|
|
if (string.isEmpty())
|
|
return "void";
|
|
if (string == "string")
|
|
return "char[]";
|
|
if (string == "unsigned short")
|
|
return "ushort";
|
|
if (string == "unsigned int")
|
|
return "uint";
|
|
if (string == "unsigned long")
|
|
return "ulong";
|
|
return string;
|
|
}
|
|
|
|
TQStringList DWriter::defaultDatatypes() {
|
|
TQStringList l;
|
|
l << "void"
|
|
<< "bool"
|
|
<< "byte"
|
|
<< "ubyte"
|
|
<< "short"
|
|
<< "ushort"
|
|
<< "int"
|
|
<< "uint"
|
|
<< "long"
|
|
<< "ulong"
|
|
<< "cent"
|
|
<< "ucent"
|
|
<< "float"
|
|
<< "double"
|
|
<< "real"
|
|
<< "ifloat"
|
|
<< "idouble"
|
|
<< "ireal"
|
|
<< "cfloat"
|
|
<< "cdouble"
|
|
<< "creal"
|
|
<< "char"
|
|
<< "wchar"
|
|
<< "dchar";
|
|
return l;
|
|
}
|
|
|
|
|
|
bool DWriter::compareDMethod(UMLOperation *op1, UMLOperation *op2) {
|
|
if (op1 == NULL || op2 == NULL)
|
|
return false;
|
|
if (op1 == op2)
|
|
return true;
|
|
if (op1->getName() != op2->getName())
|
|
return false;
|
|
UMLAttributeList atl1 = op1->getParmList();
|
|
UMLAttributeList atl2 = op2->getParmList();
|
|
if (atl1.count() != atl2.count())
|
|
return false;
|
|
UMLAttribute *at1;
|
|
UMLAttribute *at2;
|
|
for (at1 = atl1.first(), at2 = atl2.first(); at1 && at2 ; at1 = atl1.next(),at2 = atl2.next())
|
|
{
|
|
if (at1->getTypeName() != at2->getTypeName())
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
bool DWriter::dMethodInList(UMLOperation *umlOp, UMLOperationList &opl) {
|
|
for (UMLOperation *op = opl.first(); op; op = opl.next()) {
|
|
if (DWriter::compareDMethod(op, umlOp)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DWriter::getSuperImplementedOperations(UMLClassifier *c, UMLOperationList &yetImplementedOpList ,UMLOperationList &toBeImplementedOpList, bool noClassInPath) {
|
|
UMLClassifierList superClasses = c->findSuperClassConcepts();
|
|
|
|
for (UMLClassifier *concept= superClasses.first(); concept; concept = superClasses.next())
|
|
{
|
|
getSuperImplementedOperations(concept, yetImplementedOpList, toBeImplementedOpList, (concept->isInterface() && noClassInPath));
|
|
UMLOperationList opl = concept->getOpList();
|
|
for (UMLOperation *op = opl.first(); op; op = opl.next()) {
|
|
if (concept->isInterface() && noClassInPath) {
|
|
if (!DWriter::dMethodInList(op,toBeImplementedOpList))
|
|
toBeImplementedOpList.append(op);
|
|
}
|
|
else
|
|
{
|
|
if (!DWriter::dMethodInList(op, yetImplementedOpList))
|
|
yetImplementedOpList.append(op);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DWriter::getInterfacesOperationsToBeImplemented(UMLClassifier *c, UMLOperationList &opList ) {
|
|
UMLOperationList yetImplementedOpList;
|
|
UMLOperationList toBeImplementedOpList;
|
|
|
|
getSuperImplementedOperations(c,yetImplementedOpList, toBeImplementedOpList);
|
|
for (UMLOperation *op = toBeImplementedOpList.first(); op; op = toBeImplementedOpList.next())
|
|
{
|
|
if ( ! DWriter::dMethodInList(op, yetImplementedOpList) && ! DWriter::dMethodInList(op, opList) )
|
|
opList.append(op);
|
|
}
|
|
}
|
|
|
|
void DWriter::writeOperations(UMLClassifier *c, TQTextStream &d) {
|
|
UMLOperationList opl;
|
|
UMLOperationList oppub,opprot,oppriv;
|
|
oppub.setAutoDelete(false);
|
|
opprot.setAutoDelete(false);
|
|
oppriv.setAutoDelete(false);
|
|
|
|
//sort operations by scope first and see if there are abstract methods
|
|
opl = c->getOpList();
|
|
if (! c->isInterface()) {
|
|
getInterfacesOperationsToBeImplemented(c, opl);
|
|
}
|
|
for (UMLOperation *op = opl.first(); op; op = opl.next()) {
|
|
switch(op->getVisibility()) {
|
|
case Uml::Visibility::Public:
|
|
oppub.append(op);
|
|
break;
|
|
case Uml::Visibility::Protected:
|
|
opprot.append(op);
|
|
break;
|
|
case Uml::Visibility::Private:
|
|
oppriv.append(op);
|
|
break;
|
|
default: //TODO: package, export
|
|
break;
|
|
}
|
|
}
|
|
|
|
// do people REALLY want these comments? Hmm.
|
|
/*
|
|
if(forceSections() || oppub.count())
|
|
{
|
|
writeComment("public operations",m_indentation,d);
|
|
writeBlankLine(d);
|
|
}
|
|
*/
|
|
|
|
if (oppub.count() > 0) {
|
|
writeProtectionMod(Uml::Visibility::Public, d);
|
|
|
|
writeOperations(oppub,d);
|
|
}
|
|
|
|
if (opprot.count() > 0) {
|
|
writeProtectionMod(Uml::Visibility::Protected, d);
|
|
|
|
writeOperations(opprot, d);
|
|
}
|
|
|
|
if (oppriv.count() > 0) {
|
|
writeProtectionMod(Uml::Visibility::Private, d);
|
|
|
|
writeOperations(oppriv, d);
|
|
}
|
|
|
|
}
|
|
|
|
void DWriter::writeOperations(UMLOperationList &oplist, TQTextStream &d) {
|
|
UMLAttributeList atl;
|
|
TQString str;
|
|
|
|
// generate method decl for each operation given
|
|
for(UMLOperation *op=oplist.first(); op; op=oplist.next()) {
|
|
TQString returnStr = "";
|
|
// write documentation
|
|
|
|
TQString methodReturnType = fixTypeName(op->getTypeName());
|
|
|
|
//TODO: return type comment
|
|
if(methodReturnType != "void") {
|
|
returnStr += "@return " + methodReturnType + m_endl;
|
|
}
|
|
|
|
str = ""; // reset for next method
|
|
if (op->getAbstract() && !isInterface) str += "abstract ";
|
|
if (op->getStatic()) str += "static ";
|
|
|
|
str += methodReturnType + ' ' +cleanName(op->getName()) + '(';
|
|
|
|
atl = op->getParmList();
|
|
int i = atl.count();
|
|
int j = 0;
|
|
for (UMLAttribute *at = atl.first(); at; at = atl.next(), j++) {
|
|
TQString typeName = fixTypeName(at->getTypeName());
|
|
TQString atName = cleanName(at->getName());
|
|
str += typeName + ' ' + atName +
|
|
(!(at->getInitialValue().isEmpty()) ?
|
|
(TQString(" = ")+at->getInitialValue()) :
|
|
TQString(""))
|
|
+ ((j < i-1)?", ":"");
|
|
returnStr += "@param " + atName+' '+at->getDoc() + m_endl;
|
|
}
|
|
|
|
str+= ')';
|
|
|
|
// method only gets a body IF its not abstract
|
|
if (op->getAbstract() || isInterface)
|
|
str += ';'; // terminate now
|
|
else
|
|
str += startline + '{' + startline + '}'; // empty method body
|
|
|
|
// write it out
|
|
writeDocumentation("", op->getDoc(), returnStr, m_indentation, d);
|
|
d << m_indentation << str << m_endl << m_endl;
|
|
}
|
|
}
|
|
|
|
TQString DWriter::fixInitialStringDeclValue(TQString value, TQString type) {
|
|
// check for strings only
|
|
if (!value.isEmpty() && type == "String") {
|
|
if (!value.startsWith("\""))
|
|
value.prepend("\"");
|
|
if (!value.endsWith("\""))
|
|
value.append("\"");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
TQString DWriter::scopeToDDecl(Uml::Visibility scope) {
|
|
TQString scopeString;
|
|
|
|
switch(scope) {
|
|
case Uml::Visibility::Public: scopeString = "public"; break;
|
|
case Uml::Visibility::Protected: scopeString = "protected"; break;
|
|
case Uml::Visibility::Private: scopeString = "private"; break;
|
|
default: break; //TODO: package and export
|
|
}
|
|
|
|
return scopeString;
|
|
}
|
|
|
|
// methods like this _shouldn't_ be needed IF we properly did things thruought the code.
|
|
TQString DWriter::getUMLObjectName(UMLObject *obj) {
|
|
return(obj!=0)?obj->getName():TQString("NULL");
|
|
}
|
|
|
|
TQString DWriter::deCapitaliseFirstLetter(TQString string) {
|
|
string.replace( 0, 1, string[0].lower());
|
|
return string;
|
|
}
|
|
|
|
TQString DWriter::pluralize(TQString string) {
|
|
return string + (string.right(1) == "s" ? "es" : "s");
|
|
}
|
|
|
|
TQString DWriter::unPluralize(TQString string) {
|
|
// does not handle special cases liek datum -> data, etc.
|
|
|
|
if (string.length() > 2 && string.right(3) == "ses") {
|
|
return string.left(string.length() - 2);
|
|
}
|
|
|
|
if (string.right(1) == "s") {
|
|
return string.left(string.length() - 1);
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
void DWriter::writeBlankLine(TQTextStream &d) {
|
|
d << m_endl;
|
|
}
|
|
|