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