cppwriter.cpp 53.8 KB
Newer Older
1
2
3
4
/***************************************************************************
 *   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     *
5
 *   (at your option) any later version.                                   *
6
 *                                                                         *
7
8
 *   copyright (C) 2003       Brian Thomas                                 *
 *                            <brian.thomas@gsfc.nasa.gov>                 *
9
 *   copyright (C) 2004-2014  Umbrello UML Modeller Authors                *
10
 *                            <umbrello-devel@kde.org>                       *
11
12
13
14
 ***************************************************************************/

// own header
#include "cppwriter.h"
15

16
// app includes
17
18
#include "association.h"
#include "classifier.h"
19
#include "codegen_utils.h"
20
#include "datatype.h"
21
22
#include "debug_utils.h"
#include "model_utils.h"
Andi Fischer's avatar
Andi Fischer committed
23
24
25
26
27
28
29
30
#include "uml.h"
#include "umldoc.h"
#include "operation.h"
#include "template.h"
#include "umltemplatelist.h"
#include "umlclassifierlistitemlist.h"
#include "classifierlistitem.h"
#include "codegenerationpolicy.h"
31
#include "enumliteral.h"
32

33
// qt includes
34
35
36
#include <QFile>
#include <QRegExp>
#include <QTextStream>
37

38
39
40
// 3-14-2003: this code developed from the javawriter with parts of the
// original cppwriter by Luis De la Parra Blum

41
42
43
/**
 * Constructor, initialises a couple of variables.
 */
44
CppWriter::CppWriter()
45
{
Oliver Kellogg's avatar
Oliver Kellogg committed
46
    // Probably we could resolve this better through the use of templates,
Frederik Schwarzer's avatar
Frederik Schwarzer committed
47
    // but it is a quick n dirty fix for the timebeing.. until codegeneration
Oliver Kellogg's avatar
Oliver Kellogg committed
48
49
50
    // template system is in place.
    // You can insert code here. 3 general variables exist: "%VARNAME%"
    // allows you to specify where the vector variable should be in your code,
51
    // and "%ITEMCLASS%", if needed, where the class of the item is declared.
52
    VECTOR_METHOD_APPEND = QLatin1String("%VARNAME%.push_back(add_object);"); // for std::vector
53
    VECTOR_METHOD_REMOVE = QString(QLatin1String("int i, size = %VARNAME%.size();\nfor (i = 0; i < size; ++i) {\n\t%ITEMCLASS% item = %VARNAME%.at(i);\n\tif(item == remove_object) {\n\t\t%1<%ITEMCLASS%>::iterator it = %VARNAME%.begin() + i;\n\t\t%VARNAME%.erase(it);\n\t\treturn;\n\t}\n }")).arg(policyExt()->getVectorClassName()); // for std::vector
54
    VECTOR_METHOD_INIT.clear(); // nothing to be done
Oliver Kellogg's avatar
Oliver Kellogg committed
55
    /*
56
57
58
        VECTOR_METHOD_APPEND = QLatin1String("%VARNAME%.append(&add_object);"); // Qt lib implementation
        VECTOR_METHOD_REMOVE = QLatin1String("%VARNAME%.removeRef(&remove_object);"); // Qt lib implementation
        VECTOR_METHOD_INIT = QLatin1String("%VARNAME%.setAutoDelete(false);"); // Qt library
Oliver Kellogg's avatar
Oliver Kellogg committed
59
60
    */

61
    OBJECT_METHOD_INIT = QLatin1String("%VARNAME% = new %ITEMCLASS%();"); // Qt library
Oliver Kellogg's avatar
Oliver Kellogg committed
62
63
64

    // boolean config params
    INLINE_ASSOCIATION_METHODS = false;
65
66
}

67
68
69
/**
 * Destructor, empty.
 */
70
71
72
CppWriter::~CppWriter()
{
}
73

74
75
76
77
/**
 * Returns "C++".
 * @return   the programming language identifier
 */
Andi Fischer's avatar
Andi Fischer committed
78
Uml::ProgrammingLanguage::Enum CppWriter::language() const
79
{
80
    return Uml::ProgrammingLanguage::Cpp;
81
82
}

83
84
85
/**
 * Return the policy object.
 */
86
87
CPPCodeGenerationPolicy *CppWriter::policyExt()
{
88
    return static_cast<CPPCodeGenerationPolicy*>(UMLApp::app()->policyExt());
89
90
}

91
92
93
94
/**
 * Call this method to generate cpp code for a UMLClassifier.
 * @param c   the class to generate code for
 */
