summaryrefslogtreecommitdiffstats
path: root/umbrello/umbrello/codegenerators/csharpwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'umbrello/umbrello/codegenerators/csharpwriter.cpp')
-rw-r--r--umbrello/umbrello/codegenerators/csharpwriter.cpp725
1 files changed, 725 insertions, 0 deletions
diff --git a/umbrello/umbrello/codegenerators/csharpwriter.cpp b/umbrello/umbrello/codegenerators/csharpwriter.cpp
new file mode 100644
index 00000000..73975b48
--- /dev/null
+++ b/umbrello/umbrello/codegenerators/csharpwriter.cpp
@@ -0,0 +1,725 @@
+/***************************************************************************
+ * *
+ * 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 *
+ * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
+ ***************************************************************************/
+
+//
+// C++ Implementation: csharpwriter
+//
+#include "csharpwriter.h"
+
+#include <kdebug.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+
+#include "../uml.h"
+#include "../umldoc.h"
+#include "../folder.h"
+#include "../classifier.h"
+#include "../association.h"
+#include "../attribute.h"
+#include "../operation.h"
+#include "../umlnamespace.h"
+
+static const char *reserved_words[] = {
+ "abstract",
+ "as",
+ "base",
+ "bool",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "checked",
+ "class",
+ "const",
+ "continue",
+ "decimal",
+ "default",
+ "delegate",
+ "do",
+ "double",
+ "else",
+ "enum",
+ "event",
+ "explicit",
+ "extern",
+ "false",
+ "finally",
+ "for",
+ "foreach",
+ "goto",
+ "if",
+ "implicit",
+ "in",
+ "int",
+ "interface",
+ "internal",
+ "is",
+ "lock",
+ "long",
+ "namespace",
+ "new",
+ "null",
+ "object",
+ "operator",
+ "out",
+ "override",
+ "params",
+ "private",
+ "protected",
+ "public",
+ "readonly",
+ "ref",
+ "return",
+ "sbyte",
+ "sealed",
+ "short",
+ "sizeof",
+ "stackalloc",
+ "static",
+ "string",
+ "struct",
+ "switch",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typeof",
+ "uint",
+ "ulong",
+ "unchecked",
+ "unsafe",
+ "ushort",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "while",
+ 0
+};
+
+CSharpWriter::CSharpWriter()
+ : SimpleCodeGenerator()
+{
+}
+
+
+CSharpWriter::~CSharpWriter()
+{
+}
+
+QStringList CSharpWriter::defaultDatatypes() {
+ QStringList l;
+ l.append("bool");
+ l.append("byte");
+ l.append("char");
+ l.append("decimal");
+ l.append("double");
+ l.append("fixed");
+ l.append("float");
+ l.append("fixed");
+ l.append("float");
+ l.append("int");
+ l.append("long");
+ l.append("object");
+ l.append("sbyte");
+ l.append("short");
+ l.append("string");
+ l.append("uint");
+ l.append("ulong");
+ l.append("ushort");
+ return l;
+}
+
+void CSharpWriter::writeClass(UMLClassifier *c) {
+ if (!c) {
+ kDebug()<<"Cannot write class of NULL concept!" << endl;
+ return;
+ }
+
+ QString classname = cleanName(c->getName());
+ //find an appropriate name for our file
+ QString fileName = findFileName(c, ".cs");
+ if (fileName.isEmpty()) {
+ emit codeGenerated(c, false);
+ return;
+ }
+
+ QFile filecs;
+ if (!openFile(filecs, fileName)) {
+ emit codeGenerated(c, false);
+ return;
+ }
+ QTextStream cs(&filecs);
+
+ //////////////////////////////
+ //Start generating the code!!
+ /////////////////////////////
+
+
+ //try to find a heading file (license, coments, etc)
+ QString str;
+ str = getHeadingFile(".cs");
+ if (!str.isEmpty()) {
+ str.replace(QRegExp("%filename%"),fileName);
+ str.replace(QRegExp("%filepath%"),filecs.name());
+ cs<<str<<m_endl;
+ }
+
+ UMLDoc *umldoc = UMLApp::app()->getDocument();
+ UMLFolder *logicalView = umldoc->getRootFolder(Uml::mt_Logical);
+
+ // write generic includes
+ cs << "using System;" << m_endl;
+ cs << "using System.Text;" << m_endl;
+ cs << "using System.Collections;" << m_endl;
+ cs << "using System.Collections.Generic;" << m_endl << m_endl;
+
+ //write includes and namespace
+
+ UMLPackage *container = c->getUMLPackage();
+ if (container == logicalView)
+ container = NULL;
+
+ UMLPackageList includes;
+ findObjectsRelated(c, includes);
+ m_seenIncludes.clear();
+ //m_seenIncludes.append(logicalView);
+ if (includes.count()) {
+ UMLPackage *p;
+ for (UMLPackageListIt it(includes); (p = it.current()) != NULL; ++it) {
+ UMLClassifier *cl = dynamic_cast<UMLClassifier*>(p);
+ if (cl)
+ p = cl->getUMLPackage();
+ if (p != logicalView && m_seenIncludes.findRef(p) == -1 && p != container) {
+ cs << "using " << p->getFullyQualifiedName(".") << ";" << m_endl;
+ m_seenIncludes.append(p);
+ }
+ }
+ cs << m_endl;
+ }
+
+ m_container_indent = "";
+
+ if (container) {
+ cs << "namespace " << container->getFullyQualifiedName(".") << m_endl;
+ cs << "{" << m_endl << m_endl;
+ m_container_indent = m_indentation;
+ m_seenIncludes.append(container);
+ }
+
+ //Write class Documentation if there is somthing or if force option
+ if (forceDoc() || !c->getDoc().isEmpty()) {
+ cs << m_container_indent << "/// <summary>" << m_endl;
+ cs << formatDoc(c->getDoc(), m_container_indent + "/// " );
+ cs << m_container_indent << "/// </summary>" << m_endl ;
+ }
+
+ UMLClassifierList superclasses = c->getSuperClasses();
+ UMLAssociationList aggregations = c->getAggregations();
+ UMLAssociationList compositions = c->getCompositions();
+ UMLAssociationList realizations = c->getRealizations();
+ bool isInterface = c->isInterface();
+ m_unnamedRoles = 1;
+
+ cs << m_container_indent << "public ";
+
+ //check if it is an interface or regular class
+ if (isInterface) {
+ cs << "interface " << classname;
+ } else {
+ //check if class is abstract and / or has abstract methods
+ if (c->getAbstract() || c->hasAbstractOps())
+ cs << "abstract ";
+
+ cs << "class " << classname << (superclasses.count() > 0 ? " : ":"");
+
+ // write baseclass, ignore interfaces, write error on multiple inheritance
+ if (superclasses.count() > 0) {
+ UMLClassifier *obj;
+ int supers = 0;
+ for (obj = superclasses.first(); obj; obj = superclasses.next()) {
+ if (!obj->isInterface()) {
+ if (supers > 0) {
+ cs << " // AND ";
+ }
+ cs << cleanName(obj->getName());
+ supers++;
+ }
+ }
+ if (supers > 1) {
+ cs << m_endl << "//WARNING: C# does not support multiple inheritance but there is more than 1 superclass defined in your UML model!" << m_endl;
+ }
+ }
+ //check for realizations
+ UMLAssociationList realizations = c->getRealizations();
+ UMLAssociation *a;
+
+ if (!realizations.isEmpty()) {
+ for (a = realizations.first(); a; a = realizations.next()) {
+ UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
+ if(real != c) {
+ // write list of realizations
+ cs << ", " << real->getName();
+ }
+
+ }
+ }
+ }
+ cs << m_endl << m_container_indent << '{' << m_endl;
+
+ //associations
+ if (forceSections() || !aggregations.isEmpty()) {
+ cs << m_endl << m_container_indent << m_indentation << "#region Aggregations" << m_endl << m_endl;
+ writeAssociatedAttributes(aggregations, c, cs);
+ cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl;
+ }
+
+ //compositions
+ if (forceSections() || !compositions.isEmpty()) {
+ cs << m_endl << m_container_indent << m_indentation << "#region Compositions" << m_endl << m_endl;
+ writeAssociatedAttributes(compositions, c, cs);
+ cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl;
+ }
+
+ //attributes
+ // FIXME: C# allows Properties in interface!
+ if (!isInterface)
+ writeAttributes(c, cs);
+
+ //operations
+ writeOperations(c, cs);
+
+ //finish file
+ cs << m_endl << m_container_indent << "}" << m_endl << m_endl; // close class
+
+ if (container) {
+ cs << "} // end of namespace "
+ << container->getFullyQualifiedName(".") << m_endl << m_endl;
+ }
+
+ //close files and notfiy we are done
+ filecs.close();
+ emit codeGenerated(c, true);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Helper Methods
+
+void CSharpWriter::writeOperations(UMLClassifier *c, QTextStream &cs) {
+
+ //Lists to store operations sorted by scope
+ UMLOperationList oppub,opprot,oppriv;
+
+ bool isInterface = c->isInterface();
+ bool generateErrorStub = true;
+
+ oppub.setAutoDelete(false);
+ opprot.setAutoDelete(false);
+ oppriv.setAutoDelete(false);
+
+ //sort operations by scope first and see if there are abstract methods
+ UMLOperationList opl(c->getOpList());
+ 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:
+ break;
+ }
+ }
+
+ // write realizations (recursive)
+ UMLAssociationList realizations = c->getRealizations();
+
+ if (!isInterface && !realizations.isEmpty()) {
+ writeRealizationsRecursive(c, &realizations, cs);
+ }
+
+ // write public operations
+ if (forceSections() || !oppub.isEmpty()) {
+ cs << m_endl << m_container_indent << m_indentation << "#region Public methods" << m_endl << m_endl;
+ writeOperations(oppub,cs,isInterface,false,generateErrorStub);
+ cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+ }
+
+ // write protected operations
+ if (forceSections() || !opprot.isEmpty()) {
+ cs << m_endl << m_container_indent << m_indentation << "#region Protected methods" << m_endl << m_endl;
+ writeOperations(opprot,cs,isInterface,false,generateErrorStub);
+ cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+ }
+
+ // write private operations
+ if (forceSections() || !oppriv.isEmpty()) {
+ cs << m_endl << m_container_indent << m_indentation << "#region Private methods" << m_endl << m_endl;
+ writeOperations(oppriv,cs,isInterface,false,generateErrorStub);
+ cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+ }
+
+ // write superclasses abstract methods
+ UMLClassifierList superclasses = c->getSuperClasses();
+
+ if (!isInterface && !c->getAbstract() && !c->hasAbstractOps()
+ && superclasses.count() > 0) {
+ writeOverridesRecursive(&superclasses, cs);
+ }
+
+}
+
+void CSharpWriter::writeOverridesRecursive(UMLClassifierList *superclasses, QTextStream &cs) {
+ // oplist for implemented abstract operations
+ UMLOperationList opabstract;
+ opabstract.setAutoDelete(false);
+ UMLClassifier *obj;
+
+ for (obj = superclasses->first(); obj; obj = superclasses->next()) {
+ if (!obj->isInterface() && obj->hasAbstractOps()) {
+ // collect abstract ops
+ UMLOperationList opl(obj->getOpList());
+ for (UMLOperation *op = opl.first(); op ; op = opl.next()) {
+ if (op->getAbstract()) {
+ opabstract.append(op);
+ }
+ }
+
+ // write abstract implementations
+ cs << m_endl << m_container_indent << m_indentation << "#region " << obj->getName() << " members" << m_endl << m_endl;
+ writeOperations(opabstract,cs,false,true,true);
+ cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+
+ opabstract.clear();
+ }
+ // Recurse to parent superclasses
+ UMLClassifierList superRecursive = obj->getSuperClasses();
+ UMLClassifierList *superRecursivePtr =& superRecursive;
+ if (superRecursivePtr->count() > 0) {
+ writeOverridesRecursive(superRecursivePtr, cs);
+ }
+ }
+}
+void CSharpWriter::writeRealizationsRecursive(UMLClassifier *currentClass, UMLAssociationList *realizations, QTextStream &cs) {
+
+ UMLAssociation *a;
+ for (a = realizations->first(); a; a = realizations->next()) {
+
+ // we know its a classifier if its in the list
+ UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
+
+ //FIXME: Interfaces realize themselves without this condition!?
+ if (real == currentClass)
+ continue;
+
+ // collect operations of one realization
+ UMLOperationList opreal = real->getOpList();
+
+ // write realizations
+ cs << m_endl << m_container_indent << m_indentation << "#region " << real->getName() << " members" << m_endl << m_endl;
+ writeOperations(opreal,cs,false,false,true);
+ cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+
+ // Recurse to parent realizations
+ UMLAssociationList parentReal = real->getRealizations();
+ if (!parentReal.isEmpty()) {
+ writeRealizationsRecursive(real, &parentReal, cs);
+ }
+ }
+}
+
+void CSharpWriter::writeOperations(UMLOperationList opList,
+ QTextStream &cs, bool isInterface /* = false */,
+ bool isOverride /* = false */,
+ bool generateErrorStub /* = false */) {
+
+ for (UMLOperation *op=opList.first(); op ; op=opList.next()) {
+ UMLAttributeList atl = op->getParmList();
+ UMLAttribute *at;
+
+ //write method doc if we have doc || if at least one of the params has doc
+ bool writeDoc = forceDoc() || !op->getDoc().isEmpty();
+
+ for (at = atl.first(); at; at = atl.next()) {
+ writeDoc |= !at->getDoc().isEmpty();
+ }
+
+ //write method documentation
+ if (writeDoc && !isOverride)
+ {
+ cs << m_container_indent << m_indentation << "/// <summary>" << m_endl;
+ cs << formatDoc(op->getDoc(), m_container_indent + m_indentation + "/// ");
+ cs << m_container_indent << m_indentation << "/// </summary>" << m_endl;
+
+ //write parameter documentation
+ for (at = atl.first(); at; at = atl.next())
+ {
+ if (forceDoc() || !at->getDoc().isEmpty()) {
+ cs << m_container_indent << m_indentation << "/// <param name=\"" << cleanName(at->getName()) << "\">";
+ //removing newlines from parameter doc
+ cs << formatDoc(at->getDoc(), "").replace("\n", " ").remove('\r').replace(QRegExp(" $"), "");
+ cs << "</param>" << m_endl;
+ }
+ }
+
+ // FIXME: "returns" should contain documentation, not type.
+ cs << m_container_indent << m_indentation << "/// <returns>";
+ if (! op->getTypeName().isEmpty()) {
+ cs << makeLocalTypeName(op);
+ }
+ cs << "</returns>" << m_endl;
+
+ }
+
+ // method visibility
+ cs << m_container_indent << m_indentation;
+ if (!isInterface) {
+ if (!isOverride) {
+ if (op->getAbstract()) cs << "abstract ";
+ cs << op->getVisibility().toString() << " ";
+ if (op->getStatic()) cs << "static ";
+ }
+ else {
+ // method overriding an abstract parent
+ cs << op->getVisibility().toString() << " override ";
+ if (op->getStatic()) cs << "static ";
+ }
+ }
+
+ // return type (unless constructor, destructor)
+ if (!op->isLifeOperation()) {
+ if (op->getTypeName().isEmpty()) {
+ cs << "void ";
+ }
+ else {
+ cs << makeLocalTypeName(op) << " ";
+ }
+ }
+
+ // method name
+ cs << cleanName(op->getName()) << "(";
+
+ // method parameters
+ int i= atl.count();
+ int j=0;
+ for (at = atl.first(); at; at = atl.next(), j++) {
+
+ cs << makeLocalTypeName(at) << " " << cleanName(at->getName());
+
+ // no initial values in C#
+ //<< (!(at->getInitialValue().isEmpty()) ?
+ // (QString(" = ")+at->getInitialValue()) :
+ // QString(""))
+ cs << ((j < i-1)?", ":"");
+ }
+ cs << ")";
+
+ //FIXME: how to control generation of error stub?
+ if (!isInterface && (!op->getAbstract() || isOverride)) {
+ cs << m_endl << m_container_indent << m_indentation << "{" << m_endl;
+ if (generateErrorStub) {
+ cs << m_container_indent << m_indentation << m_indentation;
+ cs << "throw new Exception(\"The method or operation is not implemented.\");" << m_endl;
+ }
+ cs << m_container_indent << m_indentation << "}" << m_endl;
+ }
+ else {
+ cs << ';' << m_endl;
+ }
+ cs << m_endl;
+ }
+}
+
+void CSharpWriter::writeAttributes(UMLClassifier *c, QTextStream &cs) {
+
+ UMLAttributeList atpub, atprot, atpriv, atdefval;
+ atpub.setAutoDelete(false);
+ atprot.setAutoDelete(false);
+ atpriv.setAutoDelete(false);
+ atdefval.setAutoDelete(false);
+
+ //sort attributes by scope and see if they have a default value
+ UMLAttributeList atl = c->getAttributeList();
+ UMLAttribute *at;
+
+ for (at = atl.first(); at ; at = atl.next()) {
+ if (!at->getInitialValue().isEmpty())
+ atdefval.append(at);
+ switch (at->getVisibility()) {
+ case Uml::Visibility::Public:
+ atpub.append(at);
+ break;
+ case Uml::Visibility::Protected:
+ atprot.append(at);
+ break;
+ case Uml::Visibility::Private:
+ atpriv.append(at);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (forceSections() || atl.count())
+ cs << m_endl << m_container_indent << m_indentation << "#region Attributes" << m_endl << m_endl;
+
+ // write public attributes
+ if (forceSections() || atpub.count()) {
+ writeAttributes(atpub,cs);
+ }
+
+ // write protected attributes
+ if (forceSections() || atprot.count()) {
+ writeAttributes(atprot,cs);
+ }
+
+ // write private attributes
+ if (forceSections() || atpriv.count()) {
+ writeAttributes(atpriv,cs);
+ }
+
+ if (forceSections() || atl.count())
+ cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
+
+}
+
+
+void CSharpWriter::writeAttributes(UMLAttributeList &atList, QTextStream &cs) {
+
+ for (UMLAttribute *at = atList.first(); at ; at = atList.next()) {
+
+ bool asProperty = true;
+ if (at->getVisibility() == Uml::Visibility::Private) {
+ asProperty = false;
+ }
+ writeAttribute(at->getDoc(), at->getVisibility(), at->getStatic(),
+ makeLocalTypeName(at), at->getName(), at->getInitialValue(), asProperty, cs);
+
+ cs << m_endl;
+ } // end for
+ return;
+}
+
+void CSharpWriter::writeAssociatedAttributes(UMLAssociationList &associated, UMLClassifier *c, QTextStream &cs) {
+
+ UMLAssociation *a;
+ for (a = associated.first(); a ; a = associated.next()) {
+ if (c != a->getObject(Uml::A)) // we need to be at the A side
+ continue;
+
+ UMLObject *o = a->getObject(Uml::B);
+ if (o == NULL) {
+ kError() << "composition role B object is NULL" << endl;
+ continue;
+ }
+ // Take name and documentaton from Role, take type name from the referenced object
+ QString roleName = cleanName(a->getRoleName(Uml::B));
+ QString typeName = cleanName(o->getName());
+ if (roleName.isEmpty()) {
+ roleName = QString("UnnamedRoleB_%1").arg(m_unnamedRoles++);
+ }
+ QString roleDoc = a->getRoleDoc(Uml::B);
+
+ //FIXME:is this simple condition enough?
+ if (a->getMulti(Uml::B).isEmpty() || a->getMulti(Uml::B) == "1") {
+ // normal attribute
+ writeAttribute(roleDoc, a->getVisibility(Uml::B), false, typeName, roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
+ } else {
+ // array
+ roleDoc += "\n(Array of " + typeName + ')';
+ writeAttribute(roleDoc, a->getVisibility(Uml::B), false, "ArrayList", roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
+ }
+ }
+}
+
+void CSharpWriter::writeAttribute(QString doc, Uml::Visibility visibility, bool isStatic, QString typeName, QString name, QString initialValue, bool asProperty, QTextStream &cs) {
+
+ if (forceDoc() || !doc.isEmpty()) {
+
+ cs << m_container_indent << m_indentation << "/// <summary>" << m_endl;
+ cs << formatDoc(doc, m_container_indent + m_indentation + "/// ");
+ cs << m_container_indent << m_indentation << "/// </summary>" << m_endl;
+
+ }
+ cs << m_container_indent << m_indentation;
+ cs << visibility.toString() << " ";
+ if (isStatic) cs << "static ";
+
+ //Variable type with/without namespace path
+ cs << typeName << " ";
+
+ cs << cleanName(name);
+
+ // FIXME: may need a GUI switch to not generate as Property?
+
+ // Generate as Property if not private
+ if (asProperty)
+ {
+ cs << m_endl;
+ cs << m_container_indent << m_indentation << "{" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << "get" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << "{" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << m_indentation << "return m_" << cleanName(name) << ";" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << "}" << m_endl;
+
+ cs << m_container_indent << m_indentation << m_indentation << "set" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << "{" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << m_indentation << "m_" << cleanName(name) << " = value;" << m_endl;
+ cs << m_container_indent << m_indentation << m_indentation << "}" << m_endl;
+ cs << m_container_indent << m_indentation << "}" << m_endl;
+ cs << m_container_indent << m_indentation << "private ";
+ if (isStatic) cs << "static ";
+ cs << typeName << " m_" << cleanName(name);
+ }
+
+ if (!initialValue.isEmpty())
+ cs << " = " << initialValue;
+
+ cs << ";" << m_endl << m_endl;
+}
+
+QString CSharpWriter::makeLocalTypeName(UMLClassifierListItem *cl) {
+ UMLPackage *p = cl->getType()->getUMLPackage();
+ if (m_seenIncludes.findRef(p) != -1) {
+ return cl->getType()->getName();
+ }
+ else {
+ return cl->getTypeName();
+ }
+
+}
+
+/**
+ * returns "C#"
+ */
+Uml::Programming_Language CSharpWriter::getLanguage() {
+ return Uml::pl_CSharp;
+}
+
+const QStringList CSharpWriter::reservedKeywords() const {
+
+ static QStringList keywords;
+
+ if (keywords.isEmpty()) {
+ for (int i = 0; reserved_words[i]; i++)
+ keywords.append(reserved_words[i]);
+ }
+
+ return keywords;
+}
+
+#include "csharpwriter.moc"
+