1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3 
4     SPDX-FileCopyrightText: 2003 Brian Thomas <thomas@mail630.gsfc.nasa.gov>
5     SPDX-FileCopyrightText: 2004-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
6 */
7 
8 // own header
9 #include "codegenerator.h"
10 
11 // app includes
12 #include "debug_utils.h"
13 #include "overwritedialog.h"
14 #include "codeviewerdialog.h"
15 #include "simplecodegenerator.h"
16 #include "attribute.h"
17 #include "association.h"
18 #include "classifier.h"
19 #include "classifiercodedocument.h"
20 #include "codedocument.h"
21 #include "codegenerationpolicy.h"
22 #include "operation.h"
23 #include "uml.h"
24 #include "umldoc.h"
25 #include "umlobject.h"
26 #include "umlattributelist.h"
27 #include "umloperationlist.h"
28 
29 #include <KLocalizedString>
30 #include <KMessageBox>
31 
32 // qt includes
33 #include <QApplication>
34 #include <QDateTime>
35 #include <QDir>
36 #include <QDomDocument>
37 #include <QDomElement>
38 #include <QPointer>
39 #include <QRegExp>
40 #include <QTextStream>
41 #include <QXmlStreamWriter>
42 
43 // system includes
44 #include <cstdlib>  // to get the user name
45 
46 /**
47  * Constructor for a code generator.
48  */
CodeGenerator()49 CodeGenerator::CodeGenerator()
50   : m_applyToAllRemaining(true),
51     m_document(UMLApp::app()->document()),
52     m_lastIDIndex(0)
53 {
54     // initial population of our project generator
55     // CANNOT Be done here because we would call pure virtual method
56     // of newClassifierDocument (bad!).
57     // We should only call from the child
58     // initFromParentDocument();
59 }
60 
61 /**
62  * Destructor.
63  */
~CodeGenerator()64 CodeGenerator::~CodeGenerator()
65 {
66     // destroy all owned codedocuments
67     qDeleteAll(m_codedocumentVector);
68     m_codedocumentVector.clear();
69 }
70 
71 /**
72  * Get a unique id for this codedocument.
73  * @return   id for the codedocument
74  */
getUniqueID(CodeDocument * codeDoc)75 QString CodeGenerator::getUniqueID(CodeDocument * codeDoc)
76 {
77     QString id = codeDoc->ID();
78 
79     // does this document already exist? then just return its present id
80     if (!id.isEmpty() && findCodeDocumentByID(id)) {
81         return id;
82     }
83 
84     // approach now differs by whether or not it is a classifier code document
85     ClassifierCodeDocument * classDoc = dynamic_cast<ClassifierCodeDocument*>(codeDoc);
86     if (classDoc) {
87         UMLClassifier *c = classDoc->getParentClassifier();
88         id = Uml::ID::toString(c->id()); // this is supposed to be unique already..
89     }
90     else {
91         QString prefix = QLatin1String("doc");
92         QString id = prefix + QLatin1String("_0");
93         int number = m_lastIDIndex;
94         for (; findCodeDocumentByID(id); ++number) {
95             id = prefix + QLatin1Char('_') + QString::number(number);
96         }
97         m_lastIDIndex = number;
98     }
99 
100     return id;
101 }
102 
103 /**
104  * Find a code document by the given id string.
105  * @return  CodeDocument
106  */
findCodeDocumentByID(const QString & tag)107 CodeDocument * CodeGenerator::findCodeDocumentByID(const QString &tag)
108 {
109     CodeDocument* doc = m_codeDocumentDictionary.value(tag);
110     if (doc) {
111         return doc;
112     }
113     else {
114         return 0;
115     }
116 }
117 
118 /**
119  * Add a CodeDocument object to the m_codedocumentVector List.
120  * @return boolean - will return false if it couldnt add a document
121  */
addCodeDocument(CodeDocument * doc)122 bool CodeGenerator::addCodeDocument(CodeDocument * doc)
123 {
124     QString tag = doc->ID();
125 
126     // assign a tag if one doesn't already exist
127     if (tag.isEmpty()) {
128         tag = getUniqueID(doc);
129         doc->setID(tag);
130     }
131 
132     if (m_codeDocumentDictionary.contains(tag)) {
133         return false;  // return false, we already have some object with this tag in the list
134     }
135     else {
136         m_codeDocumentDictionary.insert(tag, doc);
137     }
138 
139     m_codedocumentVector.append(doc);
140     return true;
141 }
142 
143 /**
144  * Remove a CodeDocument object from m_codedocumentVector List.
145  * @return boolean - will return false if it couldnt remove a document
146  */
removeCodeDocument(CodeDocument * remove_object)147 bool CodeGenerator::removeCodeDocument(CodeDocument * remove_object)
148 {
149     QString tag = remove_object->ID();
150     if (!(tag.isEmpty())) {
151         m_codeDocumentDictionary.remove(tag);
152     }
153     else {
154         return false;
155     }
156 
157     m_codedocumentVector.removeAll(remove_object);
158     return true;
159 }
160 
161 /**
162  * Get the list of CodeDocument objects held by m_codedocumentVector.
163  * @return CodeDocumentList list of CodeDocument objects held by
164  * m_codedocumentVector
165  */
getCodeDocumentList()166 CodeDocumentList * CodeGenerator::getCodeDocumentList()
167 {
168     return &m_codedocumentVector;
169 }
170 
171 /**
172  * Load codegenerator data from xmi.
173  * @param qElement   the element from which to load
174  */
loadFromXMI1(QDomElement & qElement)175 void CodeGenerator::loadFromXMI1(QDomElement & qElement)
176 {
177     // look for our particular child element
178     QDomNode node = qElement.firstChild();
179     QDomElement element = node.toElement();
180     QString langType = Uml::ProgrammingLanguage::toString(language());
181 
182     if (qElement.tagName() != QLatin1String("codegenerator")
183         || qElement.attribute(QLatin1String("language"), QLatin1String("UNKNOWN")) != langType) {
184         return;
185     }
186     // got our code generator element, now load
187     // codedocuments
188     QDomNode codeDocNode = qElement.firstChild();
189     QDomElement codeDocElement = codeDocNode.toElement();
190     while (!codeDocElement.isNull()) {
191         QString docTag = codeDocElement.tagName();
192         QString id = codeDocElement.attribute(QLatin1String("id"), QLatin1String("-1"));
193         if (docTag == QLatin1String("sourcecode")) {
194             loadCodeForOperation(id, codeDocElement);
195         }
196         else if (docTag == QLatin1String("codedocument") || docTag == QLatin1String("classifiercodedocument")) {
197             CodeDocument * codeDoc = findCodeDocumentByID(id);
198             if (codeDoc) {
199                 codeDoc->loadFromXMI1(codeDocElement);
200             }
201             else {
202                 uWarning() << "missing code document for id:" << id;
203             }
204         }
205         else {
206             uWarning() << "got strange codegenerator child node:" << docTag << ", ignoring.";
207         }
208         codeDocNode = codeDocElement.nextSibling();
209         codeDocElement = codeDocNode.toElement();
210     }
211 }
212 
213 /**
214  * Extract and load code for operations from xmi section.
215  * Probably we have code which was entered in classpropdlg for an operation.
216  */
loadCodeForOperation(const QString & idStr,const QDomElement & codeDocElement)217 void CodeGenerator::loadCodeForOperation(const QString& idStr, const QDomElement& codeDocElement)
218 {
219     Uml::ID::Type id = Uml::ID::fromString(idStr);
220     UMLObject *obj = m_document->findObjectById(id);
221     if (obj) {
222         uDebug() << "found UMLObject for id:" << idStr;
223         QString value = codeDocElement.attribute(QLatin1String("value"));
224 
225         UMLObject::ObjectType t = obj->baseType();
226         if (t == UMLObject::ot_Operation) {
227             UMLOperation *op = obj->asUMLOperation();
228             op->setSourceCode(value);
229         }
230         else {
231             uError() << "sourcecode id " << idStr << " has unexpected type " << UMLObject::toString(t);
232         }
233     }
234     else {
235         uError() << "unknown sourcecode id " << idStr;
236     }
237 }
238 
239 /**
240  * Save the XMI representation of this object
241  */
saveToXMI1(QXmlStreamWriter & writer)242 void CodeGenerator::saveToXMI1(QXmlStreamWriter& writer)
243 {
244     QString langType = Uml::ProgrammingLanguage::toString(language());
245     writer.writeStartElement(QLatin1String("codegenerator"));
246     writer.writeAttribute(QLatin1String("language"), langType);
247 
248     if (dynamic_cast<SimpleCodeGenerator*>(this)) {
249         UMLClassifierList concepts = m_document->classesAndInterfaces();
250         foreach (UMLClassifier *c, concepts) {
251             uIgnoreZeroPointer(c);
252             UMLOperationList operations = c->getOpList();
253             foreach (UMLOperation *op, operations) {
254                 // save the source code
255                 QString code = op->getSourceCode();
256                 if (code.isEmpty()) {
257                     continue;
258                 }
259                 writer.writeStartElement(QLatin1String("sourcecode"));
260                 writer.writeAttribute(QLatin1String("id"), Uml::ID::toString(op->id()));
261                 writer.writeAttribute(QLatin1String("value"), code);
262                 writer.writeEndElement();
263             }
264         }
265     }
266     else {
267         const CodeDocumentList * docList = getCodeDocumentList();
268         CodeDocumentList::const_iterator it = docList->begin();
269         CodeDocumentList::const_iterator end = docList->end();
270         for (; it != end; ++it) {
271             (*it)->saveToXMI1(writer);
272         }
273     }
274     writer.writeEndElement();
275 }
276 
277 /**
278  * Force a synchronize of this code generator, and its present contents, to that of the parent UMLDocument.
279  * "UserGenerated" code will be preserved, but Autogenerated contents will be updated/replaced
280  * or removed as is apppropriate.
281  */
syncCodeToDocument()282 void CodeGenerator::syncCodeToDocument()
283 {
284     CodeDocumentList::iterator it = m_codedocumentVector.begin();
285     CodeDocumentList::iterator end = m_codedocumentVector.end();
286     for (; it != end; ++it) {
287         (*it)->synchronize();
288     }
289 }
290 
291 /**
292  * Find a code document by the given classifier.
293  * NOTE: FIX, this should be 'protected' or we could have problems with CPP code generator
294  * @return      CodeDocument
295  * @param       classifier
296  */
findCodeDocumentByClassifier(UMLClassifier * classifier)297 CodeDocument * CodeGenerator::findCodeDocumentByClassifier(UMLClassifier * classifier)
298 {
299     return findCodeDocumentByID(Uml::ID::toString(classifier->id()));
300 }
301 
302 /**
303  * This method is here to provide class wizard the
304  * ability to write out only those classes which
305  * are selected by the user.
306  */
writeCodeToFile()307 void CodeGenerator::writeCodeToFile()
308 {
309     writeListedCodeDocsToFile(&m_codedocumentVector);
310     finalizeRun();
311 }
312 
313 /**
314  * This method is here to provide class wizard the
315  * ability to write out only those classes which
316  * are selected by the user.
317  */
writeCodeToFile(UMLClassifierList & concepts)318 void CodeGenerator::writeCodeToFile(UMLClassifierList & concepts)
319 {
320     CodeDocumentList docs;
321 
322     foreach (UMLClassifier *concept, concepts) {
323         CodeDocument * doc = findCodeDocumentByClassifier(concept);
324         if (doc) {
325             docs.append(doc);
326         }
327     }
328 
329     writeListedCodeDocsToFile(&docs);
330     finalizeRun();
331 }
332 
333 // Main method. Will write out passed code documents to file as appropriate.
334 
335 /**
336  * The actual internal routine which writes code documents.
337  */
writeListedCodeDocsToFile(CodeDocumentList * docs)338 void CodeGenerator::writeListedCodeDocsToFile(CodeDocumentList * docs)
339 {
340     // iterate thru all code documents
341     CodeDocumentList::iterator it = docs->begin();
342     CodeDocumentList::iterator end = docs->end();
343     for (; it != end; ++it)
344     {
345         // we need this so we know when to emit a 'codeGenerated' signal
346         ClassifierCodeDocument * cdoc = dynamic_cast<ClassifierCodeDocument *>(*it);
347         bool codeGenSuccess = false;
348 
349         // we only write the document, if so requested
350         if ((*it)->getWriteOutCode()) {
351             QString filename = findFileName(*it);
352             // check that we may open that file for writing
353             QFile file;
354             if (openFile(file, filename)) {
355                 QTextStream stream(&file);
356                 stream << (*it)->toString() << endl;
357                 file.close();
358                 codeGenSuccess = true; // we wrote the code - OK
359                 emit showGeneratedFile(file.fileName());
360             }
361             else {
362                 uWarning() << "Cannot open file :" << file.fileName() << " for writing!";
363                 codeGenSuccess = false;
364             }
365         }
366 
367         if (cdoc) {
368             emit codeGenerated(cdoc->getParentClassifier(), codeGenSuccess);
369         }
370     }
371 }
372 
373 /**
374  * A single call to writeCodeToFile() usually entails processing many
375  * items (e.g. as classifiers) for which code is generated.
376  * This method is called after all code of one call to writeCodeToFile()
377  * has been generated.
378  * It can be reimplemented by concrete code generators to perform additional
379  * cleanups or other actions that can only be performed once all code has
380  * been written.
381  */
finalizeRun()382 void CodeGenerator::finalizeRun()
383 {
384 }
385 
386 /**
387  *  Gets the heading file (as a string) to be inserted at the
388  *  beginning of the generated file. you give the file type as
389  *  parameter and get the string. if fileName starts with a
390  *  period (.) then fileName is the extension (.cpp, .h,
391  *  .java) if fileName starts with another character you are
392  *  requesting a specific file (mylicensefile.txt).  The files
393  *  can have parameters which are denoted by %parameter%.
394  *
395  *  current parameters are
396  *  %author%
397  *  %date%
398  *  %time%
399  *  %filepath%
400  *
401  * @return      QString
402  * @param       file
403  */
getHeadingFile(const QString & file)404 QString CodeGenerator::getHeadingFile(const QString &file)
405 {
406     return UMLApp::app()->commonPolicy()->getHeadingFile(file);
407 }
408 
409 /**
410  * Returns a name that can be written to in the output directory,
411  * respecting the overwrite policy.
412  * If a file of the given name and extension does not exist,
413  * then just returns the name.
414  * If a file of the given name and extension does exist,
415  * then opens an overwrite dialog. In this case the name returned
416  * may be a modification of the input name.
417  * This method is invoked by findFileName().
418  *
419  * @param name the proposed output file name
420  * @param extension the extension to use
421  * @return the real file name that should be used (including extension) or
422  *      QString() if none to be used
423  */
overwritableName(const QString & name,const QString & extension)424 QString CodeGenerator::overwritableName(const QString& name, const QString &extension)
425 {
426     CodeGenerationPolicy *pol = UMLApp::app()->commonPolicy();
427     QDir outputDirectory = pol->getOutputDirectory();
428     QString filename = name + extension;
429 
430     if (!outputDirectory.exists(filename)) {
431         return filename;
432     }
433 
434     int suffix;
435     QPointer<OverwriteDialog> overwriteDialog =
436         new OverwriteDialog(name, outputDirectory.absolutePath(),
437                               m_applyToAllRemaining, qApp->activeWindow());
438     switch (pol->getOverwritePolicy()) {  //if it exists, check the OverwritePolicy we should use
439     case CodeGenerationPolicy::Ok:              //ok to overwrite file
440         filename = name + extension;
441         break;
442     case CodeGenerationPolicy::Ask:            //ask if we can overwrite
443         switch(overwriteDialog->exec()) {
444 #if QT_VERSION >= 0x050000
445         case OverwriteDialog::Ok:  //overwrite file
446 #else
447         case QDialog::Accepted:
448 #endif
449             if (overwriteDialog->applyToAllRemaining()) {
450                 pol->setOverwritePolicy(CodeGenerationPolicy::Ok);
451                 filename = name + extension;
452             }
453             else {
454                 m_applyToAllRemaining = false;
455             }
456             break;
457 #if QT_VERSION >= 0x050000
458         case OverwriteDialog::No: //generate similar name
459 #else
460         case KDialog::No:
461 #endif
462             suffix = 1;
463             while (1) {
464                 filename = name + QLatin1String("__") + QString::number(suffix) + extension;
465                 if (!outputDirectory.exists(filename))
466                     break;
467                 suffix++;
468             }
469             if (overwriteDialog->applyToAllRemaining()) {
470                 pol->setOverwritePolicy(CodeGenerationPolicy::Never);
471             }
472             else {
473                 m_applyToAllRemaining = false;
474             }
475             break;
476 #if QT_VERSION >= 0x050000
477         case OverwriteDialog::Cancel: //don't output anything
478 #else
479         case QDialog::Rejected:
480 #endif
481             if (overwriteDialog->applyToAllRemaining()) {
482                 pol->setOverwritePolicy(CodeGenerationPolicy::Cancel);
483             }
484             else {
485                 m_applyToAllRemaining = false;
486             }
487             delete overwriteDialog;
488             return QString();
489             break;
490         }
491         break;
492     case CodeGenerationPolicy::Never: //generate similar name
493         suffix = 1;
494         while (1) {
495             filename = name + QLatin1String("__") + QString::number(suffix) + extension;
496             if (!outputDirectory.exists(filename)) {
497                 break;
498             }
499             suffix++;
500         }
501         break;
502     case CodeGenerationPolicy::Cancel: //don't output anything
503         delete overwriteDialog;
504         return QString();
505         break;
506     }
507 
508     delete overwriteDialog;
509     return filename;
510 }
511 
512 /**
513  * Opens a file named "name" for writing in the outputDirectory.
514  * If something goes wrong, it informs the user
515  * if this function returns true, you know you can write to the file.
516  * @param file       file descriptor
517  * @param fileName   the name of the file
518  * @return           success state
519  */
openFile(QFile & file,const QString & fileName)520 bool CodeGenerator::openFile(QFile & file, const QString &fileName)
521 {
522     //open files for writing.
523     if (fileName.isEmpty()) {
524         uWarning() << "cannot find a file name";
525         return false;
526     }
527     else {
528         QDir outputDirectory = UMLApp::app()->commonPolicy()->getOutputDirectory();
529         if (!outputDirectory.exists())
530             outputDirectory.mkpath(outputDirectory.absolutePath());
531 
532         file.setFileName(outputDirectory.absoluteFilePath(fileName));
533         if(!file.open(QIODevice::WriteOnly)) {
534             KMessageBox::sorry(0, i18n("Cannot open file %1 for writing. Please make sure the folder exists and you have permissions to write to it.", file.fileName()), i18n("Cannot Open File"));
535             return false;
536         }
537         return true;
538     }
539 }
540 
541 /**
542  * Replaces spaces with underscores and capitalises as defined in m_modname
543  * @return      QString
544  * @param       name
545  */
cleanName(const QString & name)546 QString CodeGenerator::cleanName(const QString &name)
547 {
548     QString retval = name;
549     retval.replace(QRegExp(QLatin1String("\\W+")), QLatin1String("_"));
550     return retval;
551 }
552 
553 /**
554  * Finds an appropriate file name for the given CodeDocument, taking into
555  * account the Overwrite Policy and asking the user what to do if need be
556  * (if policy == Ask).
557  *
558  * @param codeDocument  the CodeDocument for which an output file name is desired.
559  * @return the file name that should be used. (with extension) or
560  *      NULL if none to be used
561  */
findFileName(CodeDocument * codeDocument)562 QString CodeGenerator::findFileName(CodeDocument * codeDocument)
563 {
564     // Get the path name
565     QString path = codeDocument->getPath();
566 
567     // if path is given add this as a directory to the file name
568     QString name;
569     if (!path.isEmpty()) {
570         path.replace(QRegExp(QLatin1String("::")), QLatin1String("/")); // Simple hack!
571         name = path + QLatin1Char('/') + codeDocument->getFileName();
572         path = QLatin1Char('/') + path;
573     }
574     else {  // determine the "natural" file name
575         name = codeDocument->getFileName();
576     }
577 
578     // Convert all "::" to "/" : Platform-specific path separator
579     name.replace(QRegExp(QLatin1String("::")), QLatin1String("/")); // Simple hack!
580 
581     // if a path name exists check the existence of the path directory
582     if (!path.isEmpty()) {
583         QDir outputDirectory = UMLApp::app()->commonPolicy()->getOutputDirectory();
584         QDir pathDir(outputDirectory.absolutePath() + path);
585 
586         // does our complete output directory exist yet? if not, try to create it
587         if (!pathDir.exists()) {
588             // ugh. dir separator here is UNIX specific..
589             const QStringList dirs = pathDir.absolutePath().split(QLatin1Char('/'));
590             QString currentDir;
591 
592             QStringList::const_iterator end(dirs.end());
593             for (QStringList::const_iterator dir(dirs.begin()); dir != end; ++dir) {
594                 currentDir += QLatin1Char('/') + *dir;
595                 if (! (pathDir.exists(currentDir) || pathDir.mkdir(currentDir))) {
596                     KMessageBox::error(0, i18n("Cannot create the folder:\n") +
597                                        pathDir.absolutePath() + i18n("\nPlease check the access rights"),
598                                        i18n("Cannot Create Folder"));
599                     return QString();
600                 }
601             }
602         }
603     }
604 
605     name = name.simplified();
606     name.replace(QRegExp(QLatin1String(" ")), QLatin1String("_"));
607 
608     return overwritableName(name, codeDocument->getFileExtension());
609 }
610 
611 /**
612  * Finds all classes in the current document to which objects of class c
613  * are in some way related. Possible relations are Associations (generalization,
614  * composition, etc) as well as parameters to methods and return values
615  * this is useful in deciding which classes/files to import/include in code generation
616  * @param c the class for which relations are to be found
617  * @param cList a reference to the list into which return the result
618  */
findObjectsRelated(UMLClassifier * c,UMLPackageList & cList)619 void CodeGenerator::findObjectsRelated(UMLClassifier *c, UMLPackageList &cList)
620 {
621     UMLPackage *temp;
622     UMLAssociationList associations = c->getAssociations();
623 
624     foreach (UMLAssociation *a, associations) {
625         temp = 0;
626         switch (a->getAssocType()) {
627         case Uml::AssociationType::Generalization:
628         case Uml::AssociationType::Realization:
629             // only the "b" end is seen by the "a" end, not other way around
630             {
631                 UMLObject *objB = a->getObject(Uml::RoleType::B);
632                 if (objB != c) {
633                     temp = (UMLPackage*)objB;
634                 }
635             }
636             break;
637         case Uml::AssociationType::Dependency:
638         case Uml::AssociationType::UniAssociation:
639             {
640                 UMLObject *objA = a->getObject(Uml::RoleType::A);
641                 UMLObject *objB = a->getObject(Uml::RoleType::B);
642                 if (objA == c) {
643                     temp = objB->asUMLPackage();
644                 }
645             }
646             break;
647         case Uml::AssociationType::Aggregation:
648         case Uml::AssociationType::Composition:
649         case Uml::AssociationType::Association:
650             {
651                 UMLObject *objA = a->getObject(Uml::RoleType::A);
652                 UMLObject *objB = a->getObject(Uml::RoleType::B);
653                 if (objA == c && !objB->isUMLDatatype()) {
654                     temp = objB->asUMLPackage();
655                 }
656             }
657             break;
658         default: // all others.. like for state diagrams..we currently don't use
659             break;
660         }
661 
662         // now add in list ONLY if it is not already there
663         if (temp  && !cList.count(temp)) {
664             cList.append(temp);
665         }
666     }
667 
668     //operations
669     UMLOperationList opl(c->getOpList());
670     foreach(UMLOperation *op, opl) {
671         temp = 0;
672         //check return value
673         temp = (UMLClassifier*) op->getType();
674         if (temp && !temp->isUMLDatatype() && !cList.count(temp)) {
675             cList.append(temp);
676         }
677         //check parameters
678         UMLAttributeList atl = op->getParmList();
679         foreach(UMLAttribute *at, atl) {
680             temp = (UMLClassifier*)at->getType();
681             if (temp && !temp->isUMLDatatype() && !cList.count(temp)) {
682                 cList.append(temp);
683             }
684         }
685     }
686 
687     //attributes
688     if (!c->isInterface()) {
689         UMLAttributeList atl = c->getAttributeList();
690         foreach (UMLAttribute *at, atl) {
691             temp=0;
692             temp = (UMLClassifier*) at->getType();
693             if (temp && !temp->isUMLDatatype() && !cList.count(temp)) {
694                 cList.append(temp);
695             }
696         }
697     }
698 }
699 
700 /**
701  * Format documentation for output in source files
702  *
703  * @param text         the documentation which has to be formatted
704  * @param linePrefix   the prefix which has to be added in the beginnig of each line
705  * @param lineWidth    the line width used for word-wrapping the documentation
706  *
707  * @return the formatted documentation text
708  */
formatDoc(const QString & text,const QString & linePrefix,int lineWidth)709 QString CodeGenerator::formatDoc(const QString &text, const QString &linePrefix, int lineWidth)
710 {
711     const QString endLine = UMLApp::app()->commonPolicy()->getNewLineEndingChars();
712     QString output;
713     QStringList lines = text.split(endLine);
714     for (QStringList::ConstIterator lit = lines.constBegin(); lit != lines.constEnd(); ++lit) {
715         QString input = *lit;
716         input.remove(QRegExp(QLatin1String("\\s+$")));
717         if (input.length() < lineWidth) {
718             output += linePrefix + input + endLine;
719             continue;
720         }
721         int index;
722         while ((index = input.lastIndexOf(QLatin1String(" "), lineWidth)) >= 0) {
723             output += linePrefix + input.left(index) + endLine; // add line
724             input.remove(0, index + 1); // remove processed string, including white space
725         }
726         if (!input.isEmpty()) {
727             output += linePrefix + input + endLine;
728         }
729     }
730     return output;
731 }
732 
733 /**
734  * Format source code for output in source files by
735  * adding the correct indentation to every line of code.
736  *
737  * @param code          the source code block which has to be formatted
738  * @param indentation   the blanks to indent
739  */
formatSourceCode(const QString & code,const QString & indentation)740 QString CodeGenerator::formatSourceCode(const QString& code, const QString& indentation)
741 {
742     const QString endLine = UMLApp::app()->commonPolicy()->getNewLineEndingChars();
743     QString output;
744     if (! code.isEmpty()) {
745         QStringList lines = code.split(endLine);
746         for (int i = 0; i < lines.size(); ++i) {
747             output += indentation + lines.at(i) + endLine;
748         }
749     }
750     return output;
751 }
752 
753 // these are utility methods for accessing the default
754 // code gen policy object and should go away when we
755 // finally implement the CodeGenDialog class -b.t.
756 
setForceDoc(bool f)757 void CodeGenerator::setForceDoc(bool f)
758 {
759     UMLApp::app()->commonPolicy()->setCodeVerboseDocumentComments(f);
760 }
761 
forceDoc() const762 bool CodeGenerator::forceDoc() const
763 {
764     return UMLApp::app()->commonPolicy()->getCodeVerboseDocumentComments();
765 }
766 
setForceSections(bool f)767 void CodeGenerator::setForceSections(bool f)
768 {
769     UMLApp::app()->commonPolicy()->setCodeVerboseSectionComments(f);
770 }
771 
forceSections() const772 bool CodeGenerator::forceSections() const
773 {
774     return UMLApp::app()->commonPolicy()->getCodeVerboseSectionComments();
775 }
776 
777 /**
778  * Return the default datatypes for your language (bool, int etc).
779  * Default implementation returns empty list.
780  */
defaultDatatypes() const781 QStringList CodeGenerator::defaultDatatypes() const
782 {
783     return QStringList();
784     //empty by default, override in your code generator
785 }
786 
787 /**
788  * Check whether the given string is a reserved word for the
789  * language of this code generator.
790  *
791  * @param keyword   string to check
792  *
793  */
isReservedKeyword(const QString & keyword)794 bool CodeGenerator::isReservedKeyword(const QString & keyword)
795 {
796     const QStringList keywords = reservedKeywords();
797     return keywords.contains(keyword);
798 }
799 
800 /**
801  * Get list of reserved keywords.
802  */
reservedKeywords() const803 QStringList CodeGenerator::reservedKeywords() const
804 {
805     static QStringList emptyList;
806     return emptyList;
807 }
808 
809 /**
810  * Create the default stereotypes for your language (constructor, int etc).
811  */
createDefaultStereotypes()812 void CodeGenerator::createDefaultStereotypes()
813 {
814     //empty by default, override in your code generator
815     //e.g.  m_document->createDefaultStereotypes("constructor");
816 }
817