95
96
void CppWriter::writeClass(UMLClassifier *c)
{
Oliver Kellogg's avatar
Oliver Kellogg committed
97
    if (!c) {
Andi Fischer's avatar
Andi Fischer committed
98
        uDebug() << "Cannot write class of NULL concept!";
Oliver Kellogg's avatar
Oliver Kellogg committed
99
100
101
102
103
104
        return;
    }

    QFile fileh, filecpp;

    // find an appropriate name for our file
105
    fileName_ = findFileName(c, QLatin1String(".h"));
106
    if (fileName_.isEmpty()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
107
108
109
110
        emit codeGenerated(c, false);
        return;
    }

111
    className_ = cleanName(c->name());
Oliver Kellogg's avatar
Oliver Kellogg committed
112

113
    if (c->visibility() != Uml::Visibility::Implementation) {
114
        if (!openFile(fileh, fileName_)) {
115
116
117
118
119
120
121
            emit codeGenerated(c, false);
            return;
        }
        // write Header file
        writeHeaderFile(c, fileh);
        fileh.close();
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
122
123
124
125

    // Determine whether the implementation file is required.
    // (It is not required if the class is an enumeration.)
    bool need_impl = true;
126
    if (c->baseType() == UMLObject::ot_Enum || c->isInterface()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
127
128
129
        need_impl = false;
    }
    if (need_impl) {
130
131
        fileName_.replace(QRegExp(QLatin1String(".h$")), QLatin1String(".cpp"));
        if (!openFile(filecpp, fileName_)) {
Oliver Kellogg's avatar
Oliver Kellogg committed
132
133
134
135
136
137
138
139
140
            emit codeGenerated(c, false);
            return;
        }
        // write Source file
        writeSourceFile(c, filecpp);
        filecpp.close();
    }

    emit codeGenerated(c, true);
141
142
}

143
144
145
146
/**
 * Write the header file for this classifier.
 */
void CppWriter::writeHeaderFile (UMLClassifier *c, QFile &file)
147
{
Oliver Kellogg's avatar
Oliver Kellogg committed
148
    // open stream for writing
149
    QTextStream h (&file);
Oliver Kellogg's avatar
Oliver Kellogg committed
150
151
152
153
154

    // up the indent level to one
    m_indentLevel = 1;

    // write header blurb
155
    QString str = getHeadingFile(QLatin1String(".h"));
156
    if (!str.isEmpty()) {
157
158
        str.replace(QRegExp(QLatin1String("%filename%")), fileName_ + QLatin1String(".h"));
        str.replace(QRegExp(QLatin1String("%filepath%")), file.fileName());
159
        h << str<< m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
160
161
162
    }

    // Write the hash define stuff to prevent multiple parsing/inclusion of header
163
    QString hashDefine = className_.toUpper().simplified().replace(QRegExp(QLatin1String(" ")),  QLatin1String("_"));
Oliver Kellogg's avatar
Oliver Kellogg committed
164
    writeBlankLine(h);
165
166
    h << "#ifndef "<< hashDefine << "_H" << m_endl;
    h << "#define "<< hashDefine << "_H" << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
167

168
    writeClassDecl(c, h);
Oliver Kellogg's avatar
Oliver Kellogg committed
169
170

    // last thing..close our hashdefine
171
    h << m_endl << "#endif // " << hashDefine << "_H" << m_endl;
172
173
}

Andi Fischer's avatar
Andi Fischer committed
174
void CppWriter::writeHeaderAccessorMethodDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream)
175
{
Oliver Kellogg's avatar
Oliver Kellogg committed
176
    // attributes
177
178
    writeHeaderAttributeAccessorMethods(c, permitScope, true, stream); // write static attributes first
    writeHeaderAttributeAccessorMethods(c, permitScope, false, stream);
179

Oliver Kellogg's avatar
Oliver Kellogg committed
180
    // associations
181
    writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), permitScope,
182
                            true, INLINE_ASSOCIATION_METHODS, true, c->id(), stream);
183
    writeAssociationMethods(c->getUniAssociationToBeImplemented(), permitScope,
184
                            true, INLINE_ASSOCIATION_METHODS, true, c->id(), stream);
185
    writeAssociationMethods(c->getAggregations(), permitScope,
186
                            true,  INLINE_ASSOCIATION_METHODS, true, c->id(), stream);
187
    writeAssociationMethods(c->getCompositions(), permitScope,
188
                            true, INLINE_ASSOCIATION_METHODS, false, c->id(), stream);
189

Oliver Kellogg's avatar
Oliver Kellogg committed
190
    writeBlankLine(stream);
191
192
}

193
194
195
196
/**
 * Write out fields and operations for this class selected on a particular
 * visibility.
 */
Andi Fischer's avatar
Andi Fischer committed
197
void CppWriter::writeHeaderFieldDecl(UMLClassifier *c, Uml::Visibility::Enum permitScope, QTextStream &stream)
198
{
Oliver Kellogg's avatar
Oliver Kellogg committed
199
    // attributes
200
201
    writeAttributeDecls(c, permitScope, true, stream); // write static attributes first
    writeAttributeDecls(c, permitScope, false, stream);
202

Oliver Kellogg's avatar
Oliver Kellogg committed
203
    // associations
204
    writeAssociationDecls(c->getSpecificAssocs(Uml::AssociationType::Association), permitScope, c->id(), stream);
205
206
207
    writeAssociationDecls(c->getUniAssociationToBeImplemented(), permitScope, c->id(), stream);
    writeAssociationDecls(c->getAggregations(), permitScope, c->id(), stream);
    writeAssociationDecls(c->getCompositions(), permitScope, c->id(), stream);
208
209
}

210
211
212
213
/**
 * Write the source code body file for this classifier.
 */
