Bug 53368 - C# Code Generation and export
Summary: C# Code Generation and export
Status: RESOLVED FIXED
Alias: None
Product: umbrello
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: unspecified Linux
: NOR wishlist
Target Milestone: ---
Assignee: Umbrello Development Group
URL:
Keywords:
: 65185 (view as bug list)
Depends on:
Blocks:
 
Reported: 2003-01-24 18:54 UTC by Jonathan Riddell
Modified: 2022-02-19 21:28 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments
Types, namespaces and formatting fixes (16.70 KB, patch)
2007-02-20 02:13 UTC, Ferenc Veres
Details
Compositions, Aggregations are generated (15.78 KB, patch)
2007-02-21 03:30 UTC, Ferenc Veres
Details
Abstract overrides, interfaces are generated, arrays ok. (14.61 KB, patch)
2007-02-22 03:09 UTC, Ferenc Veres
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Riddell 2003-01-24 18:54:47 UTC
Version:            (using KDE Devel)
Compiler:          gcc 2.95 
OS:          Linux

Someone wanted C-Hash code generation. 

I was wondering,
with C# becoming more important every day (.NET/MONO),
will there (or is there already) support for C# code
generation ?
Comment 1 Sebastian Stein 2003-09-29 18:31:25 UTC
*** Bug 65185 has been marked as a duplicate of this bug. ***
Comment 2 Heiko Nardmann 2004-08-18 06:20:42 UTC
Please give a reference/URL to C# standard definition. A coder needs this information to start with a simple generator which can be enhanced later.
I already have a list of reserved keywords but no information about syntax and so on.
Comment 3 Sebastian Stein 2004-08-18 12:02:55 UTC
Heiko Nardmann <heiko.nardmann@onlinehome.de> [040818 11:46]:
> Please give a reference/URL to C# standard definition. A coder needs this
> information to start with a simple generator which can be enhanced later.
> I already have a list of reserved keywords but no information about syntax
> and so on.

As you are a German reader, I like to point to:

http://www.galileocomputing.de/openbook/csharp/

Also, I can test the patches, if they produce correct C# code.

Sebastian
Comment 4 Sebastian Stein 2004-08-18 12:06:00 UTC
Heiko Nardmann <heiko.nardmann@onlinehome.de> [040818 11:46]:
> Please give a reference/URL to C# standard definition. A coder needs this
> information to start with a simple generator which can be enhanced later.

Please take a look at the following page:

http://www.wordiq.com/definition/C_Sharp#Standardization

There are some links listed directly to the official standard.

Sebastian
Comment 5 Oliver Kellogg 2007-02-15 06:11:09 UTC
Sebastian left the dev team so this is wide open.
Comment 6 Oliver Kellogg 2007-02-19 20:28:48 UTC
SVN commit 635308 by okellogg:

Add C# code generator by Ferenc Veres, see
http://www.geeksoc.org/~jr/umbrello/uml-devel/10058.html
Still work in progress - his comments no. 3 to 11 are not yet solved.
Perhaps somebody in the know of C# could help out.
CCBUG:53368


 M  +2 -1      ChangeLog  
 M  +1 -0      THANKS  
 M  +1 -1      umbrello/codegenerators/Makefile.am  
 M  +4 -4      umbrello/codegenerators/codegenfactory.cpp  
 A             umbrello/codegenerators/csharpwriter.cpp   [License: UNKNOWN]
 A             umbrello/codegenerators/csharpwriter.h   [License: UNKNOWN]
 M  +4 -4      umbrello/model_utils.cpp  
 M  +2 -0      umbrello/uml.cpp  
 M  +1 -1      umbrello/umlnamespace.h  


--- branches/KDE/3.5/kdesdk/umbrello/ChangeLog #635307:635308
@@ -1,6 +1,7 @@
 Version 1.5.7
 
-* Bugs fixed from http://bugs.kde.org:
+* Bugs/wishes from http://bugs.kde.org:
+* C# Code Generation and export (53368)
 * %date% and %time% not being parsed (96612)
 * Relationships for entities do not live outside of the diagram (125146)
 * Javascript wrong Code Generation (135527)
--- branches/KDE/3.5/kdesdk/umbrello/THANKS #635307:635308
@@ -77,6 +77,7 @@
 Andrew Sutton <asutton @cs.kent.edu>
 Tanuj <tagrawal @hss.hns.com>
 Brian Thomas <brian.thomas @gsfc.nasa.gov>
