00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "csharpwriter.h"
00016
00017 #include <kdebug.h>
00018 #include <qregexp.h>
00019 #include <qtextstream.h>
00020
00021 #include "../uml.h"
00022 #include "../umldoc.h"
00023 #include "../folder.h"
00024 #include "../classifier.h"
00025 #include "../association.h"
00026 #include "../attribute.h"
00027 #include "../operation.h"
00028 #include "../umlnamespace.h"
00029
00030 static const char *reserved_words[] = {
00031 "abstract",
00032 "as",
00033 "base",
00034 "bool",
00035 "break",
00036 "byte",
00037 "case",
00038 "catch",
00039 "char",
00040 "checked",
00041 "class",
00042 "const",
00043 "continue",
00044 "decimal",
00045 "default",
00046 "delegate",
00047 "do",
00048 "double",
00049 "else",
00050 "enum",
00051 "event",
00052 "explicit",
00053 "extern",
00054 "false",
00055 "finally",
00056 "for",
00057 "foreach",
00058 "goto",
00059 "if",
00060 "implicit",
00061 "in",
00062 "int",
00063 "interface",
00064 "internal",
00065 "is",
00066 "lock",
00067 "long",
00068 "namespace",
00069 "new",
00070 "null",
00071 "object",
00072 "operator",
00073 "out",
00074 "override",
00075 "params",
00076 "private",
00077 "protected",
00078 "public",
00079 "readonly",
00080 "ref",
00081 "return",
00082 "sbyte",
00083 "sealed",
00084 "short",
00085 "sizeof",
00086 "stackalloc",
00087 "static",
00088 "string",
00089 "struct",
00090 "switch",
00091 "this",
00092 "throw",
00093 "true",
00094 "try",
00095 "typeof",
00096 "uint",
00097 "ulong",
00098 "unchecked",
00099 "unsafe",
00100 "ushort",
00101 "using",
00102 "virtual",
00103 "void",
00104 "volatile",
00105 "while",
00106 0
00107 };
00108
00109 CSharpWriter::CSharpWriter()
00110 : SimpleCodeGenerator()
00111 {
00112 }
00113
00114
00115 CSharpWriter::~CSharpWriter()
00116 {
00117 }
00118
00119 QStringList CSharpWriter::defaultDatatypes() {
00120 QStringList l;
00121 l.append("bool");
00122 l.append("byte");
00123 l.append("char");
00124 l.append("decimal");
00125 l.append("double");
00126 l.append("fixed");
00127 l.append("float");
00128 l.append("fixed");
00129 l.append("float");
00130 l.append("int");
00131 l.append("long");
00132 l.append("object");
00133 l.append("sbyte");
00134 l.append("short");
00135 l.append("string");
00136 l.append("uint");
00137 l.append("ulong");
00138 l.append("ushort");
00139 return l;
00140 }
00141
00142 void CSharpWriter::writeClass(UMLClassifier *c) {
00143 if (!c) {
00144 kDebug()<<"Cannot write class of NULL concept!" << endl;
00145 return;
00146 }
00147
00148 QString classname = cleanName(c->getName());
00149
00150 QString fileName = findFileName(c, ".cs");
00151 if (fileName.isEmpty()) {
00152 emit codeGenerated(c, false);
00153 return;
00154 }
00155
00156 QFile filecs;
00157 if (!openFile(filecs, fileName)) {
00158 emit codeGenerated(c, false);
00159 return;
00160 }
00161 QTextStream cs(&filecs);
00162
00164
00166
00167
00168
00169 QString str;
00170 str = getHeadingFile(".cs");
00171 if (!str.isEmpty()) {
00172 str.replace(QRegExp("%filename%"),fileName);
00173 str.replace(QRegExp("%filepath%"),filecs.name());
00174 cs<<str<<m_endl;
00175 }
00176
00177 UMLDoc *umldoc = UMLApp::app()->getDocument();
00178 UMLFolder *logicalView = umldoc->getRootFolder(Uml::mt_Logical);
00179
00180
00181 cs << "using System;" << m_endl;
00182 cs << "using System.Text;" << m_endl;
00183 cs << "using System.Collections;" << m_endl;
00184 cs << "using System.Collections.Generic;" << m_endl << m_endl;
00185
00186
00187
00188 UMLPackage *container = c->getUMLPackage();
00189 if (container == logicalView)
00190 container = NULL;
00191
00192 UMLPackageList includes;
00193 findObjectsRelated(c, includes);
00194 m_seenIncludes.clear();
00195
00196 if (includes.count()) {
00197 UMLPackage *p;
00198 for (UMLPackageListIt it(includes); (p = it.current()) != NULL; ++it) {
00199 UMLClassifier *cl = dynamic_cast<UMLClassifier*>(p);
00200 if (cl)
00201 p = cl->getUMLPackage();
00202 if (p != logicalView && m_seenIncludes.findRef(p) == -1 && p != container) {
00203 cs << "using " << p->getFullyQualifiedName(".") << ";" << m_endl;
00204 m_seenIncludes.append(p);
00205 }
00206 }
00207 cs << m_endl;
00208 }
00209
00210 m_container_indent = "";
00211
00212 if (container) {
00213 cs << "namespace " << container->getFullyQualifiedName(".") << m_endl;
00214 cs << "{" << m_endl << m_endl;
00215 m_container_indent = m_indentation;
00216 m_seenIncludes.append(container);
00217 }
00218
00219
00220 if (forceDoc() || !c->getDoc().isEmpty()) {
00221 cs << m_container_indent << "
00222 cs << formatDoc(c->getDoc(), m_container_indent + "
00223 cs << m_container_indent << "
00224 }
00225
00226 UMLClassifierList superclasses = c->getSuperClasses();
00227 UMLAssociationList aggregations = c->getAggregations();
00228 UMLAssociationList compositions = c->getCompositions();
00229 UMLAssociationList realizations = c->getRealizations();
00230 bool isInterface = c->isInterface();
00231 m_unnamedRoles = 1;
00232
00233 cs << m_container_indent << "public ";
00234
00235 //check if it is an interface or regular class
00236 if (isInterface) {
00237 cs << "interface " << classname;
00238 } else {
00239 //check if class is abstract and / or has abstract methods
00240 if (c->getAbstract() || c->hasAbstractOps())
00241 cs << "abstract ";
00242
00243 cs << "class " << classname << (superclasses.count() > 0 ? " : ":"");
00244
00245
00246 if (superclasses.count() > 0) {
00247 UMLClassifier *obj;
00248 int supers = 0;
00249 for (obj = superclasses.first(); obj; obj = superclasses.next()) {
00250 if (!obj->isInterface()) {
00251 if (supers > 0) {
00252 cs << " // AND ";
00253 }
00254 cs << cleanName(obj->getName());
00255 supers++;
00256 }
00257 }
00258 if (supers > 1) {
00259 cs << m_endl << "//WARNING: C# does not support multiple inheritance but there is more than 1 superclass defined in your UML model!" << m_endl;
00260 }
00261 }
00262
00263 UMLAssociationList realizations = c->getRealizations();
00264 UMLAssociation *a;
00265
00266 if (!realizations.isEmpty()) {
00267 for (a = realizations.first(); a; a = realizations.next()) {
00268 UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
00269 if(real != c) {
00270
00271 cs << ", " << real->getName();
00272 }
00273
00274 }
00275 }
00276 }
00277 cs << m_endl << m_container_indent << '{' << m_endl;
00278
00279
00280 if (forceSections() || !aggregations.isEmpty()) {
00281 cs << m_endl << m_container_indent << m_indentation << "#region Aggregations" << m_endl << m_endl;
00282 writeAssociatedAttributes(aggregations, c, cs);
00283 cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl;
00284 }
00285
00286
00287 if (forceSections() || !compositions.isEmpty()) {
00288 cs << m_endl << m_container_indent << m_indentation << "#region Compositions" << m_endl << m_endl;
00289 writeAssociatedAttributes(compositions, c, cs);
00290 cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl;
00291 }
00292
00293
00294
00295 if (!isInterface)
00296 writeAttributes(c, cs);
00297
00298
00299 writeOperations(c, cs);
00300
00301
00302 cs << m_endl << m_container_indent << "}" << m_endl << m_endl;
00303
00304 if (container) {
00305 cs << "} // end of namespace "
00306 << container->getFullyQualifiedName(".") << m_endl << m_endl;
00307 }
00308
00309
00310 filecs.close();
00311 emit codeGenerated(c, true);
00312 }
00313
00315
00316
00317 void CSharpWriter::writeOperations(UMLClassifier *c, QTextStream &cs) {
00318
00319
00320 UMLOperationList oppub,opprot,oppriv;
00321
00322 bool isInterface = c->isInterface();
00323 bool generateErrorStub = true;
00324
00325 oppub.setAutoDelete(false);
00326 opprot.setAutoDelete(false);
00327 oppriv.setAutoDelete(false);
00328
00329
00330 UMLOperationList opl(c->getOpList());
00331 for (UMLOperation *op = opl.first(); op ; op = opl.next()) {
00332 switch (op->getVisibility()) {
00333 case Uml::Visibility::Public:
00334 oppub.append(op);
00335 break;
00336 case Uml::Visibility::Protected:
00337 opprot.append(op);
00338 break;
00339 case Uml::Visibility::Private:
00340 oppriv.append(op);
00341 break;
00342 default:
00343 break;
00344 }
00345 }
00346
00347
00348 UMLAssociationList realizations = c->getRealizations();
00349
00350 if (!isInterface && !realizations.isEmpty()) {
00351 writeRealizationsRecursive(c, &realizations, cs);
00352 }
00353
00354
00355 if (forceSections() || !oppub.isEmpty()) {
00356 cs << m_endl << m_container_indent << m_indentation << "#region Public methods" << m_endl << m_endl;
00357 writeOperations(oppub,cs,isInterface,false,generateErrorStub);
00358 cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00359 }
00360
00361
00362 if (forceSections() || !opprot.isEmpty()) {
00363 cs << m_endl << m_container_indent << m_indentation << "#region Protected methods" << m_endl << m_endl;
00364 writeOperations(opprot,cs,isInterface,false,generateErrorStub);
00365 cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00366 }
00367
00368
00369 if (forceSections() || !oppriv.isEmpty()) {
00370 cs << m_endl << m_container_indent << m_indentation << "#region Private methods" << m_endl << m_endl;
00371 writeOperations(oppriv,cs,isInterface,false,generateErrorStub);
00372 cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00373 }
00374
00375
00376 UMLClassifierList superclasses = c->getSuperClasses();
00377
00378 if (!isInterface && !c->getAbstract() && !c->hasAbstractOps()
00379 && superclasses.count() > 0) {
00380 writeOverridesRecursive(&superclasses, cs);
00381 }
00382
00383 }
00384
00385 void CSharpWriter::writeOverridesRecursive(UMLClassifierList *superclasses, QTextStream &cs) {
00386
00387 UMLOperationList opabstract;
00388 opabstract.setAutoDelete(false);
00389 UMLClassifier *obj;
00390
00391 for (obj = superclasses->first(); obj; obj = superclasses->next()) {
00392 if (!obj->isInterface() && obj->hasAbstractOps()) {
00393
00394 UMLOperationList opl(obj->getOpList());
00395 for (UMLOperation *op = opl.first(); op ; op = opl.next()) {
00396 if (op->getAbstract()) {
00397 opabstract.append(op);
00398 }
00399 }
00400
00401
00402 cs << m_endl << m_container_indent << m_indentation << "#region " << obj->getName() << " members" << m_endl << m_endl;
00403 writeOperations(opabstract,cs,false,true,true);
00404 cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00405
00406 opabstract.clear();
00407 }
00408
00409 UMLClassifierList superRecursive = obj->getSuperClasses();
00410 UMLClassifierList *superRecursivePtr =& superRecursive;
00411 if (superRecursivePtr->count() > 0) {
00412 writeOverridesRecursive(superRecursivePtr, cs);
00413 }
00414 }
00415 }
00416 void CSharpWriter::writeRealizationsRecursive(UMLClassifier *currentClass, UMLAssociationList *realizations, QTextStream &cs) {
00417
00418 UMLAssociation *a;
00419 for (a = realizations->first(); a; a = realizations->next()) {
00420
00421
00422 UMLClassifier *real = (UMLClassifier*)a->getObject(Uml::B);
00423
00424
00425 if (real == currentClass)
00426 continue;
00427
00428
00429 UMLOperationList opreal = real->getOpList();
00430
00431
00432 cs << m_endl << m_container_indent << m_indentation << "#region " << real->getName() << " members" << m_endl << m_endl;
00433 writeOperations(opreal,cs,false,false,true);
00434 cs << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00435
00436
00437 UMLAssociationList parentReal = real->getRealizations();
00438 if (!parentReal.isEmpty()) {
00439 writeRealizationsRecursive(real, &parentReal, cs);
00440 }
00441 }
00442 }
00443
00444 void CSharpWriter::writeOperations(UMLOperationList opList,
00445 QTextStream &cs, bool isInterface ,
00446 bool isOverride ,
00447 bool generateErrorStub ) {
00448
00449 for (UMLOperation *op=opList.first(); op ; op=opList.next()) {
00450 UMLAttributeList atl = op->getParmList();
00451 UMLAttribute *at;
00452
00453
00454 bool writeDoc = forceDoc() || !op->getDoc().isEmpty();
00455
00456 for (at = atl.first(); at; at = atl.next()) {
00457 writeDoc |= !at->getDoc().isEmpty();
00458 }
00459
00460
00461 if (writeDoc && !isOverride)
00462 {
00463 cs << m_container_indent << m_indentation << "
00464 cs << formatDoc(op->getDoc(), m_container_indent + m_indentation + "
00465 cs << m_container_indent << m_indentation << "
00466
00467 //write parameter documentation
00468 for (at = atl.first(); at; at = atl.next())
00469 {
00470 if (forceDoc() || !at->getDoc().isEmpty()) {
00471 cs << m_container_indent << m_indentation << "
00472
00473 cs << formatDoc(at->getDoc(), "").replace("\n", " ").remove('\r').replace(QRegExp(" $"), "");
00474 cs << "</param>" << m_endl;
00475 }
00476 }
00477
00478
00479 cs << m_container_indent << m_indentation << "
00480 if (op->getTypeName() != "") {
00481 cs << makeLocalTypeName(op);
00482 }
00483 cs << "</returns>" << m_endl;
00484
00485 }
00486
00487 // method visibility
00488 cs << m_container_indent << m_indentation;
00489 if (!isInterface) {
00490 if (!isOverride) {
00491 if (op->getAbstract()) cs << "abstract ";
00492 cs << op->getVisibility().toString() << " ";
00493 if (op->getStatic()) cs << "static ";
00494 }
00495 else {
00496 // method overriding an abstract parent
00497 cs << op->getVisibility().toString() << " override ";
00498 if (op->getStatic()) cs << "static ";
00499 }
00500 }
00501
00502 // return type
00503 if (op->getTypeName() == "") {
00504 cs << "void ";
00505 }
00506 else {
00507 cs << makeLocalTypeName(op) << " ";
00508 }
00509
00510 // method name
00511 cs << cleanName(op->getName()) << "(";
00512
00513 // method parameters
00514 int i= atl.count();
00515 int j=0;
00516 for (at = atl.first(); at; at = atl.next(), j++) {
00517
00518 cs << makeLocalTypeName(at) << " " << cleanName(at->getName());
00519
00520 // no initial values in C#
00521 //<< (!(at->getInitialValue().isEmpty()) ?
00522 // (QString(" = ")+at->getInitialValue()) :
00523 // QString(""))
00524 cs << ((j < i-1)?", ":"");
00525 }
00526 cs << ")";
00527
00528 //FIXME: how to control generation of error stub?
00529 if (!isInterface && (!op->getAbstract() || isOverride)) {
00530 cs << m_endl << m_container_indent << m_indentation << "{" << m_endl;
00531 if (generateErrorStub) {
00532 cs << m_container_indent << m_indentation << m_indentation;
00533 cs << "throw new Exception(\"The method or operation is not implemented.\");" << m_endl;
00534 }
00535 cs << m_container_indent << m_indentation << "}" << m_endl;
00536 }
00537 else {
00538 cs << ';' << m_endl;
00539 }
00540 cs << m_endl;
00541 }
00542 }
00543
00544 void CSharpWriter::writeAttributes(UMLClassifier *c, QTextStream &cs) {
00545
00546 UMLAttributeList atpub, atprot, atpriv, atdefval;
00547 atpub.setAutoDelete(false);
00548 atprot.setAutoDelete(false);
00549 atpriv.setAutoDelete(false);
00550 atdefval.setAutoDelete(false);
00551
00552
00553 UMLAttributeList atl = c->getAttributeList();
00554 UMLAttribute *at;
00555
00556 for (at = atl.first(); at ; at = atl.next()) {
00557 if (!at->getInitialValue().isEmpty())
00558 atdefval.append(at);
00559 switch (at->getVisibility()) {
00560 case Uml::Visibility::Public:
00561 atpub.append(at);
00562 break;
00563 case Uml::Visibility::Protected:
00564 atprot.append(at);
00565 break;
00566 case Uml::Visibility::Private:
00567 atpriv.append(at);
00568 break;
00569 default:
00570 break;
00571 }
00572 }
00573
00574 if (forceSections() || atl.count())
00575 cs << m_endl << m_container_indent << m_indentation << "#region Attributes" << m_endl << m_endl;
00576
00577
00578 if (forceSections() || atpub.count()) {
00579 writeAttributes(atpub,cs);
00580 }
00581
00582
00583 if (forceSections() || atprot.count()) {
00584 writeAttributes(atprot,cs);
00585 }
00586
00587
00588 if (forceSections() || atpriv.count()) {
00589 writeAttributes(atpriv,cs);
00590 }
00591
00592 if (forceSections() || atl.count())
00593 cs << m_endl << m_container_indent << m_indentation << "#endregion" << m_endl << m_endl;
00594
00595 }
00596
00597
00598 void CSharpWriter::writeAttributes(UMLAttributeList &atList, QTextStream &cs) {
00599
00600 for (UMLAttribute *at = atList.first(); at ; at = atList.next()) {
00601
00602 bool asProperty = true;
00603 if (at->getVisibility() == Uml::Visibility::Private) {
00604 asProperty = false;
00605 }
00606 writeAttribute(at->getDoc(), at->getVisibility(), at->getStatic(),
00607 makeLocalTypeName(at), at->getName(), at->getInitialValue(), asProperty, cs);
00608
00609 cs << m_endl;
00610 }
00611 return;
00612 }
00613
00614 void CSharpWriter::writeAssociatedAttributes(UMLAssociationList &associated, UMLClassifier *c, QTextStream &cs) {
00615
00616 UMLAssociation *a;
00617 for (a = associated.first(); a ; a = associated.next()) {
00618 if (c != a->getObject(Uml::A))
00619 continue;
00620
00621 UMLObject *o = a->getObject(Uml::B);
00622 if (o == NULL) {
00623 kError() << "composition role B object is NULL" << endl;
00624 continue;
00625 }
00626
00627 QString roleName = cleanName(a->getRoleName(Uml::B));
00628 QString typeName = cleanName(o->getName());
00629 if (roleName.isEmpty()) {
00630 roleName = QString("UnnamedRoleB_%1").arg(m_unnamedRoles++);
00631 }
00632 QString roleDoc = a->getRoleDoc(Uml::B);
00633
00634
00635 if (a->getMulti(Uml::B).isEmpty() || a->getMulti(Uml::B) == "1") {
00636
00637 writeAttribute(roleDoc, a->getVisibility(Uml::B), false, typeName, roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
00638 } else {
00639
00640 roleDoc += "\n(Array of " + typeName + ")";
00641 writeAttribute(roleDoc, a->getVisibility(Uml::B), false, "ArrayList", roleName, "", ( a->getVisibility(Uml::B) != Uml::Visibility::Private), cs);
00642 }
00643 }
00644 }
00645
00646 void CSharpWriter::writeAttribute(QString doc, Uml::Visibility visibility, bool isStatic, QString typeName, QString name, QString initialValue, bool asProperty, QTextStream &cs) {
00647
00648 if (forceDoc() || !doc.isEmpty()) {
00649
00650 cs << m_container_indent << m_indentation << "
00651 cs << formatDoc(doc, m_container_indent + m_indentation + "
00652 cs << m_container_indent << m_indentation << "
00653
00654 }
00655 cs << m_container_indent << m_indentation;
00656 cs << visibility.toString() << " ";
00657 if (isStatic) cs << "static ";
00658
00659 //Variable type with/without namespace path
00660 cs << typeName << " ";
00661
00662 cs << cleanName(name);
00663
00664 // FIXME: may need a GUI switch to not generate as Property?
00665
00666 // Generate as Property if not private
00667 if (asProperty)
00668 {
00669 cs << m_endl;
00670 cs << m_container_indent << m_indentation << "{" << m_endl;
00671 cs << m_container_indent << m_indentation << m_indentation << "get" << m_endl;
00672 cs << m_container_indent << m_indentation << m_indentation << "{" << m_endl;
00673 cs << m_container_indent << m_indentation << m_indentation << m_indentation << "return m_" << cleanName(name) << ";" << m_endl;
00674 cs << m_container_indent << m_indentation << m_indentation << "}" << m_endl;
00675
00676 cs << m_container_indent << m_indentation << m_indentation << "set" << m_endl;
00677 cs << m_container_indent << m_indentation << m_indentation << "{" << m_endl;
00678 cs << m_container_indent << m_indentation << m_indentation << m_indentation << "m_" << cleanName(name) << " = value;" << m_endl;
00679 cs << m_container_indent << m_indentation << m_indentation << "}" << m_endl;
00680 cs << m_container_indent << m_indentation << "}" << m_endl;
00681 cs << m_container_indent << m_indentation << "private ";
00682 if (isStatic) cs << "static ";
00683 cs << typeName << " m_" << cleanName(name);
00684 }
00685
00686 if (!initialValue.isEmpty())
00687 cs << " = " << initialValue;
00688
00689 cs << ";" << m_endl << m_endl;
00690 }
00691
00692 QString CSharpWriter::makeLocalTypeName(UMLClassifierListItem *cl) {
00693 UMLPackage *p = cl->getType()->getUMLPackage();
00694 if (m_seenIncludes.findRef(p) != -1) {
00695 return cl->getType()->getName();
00696 }
00697 else {
00698 return cl->getTypeName();
00699 }
00700
00701 }
00702
00706 Uml::Programming_Language CSharpWriter::getLanguage() {
00707 return Uml::pl_CSharp;
00708 }
00709
00710 const QStringList CSharpWriter::reservedKeywords() const {
00711
00712 static QStringList keywords;
00713
00714 if (keywords.isEmpty()) {
00715 for (int i = 0; reserved_words[i]; i++)
00716 keywords.append(reserved_words[i]);
00717 }
00718
00719 return keywords;
00720 }
00721
00722 #include "csharpwriter.moc"
00723