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-2020 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
6 */
7
8 // own header
9 #include "simplecodegenerator.h"
10
11 // app includes
12 #include "overwritedialog.h"
13 #include "model_utils.h"
14 #include "attribute.h"
15 #include "umloperationlist.h"
16 #include "umlattributelist.h"
17 #include "classifier.h"
18 #include "codedocument.h"
19 #include "codegenerationpolicy.h"
20 #include "operation.h"
21 #include "umldoc.h"
22 #include "uml.h"
23
24 // kde includes
25 #if QT_VERSION < 0x050000
26 #include <kdialog.h>
27 #endif
28 #include <KLocalizedString>
29 #include <KMessageBox>
30
31 // qt includes
32 #include <QApplication>
33 #include <QDateTime>
34 #include <QDir>
35 #include <QPointer>
36 #include <QRegExp>
37
38 // system includes
39 #include <cstdlib> //to get the user name
40
41 /**
42 * Constructor.
43 */
SimpleCodeGenerator(bool createDirHierarchyForPackages)44 SimpleCodeGenerator::SimpleCodeGenerator(bool createDirHierarchyForPackages)
45 : CodeGenerator(),
46 m_createDirHierarchyForPackages(createDirHierarchyForPackages),
47 m_indentLevel(0)
48 {
49 m_document->disconnect(this); // disconnect from UMLDoc.. we arent planning to be synced at all
50
51 // load Classifier documents from parent document
52 // initFromParentDocument();
53
54 m_fileMap.clear();
55
56 // this really is just being used to sync the internal params
57 // to the codegenpolicy as there are no code documents to really sync.
58 syncCodeToDocument();
59 }
60
61 /**
62 * Destructor.
63 */
~SimpleCodeGenerator()64 SimpleCodeGenerator::~SimpleCodeGenerator()
65 {
66 }
67
68 /**
69 * Returns the current indent string based on m_indentLevel and m_indentation.
70 * @return indentation string
71 */
indent()72 QString SimpleCodeGenerator::indent()
73 {
74 QString myIndent;
75 for (int i = 0 ; i < m_indentLevel; ++i) {
76 myIndent.append(m_indentation);
77 }
78 return myIndent;
79 }
80
81 /**
82 * Determine the file name.
83 * @param concept the package
84 * @param ext the file extension
85 * @return the valid file name
86 */
findFileName(UMLPackage * concept,const QString & ext)87 QString SimpleCodeGenerator::findFileName(UMLPackage* concept, const QString &ext)
88 {
89 //if we already know to which file this class was written/should be written, just return it.
90 if (m_fileMap.contains(concept))
91 return m_fileMap[concept];
92
93 //else, determine the "natural" file name
94 QString name;
95 // Get the package name
96 QString package = concept->package(QLatin1String("."));
97
98 // Replace all white spaces with blanks
99 package = package.simplified();
100
101 // Replace all blanks with underscore
102 package.replace(QRegExp(QLatin1String(" ")), QLatin1String("_"));
103
104 // Convert all "::" to "/" : Platform-specific path separator
105 // package.replace(QRegExp(QLatin1String("::")), QLatin1String("/"));
106
107 // if package is given add this as a directory to the file name
108 if (!package.isEmpty() && m_createDirHierarchyForPackages) {
109 name = package + QLatin1Char('.') + concept->name();
110 name.replace(QRegExp(QLatin1String("\\.")), QLatin1String("/"));
111 package.replace(QRegExp(QLatin1String("\\.")), QLatin1String("/"));
112 package = QLatin1Char('/') + package;
113 } else {
114 name = concept->fullyQualifiedName(QLatin1String("-"));
115 }
116
117 if (! UMLApp::app()->activeLanguageIsCaseSensitive()) {
118 package = package.toLower();
119 name = name.toLower();
120 }
121
122 // if a package name exists check the existence of the package directory
123 if (!package.isEmpty() && m_createDirHierarchyForPackages) {
124 QDir pathDir(UMLApp::app()->commonPolicy()->getOutputDirectory().absolutePath() + package);
125 // does our complete output directory exist yet? if not, try to create it
126 if (!pathDir.exists())
127 {
128 const QStringList dirs = pathDir.absolutePath().split(QLatin1Char('/'));
129 QString currentDir;
130
131 QStringList::const_iterator end(dirs.end());
132 for (QStringList::const_iterator dir(dirs.begin()); dir != end; ++dir)
133 {
134 currentDir += QLatin1Char('/') + *dir;
135 if (! (pathDir.exists(currentDir)
136 || pathDir.mkdir(currentDir)))
137 {
138 KMessageBox::error(0, i18n("Cannot create the folder:\n") +
139 pathDir.absolutePath() + i18n("\nPlease check the access rights"),
140 i18n("Cannot Create Folder"));
141 return QString();
142 }
143 }
144 }
145 }
146
147 name = name.simplified();
148 name.replace(QRegExp(QLatin1String(" ")), QLatin1String("_"));
149
150 QString extension = ext.simplified();
151 extension.replace(QLatin1Char(' '), QLatin1Char('_'));
152
153 return overwritableName(concept, name, extension);
154 }
155
156 /**
157 * Check if a file named "name" with extension "ext" already exists.
158 * @param concept the package
159 * @param name the name of the file
160 * @param ext the extension of the file
161 * @return the valid filename or null
162 */
overwritableName(UMLPackage * concept,const QString & name,const QString & ext)163 QString SimpleCodeGenerator::overwritableName(UMLPackage* concept, const QString &name, const QString &ext)
164 {
165 CodeGenerationPolicy *commonPolicy = UMLApp::app()->commonPolicy();
166 QDir outputDir = commonPolicy->getOutputDirectory();
167 QString filename = name + ext;
168 if (!outputDir.exists(filename)) {
169 m_fileMap.insert(concept, filename);
170 return filename; //if not, "name" is OK and we have not much to to
171 }
172
173 int suffix;
174 QPointer<OverwriteDialog> overwriteDialog =
175 new OverwriteDialog(filename, outputDir.absolutePath(),
176 m_applyToAllRemaining, qApp->activeWindow());
177 switch(commonPolicy->getOverwritePolicy()) { //if it exists, check the OverwritePolicy we should use
178 case CodeGenerationPolicy::Ok: //ok to overwrite file
179 break;
180 case CodeGenerationPolicy::Ask: //ask if we can overwrite
181 switch(overwriteDialog->exec()) {
182 #if QT_VERSION >= 0x050000
183 case OverwriteDialog::Ok: //overwrite file
184 #else
185 case QDialog::Accepted:
186 #endif
187 if (overwriteDialog->applyToAllRemaining()) {
188 commonPolicy->setOverwritePolicy(CodeGenerationPolicy::Ok);
189 } else {
190 m_applyToAllRemaining = false;
191 }
192 break;
193 #if QT_VERSION >= 0x050000
194 case OverwriteDialog::No: //generate similar name
195 #else
196 case KDialog::No:
197 #endif
198 suffix = 1;
199 while (1) {
200 filename = name + QLatin1String("__") + QString::number(suffix) + ext;
201 if (!outputDir.exists(filename))
202 break;
203 suffix++;
204 }
205 if (overwriteDialog->applyToAllRemaining()) {
206 commonPolicy->setOverwritePolicy(CodeGenerationPolicy::Never);
207 } else {
208 m_applyToAllRemaining = false;
209 }
210 break;
211 #if QT_VERSION >= 0x050000
212 case OverwriteDialog::Cancel: //don't output anything
213 #else
214 case QDialog::Rejected:
215 #endif
216 if (overwriteDialog->applyToAllRemaining()) {
217 commonPolicy->setOverwritePolicy(CodeGenerationPolicy::Cancel);
218 } else {
219 m_applyToAllRemaining = false;
220 }
221 delete overwriteDialog;
222 return QString();
223 break;
224 }
225 break;
226 case CodeGenerationPolicy::Never: //generate similar name
227 suffix = 1;
228 while (1) {
229 filename = name + QLatin1String("__") + QString::number(suffix) + ext;
230 if (!outputDir.exists(filename))
231 break;
232 suffix++;
233 }
234 break;
235 case CodeGenerationPolicy::Cancel: //don't output anything
236 delete overwriteDialog;
237 return QString();
238 break;
239 }
240
241 m_fileMap.insert(concept, filename);
242 delete overwriteDialog;
243 return filename;
244 }
245
246 /**
247 * Check whether classifier has default values for attributes.
248 * @param c the classifier to check
249 * @return true when classifier attributes has default values
250 */
hasDefaultValueAttr(UMLClassifier * c)251 bool SimpleCodeGenerator::hasDefaultValueAttr(UMLClassifier *c)
252 {
253 UMLAttributeList atl = c->getAttributeList();
254 foreach (UMLAttribute* at, atl) {
255 if (!at->getInitialValue().isEmpty())
256 return true;
257 }
258 return false;
259 }
260
261 /**
262 * Check whether classifier has abstract operations.
263 * @param c the classifier to check
264 * @return true when classifier has abstract operations
265 */
hasAbstractOps(UMLClassifier * c)266 bool SimpleCodeGenerator::hasAbstractOps(UMLClassifier *c)
267 {
268 UMLOperationList opl(c->getOpList());
269 foreach (UMLOperation* op, opl) {
270 if (op->isAbstract())
271 return true;
272 }
273 return false;
274 }
275
276 /**
277 * Write all concepts in project to file.
278 */
writeCodeToFile()279 void SimpleCodeGenerator::writeCodeToFile()
280 {
281 UMLClassifierList concepts = m_document->classesAndInterfaces();
282 writeCodeToFile(concepts);
283 }
284
285 /**
286 * Write only selected concepts to file.
287 * @param concepts the selected concepts
288 */
writeCodeToFile(UMLClassifierList & concepts)289 void SimpleCodeGenerator::writeCodeToFile(UMLClassifierList & concepts)
290 {
291 m_fileMap.clear(); // ??
292 foreach (UMLClassifier* c, concepts) {
293 if (! Model_Utils::isCommonDataType(c->name()))
294 this->writeClass(c); // call the writer for each class.
295 }
296 finalizeRun();
297 }
298
299 /**
300 * A little method to provide some compatibility between
301 * the newer codegenpolicy object and the older class fields.
302 */
syncCodeToDocument()303 void SimpleCodeGenerator::syncCodeToDocument()
304 {
305 CodeGenerationPolicy *policy = UMLApp::app()->commonPolicy();
306
307 m_indentation = policy->getIndentation();
308 m_endl = policy->getNewLineEndingChars();
309 }
310
311 /**
312 * Override parent method.
313 */
initFromParentDocument()314 void SimpleCodeGenerator::initFromParentDocument()
315 {
316 // do nothing
317 }
318
319