+Ferenc Veres <lion @netngine.hu>
 Jean Vittor <jean.vittor @wanadoo.fr>
 Egbert Voigt <Egbert.Voigt @alcatel.de>
 Stefan Walter <sw @gegenunendlich.de>
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/Makefile.am #635307:635308
@@ -27,7 +27,7 @@
 	rubycodegenerationpolicy.cpp rubycodegenerationpolicypage.cpp \
 	rubycodegenerationformbase.ui \
 	simplecodegenerator.cpp \
-	adawriter.cpp aswriter.cpp cppwriter.cpp javawriter.cpp jswriter.cpp \
+	adawriter.cpp aswriter.cpp cppwriter.cpp csharpwriter.cpp javawriter.cpp jswriter.cpp \
 	idlwriter.cpp pascalwriter.cpp perlwriter.cpp php5writer.cpp phpwriter.cpp \
 	pythonwriter.cpp rubywriter.cpp sqlwriter.cpp tclwriter.cpp xmlschemawriter.cpp \
 	xmlelementcodeblock.cpp xmlcodecomment.cpp \
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/codegenfactory.cpp #635307:635308
@@ -34,7 +34,7 @@
 
 #include "adawriter.h"
 #include "cppwriter.h"
-// #include "cswriter.h" // missing in action?!?
+#include "csharpwriter.h"
 #include "idlwriter.h"
 #include "javawriter.h"
 #include "pascalwriter.h"
@@ -102,9 +102,9 @@
             else
                 obj = new CppWriter();
             break;
-        // case Uml::pl_Csharp:
-        //     obj = new CsWriter();
-        //     break;
+        case Uml::pl_CSharp:
+            obj = new CSharpWriter();
+            break;
         case Uml::pl_IDL:
             obj = new IDLWriter();
             break;
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/model_utils.cpp #635307:635308
@@ -570,8 +570,8 @@
             return "Ada";
         case Uml::pl_Cpp:
             return "C++";
-        // case Uml::pl_Csharp:
-            // return "C#";
+        case Uml::pl_CSharp:
+            return "C#";
         case Uml::pl_IDL:
             return "IDL";
         case Uml::pl_Java:
@@ -609,8 +609,8 @@
         return Uml::pl_Ada;
     if (str == "C++" || str == "Cpp")  // "Cpp" only for bkwd compatibility
         return Uml::pl_Cpp;
-    // if (str == "C#")
-        // return Uml::pl_Csharp;
+    if (str == "C#")
+        return Uml::pl_CSharp;
     if (str == "IDL")
         return Uml::pl_IDL;
     if (str == "Java")
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/uml.cpp #635307:635308
@@ -457,6 +457,7 @@
 
     m_listDock = createDockWidget( "Model", 0L, 0L, i18n("&Tree View") );
     m_listView = new UMLListView(m_listDock ,"LISTVIEW");
+    //m_listView->setSorting(-1);
     m_listView->setDocument(m_doc);
     m_listView->init();
     m_listDock->setWidget(m_listView);