void CppWriter::writeSourceFile(UMLClassifier *c, QFile &file)
214
{
Oliver Kellogg's avatar
Oliver Kellogg committed
215
    // open stream for writing
216
    QTextStream cpp (&file);
Oliver Kellogg's avatar
Oliver Kellogg committed
217
218
219
220
221
222

    // set the starting indentation at zero
    m_indentLevel = 0;

    //try to find a heading file (license, coments, etc)
    QString str;
223
    str = getHeadingFile(QLatin1String(".cpp"));
224
    if (!str.isEmpty()) {
225
226
        str.replace(QRegExp(QLatin1String("%filename%")), fileName_ + QLatin1String(".cpp"));
        str.replace(QRegExp(QLatin1String("%filepath%")), file.fileName());
227
        cpp << str << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
228
229
230
231
232
233
    }

    // IMPORT statements
    // Q: Why all utils? Isnt just List and Vector the only classes we are using?
    // Our import *should* also look at operations, and check that objects being
    // used arent in another package (and thus need to be explicitly imported here).
234
    cpp << "#include \"" << className_ << ".h\"" << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
235
236
    writeBlankLine(cpp);

237
    if (c->visibility() == Uml::Visibility::Implementation) {
238
239
240
        writeClassDecl(c, cpp);
    }

Oliver Kellogg's avatar
Oliver Kellogg committed
241
242
243
244
    // Start body of class

    // Constructors: anything we more we need to do here ?
    //
245
246
    if (!c->isInterface())
        writeConstructorMethods(c, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
247
248
249
250
251

    // METHODS
    //

    // write comment for section IF needed
252
253
    QString indnt = indent();
    if (forceDoc() || c->hasAccessorMethods() || c->hasOperationMethods()) {
254
255
256
        writeComment(QLatin1String(" "), indnt, cpp);
        writeComment(QLatin1String("Methods"), indnt, cpp);
        writeComment(QLatin1String(" "), indnt, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
257
258
259
260
261
        writeBlankLine(cpp);
        writeBlankLine(cpp);
    }

    // write comment for sub-section IF needed
262
    if (forceDoc() || c->hasAccessorMethods()) {
263
264
        writeComment(QLatin1String("Accessor methods"), indnt, cpp);
        writeComment(QLatin1String(" "), indnt, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
265
266
267
268
        writeBlankLine(cpp);
    }

    // Accessor methods for attributes
269
    const bool bInlineAccessors = policyExt()->getAccessorsAreInline();
270
    if (!bInlineAccessors && c->hasAttributes()) {
271
272
273
274
275
276
        writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Public), Uml::Visibility::Public, false, true, !bInlineAccessors, cpp);
        writeAttributeMethods(c->getAttributeList(Uml::Visibility::Public), Uml::Visibility::Public, false, false, !bInlineAccessors, cpp);
        writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Protected), Uml::Visibility::Protected, false, true, !bInlineAccessors, cpp);
        writeAttributeMethods(c->getAttributeList(Uml::Visibility::Protected), Uml::Visibility::Protected, false, false, !bInlineAccessors, cpp);
        writeAttributeMethods(c->getAttributeListStatic(Uml::Visibility::Private), Uml::Visibility::Private, false, true, !bInlineAccessors, cpp);
        writeAttributeMethods(c->getAttributeList(Uml::Visibility::Private), Uml::Visibility::Private, false, false, !bInlineAccessors, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
277
278
279
280
281
    }

    // accessor methods for associations

    // public
282
    writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Public, false,
283
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
284
    writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Public, false,
285
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
286
    writeAssociationMethods(c->getAggregations(), Uml::Visibility::Public, false,
287
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
288
    writeAssociationMethods(c->getCompositions(), Uml::Visibility::Public, false,
289
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
290
291

    // protected
292
    writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Protected, false,
293
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
294
    writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Protected, false,
295
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
296
    writeAssociationMethods(c->getAggregations(), Uml::Visibility::Protected, false,
297
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
298
    writeAssociationMethods(c->getCompositions(), Uml::Visibility::Protected, false,
299
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
300
301

    // private
302
    writeAssociationMethods(c->getSpecificAssocs(Uml::AssociationType::Association), Uml::Visibility::Private, false,
303
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
304
    writeAssociationMethods(c->getUniAssociationToBeImplemented(), Uml::Visibility::Private, false,
305
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
306
    writeAssociationMethods(c->getAggregations(), Uml::Visibility::Private, false,
307
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
308
    writeAssociationMethods(c->getCompositions(), Uml::Visibility::Private, false,
309
                            !INLINE_ASSOCIATION_METHODS, true, c->id(), cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
310
311
312
313
314
315
    writeBlankLine(cpp);

    // Other operation methods -- all other operations are now written
    //

    // write comment for sub-section IF needed
316
    if (forceDoc() || c->hasOperationMethods()) {
317
318
        writeComment(QLatin1String("Other methods"), indnt, cpp);
        writeComment(QLatin1String(" "), indnt, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
319
320
321
        writeBlankLine(cpp);
    }

322
    if (!policyExt()->getOperationsAreInline()) {
Ralf Habacker's avatar
Ralf Habacker committed
323
324
325
        writeOperations(c, false, Uml::Visibility::Public, cpp);
        writeOperations(c, false, Uml::Visibility::Protected, cpp);
        writeOperations(c, false, Uml::Visibility::Private, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
326
327
328
    }

    // Yep, bringing up the back of the bus, our initialization method for attributes
329
    writeInitAttributeMethod(c, cpp);
Oliver Kellogg's avatar
Oliver Kellogg committed
330
331

    writeBlankLine(cpp);
332
333
}

334
335
336
337
/**
 * Writes class's documentation to the class header
 * "public abstract class Foo extents {".
 */
338
339
void CppWriter::writeClassDecl(UMLClassifier *c, QTextStream &cpp)
{
340
    UMLClassifierList superclasses = c->getSuperClasses();
341
    foreach (UMLClassifier* classifier, superclasses) {
342
        QString headerName = findFileName(classifier, QLatin1String(".h"));
343
344
345
346
        if (!headerName.isEmpty()) {
            cpp << "#include \"" << headerName << "\"" << m_endl;
        }
    }
347

348
    writeBlankLine(cpp);
349
    cpp << "#include " << policyExt()->getStringClassNameInclude() << m_endl;
350
    if (c->hasVectorFields())
351
    {
352
        cpp << "#include " << policyExt()->getVectorClassNameInclude() << m_endl;
353
354
        writeBlankLine(cpp);
    }
355

356
    if (c->hasAssociations())
Oliver Kellogg's avatar
Oliver Kellogg committed
357
    {
358
        // write all includes we need to include other classes, that arent us.
359
        printAssociationIncludeDecl (c->getSpecificAssocs(Uml::AssociationType::Association), c->id(), cpp);
360
361
362
        printAssociationIncludeDecl (c->getUniAssociationToBeImplemented(), c->id(), cpp);
        printAssociationIncludeDecl (c->getAggregations(), c->id(), cpp);
        printAssociationIncludeDecl (c->getCompositions(), c->id(), cpp);
363

Oliver Kellogg's avatar
Oliver Kellogg committed
364
365
        writeBlankLine(cpp);
    }
366

367
    foreach (UMLClassifier* classifier, superclasses) {
368
369
        if (classifier->package()!=c->package() && !classifier->package().isEmpty()) {
            cpp << "using " << cleanName(classifier->package()) << "::" << cleanName(classifier->name()) << ";" << m_endl;
370
371
372
        }
    }

373
374
    if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace())
        cpp << m_endl << "namespace " << cleanName(c->package()) << " {" << m_endl << m_endl;
375
376

    //Write class Documentation if there is somthing or if force option
377
    if (forceDoc() || !c->doc().isEmpty()) {
378
        cpp << m_endl << "/**" << m_endl;
379
        cpp << "  * class " << className_ << m_endl;
380
        cpp << formatDoc(c->doc(), QLatin1String("  * "));
381
382
383
384
        cpp << "  */";
        writeBlankLine(cpp);
        writeBlankLine(cpp);
    }
385

386
    //check if class is abstract and / or has abstract methods
387
    if ((c->isAbstract() || c->isInterface())
388
389
            && !hasAbstractOps(c))
        cpp << "/******************************* Abstract Class ****************************" << m_endl
390
391
392
393
        << className_ << " does not have any pure virtual methods, but its author" << m_endl
        << "  defined it as an abstract class, so you should not use it directly." << m_endl
        << "  Inherit from it instead and create only objects from the derived classes" << m_endl
        << "*****************************************************************************/" << m_endl << m_endl;
394

395
396
    if (c->baseType() == UMLObject::ot_Enum) {
        UMLClassifierListItemList litList = c->getFilteredList(UMLObject::ot_EnumLiteral);
397
        uint i = 0;
398
        cpp << "enum " << className_ << " {" << m_endl;
399
        foreach (UMLClassifierListItem* lit, litList) {
400
            UMLEnumLiteral *el = static_cast<UMLEnumLiteral *>(lit);
401
            QString enumLiteral = cleanName(lit->name());
402
            cpp << indent() << enumLiteral;
403
404
            if (!el->value().isEmpty())
                cpp << " = " << el->value();
405
            if (++i < (uint)litList.count())
406
407
408
409
                cpp << ",";
            cpp << m_endl;
        }
        cpp << m_endl << "};" << m_endl;  // end of class header
410
        if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace())
411
412
413
414
415
416
417
418
            cpp << "}  // end of package namespace" << m_endl;
        return;
    }

    // Generate template parameters.
    UMLTemplateList template_params = c->getTemplateList();
    if (template_params.count()) {
        cpp << "template<";
419
        for (UMLTemplateListIt tlit(template_params); tlit.hasNext();) {
Sharan Rao's avatar
Sharan Rao committed
420
            UMLTemplate* t = tlit.next();
421
            QString formalName = t->name();
422
423
            QString typeName = t->getTypeName();
            cpp << typeName << " " << formalName;
424
            if (tlit.hasNext()) {
Ralf Habacker's avatar
Ralf Habacker committed
425
                tlit.next();
426
                cpp << ", ";
Sharan Rao's avatar
Sharan Rao committed
427
            }
428
429
430
431
        }
        cpp << ">" << m_endl;
    }

432
433
434
    cpp << "class " << className_;
    uint numOfSuperClasses = c->getSuperClasses().count();
    if (numOfSuperClasses > 0)
435
436
        cpp << " : ";
    uint i = 0;
437
    foreach (UMLClassifier* superClass, c->getSuperClasses()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
438
        i++;
439
        if (superClass->isAbstract() || superClass->isInterface())
440
            cpp << "virtual ";
441
        cpp << "public " << cleanName(superClass->name());
442
443
        if (i < numOfSuperClasses)
            cpp << ", ";
Oliver Kellogg's avatar
Oliver Kellogg committed
444
    }
445

446
447
448
449
450
451
452
453
    cpp << m_endl << "{" << m_endl; // begin the body of the class

    //
    // write out field and operations decl grouped by visibility
    //

    // PUBLIC attribs/methods
    cpp << "public:" << m_endl << m_endl; // print visibility decl.
454
    writeDataTypes(c, Uml::Visibility::Public, cpp);
455
    // for public: constructors are first ops we print out
456
    if (!c->isInterface())
457
        writeConstructorDecls(cpp);
Ralf Habacker's avatar
Ralf Habacker committed
458
    writeHeaderFieldDecl(c, Uml::Visibility::Public, cpp);
459
    writeHeaderAccessorMethodDecl(c, Uml::Visibility::Public, cpp);
Ralf Habacker's avatar
Ralf Habacker committed
460
    writeOperations(c, true, Uml::Visibility::Public, cpp);
461
462
463
464

    // PROTECTED attribs/methods
    //
    cpp << "protected" << ":" << m_endl << m_endl; // print visibility decl.
465
    writeDataTypes(c, Uml::Visibility::Protected, cpp);
Ralf Habacker's avatar
Ralf Habacker committed
466
    writeHeaderFieldDecl(c, Uml::Visibility::Protected, cpp);
467
    writeHeaderAccessorMethodDecl(c, Uml::Visibility::Protected, cpp);
Ralf Habacker's avatar
Ralf Habacker committed
468
    writeOperations(c, true, Uml::Visibility::Protected, cpp);
469
470
471
472

    // PRIVATE attribs/methods
    //
    cpp << "private" << ":" << m_endl << m_endl; // print visibility decl.
473
    writeDataTypes(c, Uml::Visibility::Private, cpp);
Ralf Habacker's avatar
Ralf Habacker committed
474
    writeHeaderFieldDecl(c, Uml::Visibility::Private, cpp);
475
    writeHeaderAccessorMethodDecl(c, Uml::Visibility::Private, cpp);
Ralf Habacker's avatar
Ralf Habacker committed
476
    writeOperations(c, true, Uml::Visibility::Private, cpp);
477
    writeInitAttributeDecl(c, cpp); // this is always private, used by constructors to initialize class
478
479
480
481
482

    // end of class header
    cpp << m_endl << "};" << m_endl;

    // end of class namespace, if any
483
    if (!c->package().isEmpty() && policyExt()->getPackageIsNamespace())
484
        cpp << "} // end of package namespace" << m_endl;
485

486
487
}

488
489
490
491
492
493
494
/**
 * Writes the attribute declarations.
 * @param c             the classifier
 * @param visibility    the visibility of the attribs to print out
 * @param writeStatic   whether to write static or non-static attributes out
 * @param stream        text stream
 */
495
void CppWriter::writeAttributeDecls (UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream)
496
{
497
    if (c->isInterface())
Oliver Kellogg's avatar
Oliver Kellogg committed
498
499
        return;

500
501
502
503
504
    UMLAttributeList list;
    if (writeStatic)
        list = c->getAttributeListStatic(visibility);
    else
        list = c->getAttributeList(visibility);
Oliver Kellogg's avatar
Oliver Kellogg committed
505
506

    //write documentation
507
    if (forceDoc() || list.count() > 0)
Oliver Kellogg's avatar
Oliver Kellogg committed
508
    {
Andi Fischer's avatar
Andi Fischer committed
509
        QString strVis = Codegen_Utils::capitalizeFirstLetter(Uml::Visibility::toString(visibility));
510
511
512
        QString strStatic = writeStatic ? QLatin1String("Static ") : QString();
        writeComment(strStatic + strVis + QLatin1String(" attributes"), indent(), stream);
        writeComment(QLatin1String(" "), indent(), stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
513
514
515
        writeBlankLine(stream);
    }

516
    if (list.count() > 0) {
Oliver Kellogg's avatar
Oliver Kellogg committed
517
518

        // write attrib declarations now
519
        // bool isFirstAttrib = true;
Oliver Kellogg's avatar
Oliver Kellogg committed
520
        QString documentation;
521
        foreach (UMLAttribute* at, list) {
522

523
            //                  bool noPriorDocExists = documentation.isEmpty();
524
            documentation = at->doc();
525

Oliver Kellogg's avatar
Oliver Kellogg committed
526
527
            // add a line for code clarity IF PRIOR attrib has comment on it
            // OR this line has documentation
528
            //                  if (!isFirstAttrib && (!documentation.isEmpty()||!noPriorDocExists))
529
            //                          writeBlankLine(stream);
530

531
            // isFirstAttrib = false;
532

Oliver Kellogg's avatar
Oliver Kellogg committed
533
            QString varName = getAttributeVariableName(at);
534

535
            QString staticValue = at->isStatic() ? QLatin1String("static ") : QString();
Oliver Kellogg's avatar
Oliver Kellogg committed
536
            QString typeName = fixTypeName(at->getTypeName());
537
538
539
540
541
            int i = typeName.indexOf(QLatin1Char('['));
            if (i > -1) {
                varName += typeName.mid(i);
                typeName = typeName.left(i);
            }
542
            if (!documentation.isEmpty())
543
544
                writeComment(documentation, indent(), stream);
            stream << indent() << staticValue << typeName << " " << varName << ";" << m_endl;
545

Oliver Kellogg's avatar
Oliver Kellogg committed
546
        }
547

Oliver Kellogg's avatar
Oliver Kellogg committed
548
        /*
549
        if (list.count() > 0)
550
            writeBlankLine(stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
551
552
        */
    }
553
554
}

555
void CppWriter::writeHeaderAttributeAccessorMethods (UMLClassifier *c, Uml::Visibility::Enum visibility, bool writeStatic, QTextStream &stream)
556
{
557
    // check the current policy about generate accessors as public
558
559
560
561
562
    UMLAttributeList list;
    if (writeStatic)
        list = c->getAttributeListStatic(visibility);
    else
        list = c->getAttributeList(visibility);
Oliver Kellogg's avatar
Oliver Kellogg committed
563

564
565
566
567
    // switch to public
    if (visibility != Uml::Visibility::Public)
        stream << "public:" << m_endl << m_endl;

Oliver Kellogg's avatar
Oliver Kellogg committed
568
    // write accessor methods for attribs we found
569
    writeAttributeMethods(list, visibility, true, false, policyExt()->getAccessorsAreInline(), stream);
570

571
572
    // switch back to previous vis.
    if (visibility != Uml::Visibility::Public)
Andi Fischer's avatar
Andi Fischer committed
573
        stream << Uml::Visibility::toString(visibility) << ":" << m_endl << m_endl;
574
575
}

576
577
578
579
/**
 * This is for writing *source* or *header* file attribute methods.
 * Calls @ref writeSingleAttributeAccessorMethods() on each of the attributes in attribs list.
 */
580
void CppWriter::writeAttributeMethods(UMLAttributeList attribs,
Andi Fischer's avatar
Andi Fischer committed
581
                                      Uml::Visibility::Enum visibility, bool isHeaderMethod,
Oliver Kellogg's avatar
Oliver Kellogg committed
582
583
                                      bool isStatic,
                                      bool writeMethodBody, QTextStream &stream)
584
{
585
    if (!policyExt()->getAutoGenerateAccessors())
Oliver Kellogg's avatar
Oliver Kellogg committed
586
587
        return;

588
    if (forceDoc() || attribs.count() > 0)
Oliver Kellogg's avatar
Oliver Kellogg committed
589
    {
Andi Fischer's avatar
Andi Fischer committed
590
        QString strVis = Codegen_Utils::capitalizeFirstLetter(Uml::Visibility::toString(visibility));
591
        QString strStatic = (isStatic ? QLatin1String(" static") : QString());
Oliver Kellogg's avatar
Oliver Kellogg committed
592
        writeBlankLine(stream);
593
594
        writeComment(strVis + strStatic + QLatin1String(" attribute accessor methods"), indent(), stream);
        writeComment(QLatin1String(" "), indent(), stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
595
596
597
598
        writeBlankLine(stream);
    }

    // return now if NO attributes to work on
599
    if (attribs.count() == 0)
Oliver Kellogg's avatar
Oliver Kellogg committed
600
601
        return;

602
    foreach (UMLAttribute* at, attribs) {
Oliver Kellogg's avatar
Oliver Kellogg committed
603
        QString varName = getAttributeVariableName(at);
604
        QString methodBaseName = cleanName(at->name());
Oliver Kellogg's avatar
Oliver Kellogg committed
605
606
607

        // force capitalizing the field name, this is silly,
        // from what I can tell, this IS the default behavior for
Frederik Schwarzer's avatar
Frederik Schwarzer committed
608
        // cleanName. I dunno why it is not working -b.t.
609
        methodBaseName = methodBaseName.trimmed();
Ralf Habacker's avatar
Ralf Habacker committed
610
        methodBaseName.replace(0, 1, methodBaseName.at(0).toUpper());
Oliver Kellogg's avatar
Oliver Kellogg committed
611
612

        writeSingleAttributeAccessorMethods(at->getTypeName(), varName,
613
                                            methodBaseName, at->doc(), Uml::Changeability::Changeable, isHeaderMethod,
614
                                            at->isStatic(), writeMethodBody, stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
615
    }
616
617
}

618
619
620
/**
 * Writes a // style comment.
 */
621
void CppWriter::writeComment(const QString &comment, const QString &myIndent, QTextStream &cpp)
622
{
Oliver Kellogg's avatar
Oliver Kellogg committed
623
624
625
    // in the case we have several line comment..
    // NOTE: this part of the method has the problem of adopting UNIX newline,
    // need to resolve for using with MAC/WinDoze eventually I assume
626
    if (comment.contains(QRegExp(QLatin1String("\n")))) {
Oliver Kellogg's avatar
Oliver Kellogg committed
627

628
        QStringList lines = comment.split(QLatin1Char('\n'));
629
        for (int i= 0; i < lines.count(); ++i)
Oliver Kellogg's avatar
Oliver Kellogg committed
630
        {
631
            cpp << myIndent << QLatin1String("// ") << lines[i] << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
632
633
634
        }
    } else {
        // this should be more fancy in the future, breaking it up into 80 char
635
        // lines so that it doesn't look too bad
636
        cpp << myIndent << QLatin1String("// ")<< comment << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
637
    }
638
639
}

640
641
642
/**
 * Writes a documentation comment.
 */
643
644
void CppWriter::writeDocumentation(QString header, QString body, QString end, QTextStream &cpp)
{
Oliver Kellogg's avatar
Oliver Kellogg committed
645
    writeBlankLine(cpp);
646
    QString indnt = indent();
Oliver Kellogg's avatar
Oliver Kellogg committed
647

648
    cpp << indnt << QLatin1String("/**") << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
649
    if (!header.isEmpty())
650
        cpp << formatDoc(header, indnt + QLatin1String(" * "));
Oliver Kellogg's avatar
Oliver Kellogg committed
651
    if (!body.isEmpty())
652
        cpp << formatDoc(body, indnt + QLatin1String(" * "));
653
    if (!end.isEmpty()) {
654
        QStringList lines = end.split(QLatin1Char('\n'));
655
        for (int i = 0; i < lines.count(); ++i) {
656
            cpp << formatDoc(lines[i], indnt + QLatin1String(" * "));
657
        }
Oliver Kellogg's avatar
Oliver Kellogg committed
658
    }
659
    cpp << indnt << QLatin1String(" */") << m_endl;
660
661
}

662
663
664
/**
 * Searches a list of associations for appropriate ones to write out as attributes.
 */
Andi Fischer's avatar
Andi Fischer committed
665
void CppWriter::writeAssociationDecls(UMLAssociationList associations, Uml::Visibility::Enum permitScope, Uml::ID::Type id, QTextStream &h)
666
{
667
    if (forceSections() || !associations.isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
668
669
    {
        bool printRoleA = false, printRoleB = false;
Ralf Habacker's avatar
Ralf Habacker committed
670
        foreach (UMLAssociation *a, associations)
Oliver Kellogg's avatar
Oliver Kellogg committed
671
672
673
        {
            // it may seem counter intuitive, but you want to insert the role of the
            // *other* class into *this* class.
674
            if (a->getObjectId(Uml::RoleType::A) == id && !a->getRoleName(Uml::RoleType::B).isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
675
676
                printRoleB = true;

677
            if (a->getObjectId(Uml::RoleType::B) == id && !a->getRoleName(Uml::RoleType::A).isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
678
679
680
                printRoleA = true;

            // First: we insert documentaion for association IF it has either role AND some documentation (!)
681
682
            if ((printRoleA || printRoleB) && !(a->doc().isEmpty()))
                writeComment(a->doc(), indent(), h);
Oliver Kellogg's avatar
Oliver Kellogg committed
683
684

            // print RoleB decl
685
            if (printRoleB && a->visibility(Uml::RoleType::B) == permitScope)
Oliver Kellogg's avatar
Oliver Kellogg committed
686
687
            {

688
689
                QString fieldClassName = cleanName(umlObjectName(a->getObject(Uml::RoleType::B)));
                writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::RoleType::B), a->getMultiplicity(Uml::RoleType::B), a->getRoleDoc(Uml::RoleType::B), h);
Oliver Kellogg's avatar
Oliver Kellogg committed
690
691
692
            }

            // print RoleA decl
693
            if (printRoleA && a->visibility(Uml::RoleType::A) == permitScope)
Oliver Kellogg's avatar
Oliver Kellogg committed
694
            {
695
696
                QString fieldClassName = cleanName(umlObjectName(a->getObject(Uml::RoleType::A)));
                writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::RoleType::A), a->getMultiplicity(Uml::RoleType::A), a->getRoleDoc(Uml::RoleType::A), h);
Oliver Kellogg's avatar
Oliver Kellogg committed
697
698
699
700
701
702
703
            }

            // reset for next association in our loop
            printRoleA = false;
            printRoleB = false;
        }
    }
704
705
}

706
707
708
/**
 * Writes out an association as an attribute using Vector.
 */
709
void CppWriter::writeAssociationRoleDecl(QString fieldClassName, QString roleName, QString multi,
Oliver Kellogg's avatar
Oliver Kellogg committed
710
        QString doc, QTextStream &stream)
711
{
Oliver Kellogg's avatar
Oliver Kellogg committed
712
    // ONLY write out IF there is a rolename given
Frederik Schwarzer's avatar
Frederik Schwarzer committed
713
    // otherwise it is not meant to be declared in the code
Oliver Kellogg's avatar
Oliver Kellogg committed
714
715
716
    if (roleName.isEmpty())
        return;

717
    QString indnt = indent();
Oliver Kellogg's avatar
Oliver Kellogg committed
718
719
720
721
722

    // always put space between this and prior decl, if any
    writeBlankLine(stream);

    if (!doc.isEmpty())
723
        writeComment(doc, indnt, stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
724
725
726

    // 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
727
    // multiplicity object that we don't have to figure out what it means via regex.
728
    if (multi.isEmpty() || multi.contains(QRegExp(QLatin1String("^[01]$"))))
Oliver Kellogg's avatar
Oliver Kellogg committed
729
    {
730
        QString fieldVarName = QLatin1String("m_") + roleName.toLower();
Oliver Kellogg's avatar
Oliver Kellogg committed
731
732
733

        // record this for later consideration of initialization IF the
        // multi value requires 1 of these objects
734
        if (ObjectFieldVariables.indexOf(fieldVarName) == -1 &&
735
                multi.contains(QRegExp(QLatin1String("^1$"))))
Oliver Kellogg's avatar
Oliver Kellogg committed
736
737
738
739
740
741
        {
            // ugh. UGLY. Storing variable name and its class in pairs.
            ObjectFieldVariables.append(fieldVarName);
            ObjectFieldVariables.append(fieldClassName);
        }

742
        stream << indnt << fieldClassName << " * " << fieldVarName << ";" << m_endl;
Oliver Kellogg's avatar
Oliver Kellogg committed
743
744
745
    }
    else
    {
746
        QString fieldVarName = QLatin1String("m_") + roleName.toLower() + QLatin1String("Vector");
Oliver Kellogg's avatar
Oliver Kellogg committed
747

748
        // record unique occurrences for later when we want to check
Oliver Kellogg's avatar
Oliver Kellogg committed
749
        // for initialization of this vector
750
        if (VectorFieldVariables.indexOf(fieldVarName) == -1)
Oliver Kellogg's avatar
Oliver Kellogg committed
751
752
            VectorFieldVariables.append(fieldVarName);

753
        stream << indnt << policyExt()->getVectorClassName() << "<" << fieldClassName << "*";
Oliver Kellogg's avatar
Oliver Kellogg committed
754
755
        stream << "> " << fieldVarName << ";" << m_endl;
    }
756
757
}

758
759
760
761
/**
 * Calls @ref writeAssociationRoleMethod() on each of the associations in the given list
 * for either source or header files.
 */
762
void CppWriter::writeAssociationMethods (UMLAssociationList associations,
Andi Fischer's avatar
Andi Fischer committed
763
        Uml::Visibility::Enum permitVisib,
Oliver Kellogg's avatar
Oliver Kellogg committed
764
765
766
        bool isHeaderMethod,
        bool writeMethodBody,
        bool writePointerVar,
Andi Fischer's avatar
Andi Fischer committed
767
        Uml::ID::Type myID, QTextStream &stream)
768
{
769
    if (forceSections() || !associations.isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
770
    {
771
        foreach (UMLAssociation *a, associations)
Oliver Kellogg's avatar
Oliver Kellogg committed
772
773
774
775
        {

            // insert the methods to access the role of the other
            // class in the code of this one
776
            if (a->getObjectId(Uml::RoleType::A) == myID && a->visibility(Uml::RoleType::B) == permitVisib)
Oliver Kellogg's avatar
Oliver Kellogg committed
777
778
            {
                // only write out IF there is a rolename given
779
                if (!a->getRoleName(Uml::RoleType::B).isEmpty()) {
780
                    QString fieldClassName = umlObjectName(a->getObject(Uml::RoleType::B)) + (writePointerVar ? QLatin1String(" *") : QString());
Oliver Kellogg's avatar
Oliver Kellogg committed
781
782
783
                    writeAssociationRoleMethod(fieldClassName,
                                               isHeaderMethod,
                                               writeMethodBody,
784
785
786
                                               a->getRoleName(Uml::RoleType::B),
                                               a->getMultiplicity(Uml::RoleType::B), a->getRoleDoc(Uml::RoleType::B),
                                               a->changeability(Uml::RoleType::B), stream);
Oliver Kellogg's avatar
Oliver Kellogg committed
787
788
789
                }
            }

790
            if (a->getObjectId(Uml::RoleType::B) == myID && a->visibility(Uml::RoleType::A) == permitVisib)
Oliver Kellogg's avatar
Oliver Kellogg committed
791
792
            {
                // only write out IF there is a rolename given
793
                if (!a->getRoleName(Uml::RoleType::A).isEmpty()) {
794
                    QString fieldClassName = umlObjectName(a->getObject(Uml::RoleType::A)) + (writePointerVar ? QLatin1String(" *") : QString());
Oliver Kellogg's avatar
Oliver Kellogg committed
795
796
797
                    writeAssociationRoleMethod(fieldClassName,
                                               isHeaderMethod,
                                               writeMethodBody,
798
799
800
801
                                               a->getRoleName(Uml::RoleType::A),
                                               a->getMultiplicity(Uml::RoleType::A),
                                               a->getRoleDoc(Uml::RoleType::A),
                                               a->changeability(Uml::RoleType::A),
Oliver Kellogg's avatar
Oliver Kellogg committed
802
803
804
805
806
807
                                               stream);
                }
            }

        }
    }
808
809
}

810
811
812
813
814
/**
 * Calls @ref writeSingleAttributeAccessorMethods() or @ref
 * writeVectorAttributeAccessorMethods() on the association
 * role.
 */