@@ -1313,6 +1314,7 @@
 QString UMLApp::activeLanguageScopeSeparator() {
     Uml::Programming_Language pl = getActiveLanguage();
     if (pl == Uml::pl_Ada ||
+        pl == Uml::pl_CSharp ||
         pl == Uml::pl_Pascal ||
         pl == Uml::pl_Java ||
         pl == Uml::pl_JavaScript ||
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/umlnamespace.h #635307:635308
@@ -308,7 +308,7 @@
     pl_ActionScript,
     pl_Ada,
     pl_Cpp,
-    // pl_Csharp,
+    pl_CSharp,
     pl_IDL,
     pl_Java,
     pl_JavaScript,
Comment 7 Ferenc Veres 2007-02-20 02:13:38 UTC
Created attachment 19752 [details]
Types, namespaces and formatting fixes

-ignore own namespace in 'using'
-less indent if no namespace
-using #region for sections
-no newlines in parameter documentation
-available namespaces are cut from return, attribute and parameter types
-property's m_ private missed datatype

Some FIXME's are still in, I think some of them depends on the base system.
Contact me in case of problems. :)
Comment 8 Oliver Kellogg 2007-02-20 19:48:19 UTC
SVN commit 635717 by okellogg:

Apply attachment 19752 [details] from Ferenc Veres with modification for aggregations
 and compositions.
Ferenc, could you look at lines 281ff. and 307ff.? I don't know C# so I'm
 not sure about the syntax.
CCBUG:53368
CCMAIL:lion@netngine.hu


 M  +126 -82   csharpwriter.cpp  
 M  +18 -0     csharpwriter.h  
Comment 9 Ferenc Veres 2007-02-21 03:30:08 UTC
Created attachment 19770 [details]
Compositions, Aggregations are generated

- compositions and aggregations are generated correctly (not with multiplicity
yet)
- unspecified role names create UnnamedRoleB_1(2,3,4)

I am not sure about & and * usage in parameters of my new functions. I did not
work in C++ in the past 7-8 years. :-( Please check them, I'll find a book. :-)


Most important bug left: interfaces cause warning of multiple inheritance
instead listing as interfaces:
class myclass : baseclass, interace1, interface2

Thanks for the cooperation ! :-)
Comment 10 Oliver Kellogg 2007-02-21 19:48:17 UTC
SVN commit 636046 by okellogg:

Apply attachment 19770 [details] from Ferenc Veres - without visibilityToText(), we can
 use Uml::Visibility::toString().
CCBUG:53368


 M  +84 -122   csharpwriter.cpp  
 M  +36 -8     csharpwriter.h  


--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/csharpwriter.cpp #636045:636046
@@ -223,6 +223,7 @@
     UMLAssociationList realizations = c->getRealizations();
     UMLAssociation *a;
     bool isInterface = c->isInterface();
+    m_unnamedRoles = 1;
 
     cs << m_container_indent << "public ";
 
@@ -261,56 +262,15 @@
 
     //associations
     if (forceSections() || !aggregations.isEmpty()) {
-        cs << m_endl << m_container_indent << m_indentation << "#region Aggregations" << m_endl;
-        for (a = aggregations.first(); a; a = aggregations.next()) {
-            if (c != a->getObject(Uml::A))  // we need to be at the A side
-                continue;
-            cs << m_endl;
-            //maybe we should parse the string here and take multiplicity into account to decide
-            //which container to use.
-            UMLObject *o = a->getObject(Uml::B);
-            if (o == NULL) {
-                kError() << "aggregation role B object is NULL" << endl;
-                continue;
-            }
-            QString roleName = cleanName(a->getRoleName(Uml::B));
-            if (roleName.isEmpty())
-                continue;   // CHECK: what should be done if no role name is set?
-            QString typeName = cleanName(o->getName());
-            if (a->getMulti(Uml::B).isEmpty())  {
-                cs << m_container_indent << m_indentation << "//" << typeName
-                   << " " << roleName << ';' << m_endl;
-            } else {
-                cs << m_container_indent << m_indentation << "//" << typeName
-                   << " " << roleName << "Vector = array();" << m_endl;
-            }
-        }//end for
+        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;
-        for (a = compositions.first(); a ; a = compositions.next()) {
-            if (c != a->getObject(Uml::A))  // we need to be at the A side
-                continue;
-            // see comment on Aggregation about multiplicity...
-            UMLObject *o = a->getObject(Uml::B);
-            if (o == NULL) {
-                kError() << "composition role B object is NULL" << endl;
-                continue;
-            }
-            QString roleName = cleanName(a->getRoleName(Uml::B));
-            if (roleName.isEmpty())
-                continue;   // CHECK: what should be done if no role name is set?
-            QString typeName = cleanName(o->getName());
-            if (a->getMulti(Uml::B).isEmpty())  {
-                cs << m_container_indent << m_indentation << "//" << typeName
-                   << " " << roleName << ';' << m_endl;
-            } else {
-                cs << m_container_indent << m_indentation << "//" << typeName
-                   << " " << roleName << "Vector = array();" << m_endl;
-            }
-        }
+        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;
     }
 
@@ -482,19 +442,7 @@
         // method visibility
         cs << m_container_indent << m_indentation;
         if (op->getAbstract()) cs << "abstract ";
-        switch (op->getVisibility()) {
-          case Uml::Visibility::Public:
-            cs << "public ";
-            break;
-          case Uml::Visibility::Protected:
-            cs << "protected ";
-            break;
-          case Uml::Visibility::Private:
-            cs << "private ";
-            break;
-          default:
-            break;
-        }
+        cs << op->getVisibility().toString() << " ";
         if (op->getStatic()) cs << "static ";
 
         // return type
@@ -594,83 +542,97 @@
 
 
 void CSharpWriter::writeAttributes(UMLAttributeList &atList, QTextStream &cs) {
+
     for (UMLAttribute *at = atList.first(); at ; at = atList.next()) {
-        bool isStatic = at->getStatic();
-        if (forceDoc() || !at->getDoc().isEmpty()) {
 
-            cs << m_container_indent << m_indentation << "/// <summary>" << m_endl;
-            cs << formatDoc(at->getDoc(), m_container_indent + m_indentation + "/// ");
-            cs << m_container_indent << m_indentation << "/// </summary>" << m_endl;
+        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);
 
-/*            if (isStatic) cs << m_indentation << " * @static" << m_endl;
-            switch (at->getVisibility()) {
-              case Uml::Visibility::Public:
-                cs << m_indentation << " * @access public" << m_endl;
-                break;
-              case Uml::Visibility::Protected:
-                cs << m_indentation << " * @access protected" << m_endl;
-                break;
-              case Uml::Visibility::Private:
-                cs << m_indentation << " * @access private" << m_endl;
-                break;
-              default:
-                break;
-            }
- */
- //          cs << m_indentation << " */" << m_endl;
+        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;
         }
-        cs << m_container_indent << m_indentation;
-        switch (at->getVisibility()) {
-          case Uml::Visibility::Public:
-            cs << "public ";
-            break;
-          case Uml::Visibility::Protected:
-            cs << "protected ";
-            break;
-          case Uml::Visibility::Private:
-            cs << "private ";
-            break;
-          default:
-            break;
+        // 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++);
         }
-        if (isStatic) cs << "static ";
+        QString roleDoc = a->getRoleDoc(Uml::B);
 
-        //Variable type with/without namespace path
-        cs << makeLocalTypeName(at) << " ";
+        //FIXME:maybe we should parse the string here and take multiplicity into account to decide
+        //which container to use.
+        if (a->getMulti(Uml::B).isEmpty())  {
+            writeAttribute(roleDoc, a->getVisibility(Uml::B), false, typeName, roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
+        } else {
+            // FIXME:not updated for C# yet
+            cs << m_container_indent << m_indentation << "//" << typeName
+                << " " << roleName << "Vector = array();" << m_endl;
+        }
+    }
+}
 
-        cs << cleanName(at->getName());
+void CSharpWriter::writeAttribute(QString doc, Uml::Visibility visibility, bool isStatic, QString typeName, QString name, QString initialValue, bool asProperty, QTextStream &cs) {
 
-        // FIXME: may need a GUI switch to not generate as Property?
+    if (forceDoc() || !doc.isEmpty()) {
 
-        // Generate as Property if not private
-        if (at->getVisibility() != Uml::Visibility::Private)
-        {
-            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(at->getName()) << ";" << m_endl;
-            cs << m_container_indent << m_indentation << m_indentation << "}" << m_endl;
+        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 << 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(at->getName()) << " = 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 " << makeLocalTypeName(at) << " m_" << cleanName(at->getName()) << ";" << m_endl;
-        }
-        else
-        {
-            cs << ";" << m_endl;
-        }
+    }
+    cs << m_container_indent << m_indentation;
+    cs << visibility.toString() << " ";
+    if (isStatic) cs << "static ";
 
-//        if (!at->getInitialValue().isEmpty())
-//            cs << " = " << at->getInitialValue();
+    //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;
-    } // end for
-    return;
+        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) {
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/csharpwriter.h #636045:636046
@@ -21,7 +21,9 @@
 #include "../umlattributelist.h"
 #include "../umloperationlist.h"
 #include "../classifierlistitem.h"
+#include "../umlassociationlist.h"
 
+
 /**
   * class CSharpWriter is a C# code generator for UMLClassifier objects
   * Just call writeClass and feed it a UMLClassifier;
@@ -70,39 +72,65 @@
     * Collection of included namespaces, to skip them from variable types.
     */
     UMLPackageList m_seenIncludes;
+
+    /**
+    * Counts associations without a role name for giving a default name.
+    */
+    int m_unnamedRoles;
     
     /**
       * write all operations for a given class
       *
       * @param c the concept we are generating code for
-      * @param php output stream for the PHP file
+      * @param cs output stream
       */
-    void writeOperations(UMLClassifier *c, QTextStream &php);
+    void writeOperations(UMLClassifier *c, QTextStream &cs);
 
     /**
       * write a list of class operations
       *
       * @param classname the name of the class
       * @param opList the list of operations
-      * @param php output stream for the PHP file
+      * @param cs output stream
       * @param interface indicates if the operation is an interface member
       */
     void writeOperations(const QString &classname, UMLOperationList &opList,
-                         QTextStream &php,
+                         QTextStream &cs,
                          bool interface = false, bool generateErrorStub = false);
 
     /** write all the attributes of a class
       * @param c the class we are generating code for
-      * @param php output stream for the PHP file
+      * @param cs output stream
       */
-    void writeAttributes(UMLClassifier *c, QTextStream &php);
+    void writeAttributes(UMLClassifier *c, QTextStream &cs);
 
     /** write a list of class attributes
       * @param atList the list of attributes
-      * @param php output stream for the PHP file
+      * @param cs output stream
       */
-    void writeAttributes(UMLAttributeList &atList, QTextStream &php);
+    void writeAttributes(UMLAttributeList &atList, QTextStream &cs);
 
+    /**
+      * write attributes from associated objects (compositions, aggregations)
+      * @param associated list of associated objects
+      * @param c currently written class, to see association direction
+      * @param cs output stream
+      */ 
+    void writeAssociatedAttributes(UMLAssociationList &associated, UMLClassifier *c, QTextStream &cs);
+
+    /**
+      * write a single attribute to the output stream
+      * @param doc attribute documentation
+      * @param visibility attribute visibility
+      * @param isStatic static attribute
+      * @param typeName class/type of the attribute
+      * @param name name of the attribute
+      * @param initialValue initial value given to the attribute at declaration
+      * @param asProperty true writes as property (get/set), false writes single line variable
+      * @param cs output stream 
+      */
+    void writeAttribute(QString doc, Uml::Visibility visibility, bool isStatic, QString typeName, QString name, QString initialValue, bool asProperty, QTextStream &cs);
+
     /** find the type in used namespaces, if namespace found return short name, complete otherwise.
       *
       * @param at Operation or Attribute to check type
Comment 11 Ferenc Veres 2007-02-22 03:09:24 UTC
Created attachment 19776 [details]
Abstract overrides, interfaces are generated, arrays ok.

There could be a lot more to do, but now my 50+ classes (not fully worked out)
project compiles when loading to VS2005 from Umbrello. Only ENUMS are missing,
I make them manually there.

Patch:

- Implemented Interfaces' methods are generated as skeleton, including
recursions
- Inherited abstract methods are generated as skels, recursive. (This may of
course create unuwanted results, e.g. unwanted implementation locations in the
class hierarchy.)
- Including some base 'using System*' commands. The whole thing is so much
hardcoded already anyway. :-) (This follows VS defaults.)
- Not empty and not "1" multiplications are generated as ArrayList (at least
compiles now, some ppl may want to customize to Array(), or [] or whatever.
- Fixes in some defitions (baseclass+interfaces,etc).

Most importnant: if you don't make enums and illogical or invalid things, the
generated code compiles out of the box! Very basic generator, but at least
there is something. I can create a list of open issues if somebody interested.
(I don't like this hardcoded approach, but it was just a "quick hack" from
another generator. I hope it will be useful for some people.)

Maintainers, please check my * and &, especially on the 2 recursive functions.
:-( That may be important, I'd really hate to scratch out elements from the
model while generating! ;-)

Best regards,
Ferenc
Comment 12 Oliver Kellogg 2007-02-22 19:15:01 UTC
SVN commit 636283 by okellogg:

Apply attachment 19776 [details] from Ferenc Veres:
> Implemented Interfaces' methods are generated as skeleton, including recursions
> Inherited abstract methods are generated as skels, recursive.
> Including some base 'using System*' commands.
> Not empty and not "1" multiplications are generated as ArrayList
> Fixes in some defitions (baseclass+interfaces,etc).
> [...] Only ENUMS are missing.


Many thanks Ferenc for contributing.

> Maintainers, please check my * and &, [...]


Look okay to me, although you could pass around lists by value (i.e. you don't
really need the pointers.)

FEATURE:53368


 M  +124 -70   csharpwriter.cpp  
 M  +25 -4     csharpwriter.h  


--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/csharpwriter.cpp #636282:636283
@@ -177,6 +177,12 @@
     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();
@@ -221,7 +227,6 @@
     UMLAssociationList aggregations = c->getAggregations();
     UMLAssociationList compositions = c->getCompositions();
     UMLAssociationList realizations = c->getRealizations();
-    UMLAssociation *a;
     bool isInterface = c->isInterface();
     m_unnamedRoles = 1;
 
@@ -232,29 +237,40 @@
         cs << "interface " << classname;
     } else {
         //check if class is abstract and / or has abstract methods
-        if (c->getAbstract())
+        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) {
-
-            // C# does not support multiple inheritance so only use the first one and print a warning if more are used
-
-            UMLClassifier *obj = superclasses.first();
-            cs << cleanName(obj->getName());
-            if (superclasses.count() > 1)
-                cs << m_indentation << "//WARNING: C# does not support multiple inheritance but there is more than 1 superclass defined in your UML model!";
+            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;
         }
-        // FIXME: I get no (valid) input here (realizations are not coming?)
+        }
         //check for realizations
+        UMLAssociationList realizations = c->getRealizations();
+        UMLAssociation *a;
+
         if (!realizations.isEmpty()) {
-            int rc = realizations.count();
-            int ri = rc;
             for (a = realizations.first(); a; a = realizations.next()) {
-                UMLObject *o = a->getObject(Uml::B);
-                QString typeName = cleanName(o->getName());
-                if (ri == rc)
-                    cs <<  ", ";
-                cs << typeName << (--rc == 0 ? "" : ", ");
+                UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
+                if(real != c) {
+                    // write list of realizations
+                    cs << ", " << real->getName();
+                }
+
             }
         }
     }
@@ -304,7 +320,7 @@
     UMLOperationList oppub,opprot,oppriv;
 
     bool isInterface = c->isInterface();
-    bool generateErrorStub = false;
+    bool generateErrorStub = true;
 
     oppub.setAutoDelete(false);
     opprot.setAutoDelete(false);
@@ -328,58 +344,106 @@
         }
     }
 
-    QString classname(cleanName(c->getName()));
+    // 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(classname,oppub,cs,isInterface,generateErrorStub);
+        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(classname,opprot,cs,isInterface,generateErrorStub);
+        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(classname,oppriv,cs,isInterface,generateErrorStub);
+        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();
 
-    // build an oplist for all of the realized operations
-    UMLOperationList opreal;
-    opreal.setAutoDelete(false);
+    if (!isInterface && !c->getAbstract() && !c->hasAbstractOps()
+            && superclasses.count() > 0) {
+        writeOverridesRecursive(&superclasses, cs);
+    }
 
-    // go through each of the realizations, taking each op
-    UMLAssociationList realizations = c->getRealizations();
-    UMLAssociation *a;
+}
 
-    if (!realizations.isEmpty()) {
-        for (a = realizations.first(); a; a = realizations.next()) {
+void CSharpWriter::writeOverridesRecursive(UMLClassifierList *superclasses, QTextStream &cs) {
+    // oplist for implemented abstract operations
+    UMLOperationList opabstract;
+    opabstract.setAutoDelete(false);
+    UMLClassifier *obj;
 
-            // we know its a classifier if its in the list
-            UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
-
-            UMLOperationList opl(real->getOpList());
+    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()) {
-                opreal.append(op);
+                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) {
 
-    // write out all the realizations operations
-    writeOperations(classname,opreal,cs,false,true);
+    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(const QString &/* classname */, UMLOperationList &opList,
+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()) {
@@ -394,7 +458,7 @@
         }
 
         //write method documentation
-        if (writeDoc)
+        if (writeDoc && !isOverride)
         {
             cs << m_container_indent << m_indentation << "/// <summary>" << m_endl;
             cs << formatDoc(op->getDoc(), m_container_indent + m_indentation + "/// ");
@@ -418,32 +482,22 @@
             }
             cs << "</returns>" << m_endl;
 
- /* not used in C#
-            if (op->getAbstract()) cs << m_indentation << " * @abstract" << m_endl;
-            if (op->getStatic()) cs << m_indentation << " * @static" << m_endl;
-
-            switch (op->getVisibility()) {
-              case Uml::Visibility::Public:
-                cs << m_indentation << " * @access public" << m_endl;
-                break;
-              case Uml::Visibility::Protected:
-                cs << m_indentation << " * @access protected" << m_endl;
-                break;
-              case Uml::Visibility::Private:
-                cs << m_indentation << " * @access private" << m_endl;
-                break;
-              default:
-                break;
-            }
-*/
-//            cs <<m_indentation << " */" << m_endl;
         }
 
         // method visibility
         cs << m_container_indent << m_indentation;
-        if (op->getAbstract()) cs << "abstract ";
-        cs << op->getVisibility().toString() << " ";
-        if (op->getStatic()) cs << "static ";
+        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
         if (op->getTypeName() == "") {
@@ -472,7 +526,7 @@
         cs << ")";
 
         //FIXME: how to control generation of error stub?
-        if (!isInterface && !op->getAbstract()) {
+        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;
@@ -563,7 +617,7 @@
     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;
@@ -577,14 +631,14 @@
         }
         QString roleDoc = a->getRoleDoc(Uml::B);
 
-        //FIXME:maybe we should parse the string here and take multiplicity into account to decide
-        //which container to use.
-        if (a->getMulti(Uml::B).isEmpty())  {
+        //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 {
-            // FIXME:not updated for C# yet
-            cs << m_container_indent << m_indentation << "//" << typeName
-                << " " << roleName << "Vector = array();" << m_endl;
+            // array
+            roleDoc += "\n(Array of " + typeName + ")";
+            writeAttribute(roleDoc, a->getVisibility(Uml::B), false, "ArrayList", roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
         }
     }
 }
@@ -631,7 +685,7 @@
 
     if (!initialValue.isEmpty())
         cs << " = " << initialValue;
-        
+
     cs << ";" << m_endl << m_endl;
 }
 
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/csharpwriter.h #636282:636283
@@ -77,8 +77,19 @@
     * Counts associations without a role name for giving a default name.
     */
     int m_unnamedRoles;
-    
+
     /**
+      * write realizations of a class and recurse to parent classes
+      
+      * @param currentClass class to start with
+      * @param realizations realizations of this class
+      * @param cs output stream
+      */
+    void writeRealizationsRecursive(UMLClassifier *currentClass,
+                                    UMLAssociationList *realizations,
+                                    QTextStream &cs);
+
+    /**
       * write all operations for a given class
       *
       * @param c the concept we are generating code for
@@ -89,15 +100,25 @@
     /**
       * write a list of class operations
       *
-      * @param classname the name of the class
       * @param opList the list of operations
       * @param cs output stream
       * @param interface indicates if the operation is an interface member
+      * @param isOverride implementation of an inherited abstract function
       */
-    void writeOperations(const QString &classname, UMLOperationList &opList,
+    void writeOperations(UMLOperationList opList,
                          QTextStream &cs,
-                         bool interface = false, bool generateErrorStub = false);
+                         bool interface = false,
+                         bool isOverride = false,
+                         bool generateErrorStub = false);
 
+    /**
+      * write superclasses' abstract methods
+      *
+      * @param superclasses List of superclasses to start recursing on
+      * @param cs output stream
+      */
+    void writeOverridesRecursive(UMLClassifierList *superclasses, QTextStream &cs);
+    
     /** write all the attributes of a class
       * @param c the class we are generating code for
       * @param cs output stream
Comment 13 Oliver Kellogg 2022-02-19 21:28:55 UTC
Git commit a5893a5030a983dfb9efec1bf1dbf34e5b191b8b by Oliver Kellogg.
Committed on 19/02/2022 at 16:35.
Pushed by okellogg into branch 'master'.

umbrello/codegenerators/csharp/csharpwriter.cpp improvements in function defaultDatatypes()

- Remove duplicated entries "fixed", "float".
- Add entries "nint", "nuint".
- For symmetry with Valawriter add single dimension array types for the
  predefined scalar types.

M  +22   -2    umbrello/codegenerators/csharp/csharpwriter.cpp

https://invent.kde.org/sdk/umbrello/commit/a5893a5030a983dfb9efec1bf1dbf34e5b191b8b