1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3
4 SPDX-FileCopyrightText: 2003 Alexander Blum <blum@kewbee.de>
5 SPDX-FileCopyrightText: 2004-2020 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
6 */
7
8 #include "aswriter.h"
9
10 #include "association.h"
11 #include "attribute.h"
12 #include "classifier.h"
13 #include "debug_utils.h"
14 #include "operation.h"
15 #include "umldoc.h"
16
17 #include <QRegExp>
18 #include <QTextStream>
19
20 static const char *reserved_words[] = {
21 "abs",
22 "acos",
23 "add",
24 "addListener",
25 "addProperty",
26 "align",
27 "_alpha",
28 "and",
29 "appendChild",
30 "apply",
31 "Array",
32 "asin",
33 "atan",
34 "atan2",
35 "attachMovie",
36 "attachSound",
37 "attributes",
38 "autoSize",
39 "background",
40 "backgroundColor",
41 "BACKSPACE",
42 "beginFill",
43 "beginGradientFill",
44 "blockIndent",
45 "bold",
46 "Boolean",
47 "border",
48 "borderColor",
49 "bottomScroll",
50 "break",
51 "bullet",
52 "call",
53 "callee",
54 "caller",
55 "capabilities",
56 "CAPSLOCK",
57 "case",
58 "ceil",
59 "charAt",
60 "charCodeAt",
61 "childNodes",
62 "chr",
63 "clear",
64 "clearInterval",
65 "cloneNode",
66 "close",
67 "color",
68 "Color",
69 "comment",
70 "concat",
71 "connect",
72 "contentType",
73 "continue",
74 "CONTROL",
75 "cos",
76 "createElement",
77 "createEmptyMovieClip",
78 "createTextField",
79 "createTextNode",
80 "_currentframe",
81 "curveTo",
82 "Date",
83 "default",
84 "delete",
85 "DELETEKEY",
86 "do",
87 "docTypeDecl",
88 "DOWN",
89 "_droptarget",
90 "duplicateMovieClip",
91 "duration",
92 "E",
93 "else",
94 "embedFonts",
95 "enabled",
96 "END",
97 "endFill",
98 "endinitclip",
99 "ENTER",
100 "eq",
101 "escape",
102 "ESCAPE",
103 "eval",
104 "evaluate",
105 "exp",
106 "false",
107 "firstChild",
108 "floor",
109 "focusEnabled",
110 "_focusrect",
111 "font",
112 "for",
113 "_framesloaded",
114 "fromCharCode",
115 "fscommand",
116 "function",
117 "ge",
118 "get",
119 "getAscii",
120 "getBeginIndex",
121 "getBounds",
122 "getBytesLoaded",
123 "getBytesTotal",
124 "getCaretIndex",
125 "getCode",
126 "getDate",
127 "getDay",
128 "getDepth",
129 "getEndIndex",
130 "getFocus",
131 "getFontList",
132 "getFullYear",
133 "getHours",
134 "getMilliseconds",
135 "getMinutes",
136 "getMonth",
137 "getNewTextFormat",
138 "getPan",
139 "getProperty",
140 "getRGB",
141 "getSeconds",
142 "getTextExtent",
143 "getTextFormat",
144 "getTime",
145 "getTimer",
146 "getTimezoneOffset",
147 "getTransform",
148 "getURL",
149 "getUTCDate",
150 "getUTCDay",
151 "getUTCFullYear",
152 "getUTCHours",
153 "getUTCMilliseconds",
154 "getUTCMinutes",
155 "getUTCMonth",
156 "getUTCSeconds",
157 "getVersion",
158 "getVolume",
159 "getYear",
160 "_global",
161 "globalToLocal",
162 "goto",
163 "gotoAndPlay",
164 "gotoAndStop",
165 "gt",
166 "hasAccessibility",
167 "hasAudio",
168 "hasAudioEncoder",
169 "hasChildNodes",
170 "hasMP3",
171 "hasVideoEncoder",
172 "height",
173 "_height",
174 "hide",
175 "_highquality",
176 "hitArea",
177 "hitTest",
178 "HOME",
179 "hscroll",
180 "html",
181 "htmlText",
182 "if",
183 "ifFrameLoaded",
184 "ignoreWhite",
185 "in",
186 "include",
187 "indent",
188 "indexOf",
189 "initclip",
190 "INSERT",
191 "insertBefore",
192 "install",
193 "instanceof",
194 "int",
195 "isActive",
196 "isDown",
197 "isFinite",
198 "isNaN",
199 "isToggled",
200 "italic",
201 "join",
202 "lastChild",
203 "lastIndexOf",
204 "le",
205 "leading",
206 "LEFT",
207 "leftMargin",
208 "length",
209 "_level",
210 "lineStyle",
211 "lineTo",
212 "list",
213 "LN10",
214 "LN2",
215 "load",
216 "loaded",
217 "loadMovie",
218 "loadMovieNum",
219 "loadSound",
220 "loadVariables",
221 "loadVariablesNum",
222 "LoadVars",
223 "localToGlobal",
224 "log",
225 "LOG10E",
226 "LOG2E",
227 "max",
228 "maxChars",
229 "maxhscroll",
230 "maxscroll",
231 "MAX_VALUE",
232 "mbchr",
233 "mblength",
234 "mbord",
235 "mbsubstring",
236 "method",
237 "min",
238 "MIN_VALUE",
239 "moveTo",
240 "multiline",
241 "_name",
242 "NaN",
243 "ne",
244 "NEGATIVE_INFINITY",
245 "new",
246 "newline",
247 "nextFrame",
248 "nextScene",
249 "nextSibling",
250 "nodeName",
251 "nodeType",
252 "nodeValue",
253 "not",
254 "null",
255 "Number",
256 "Object",
257 "on",
258 "onChanged",
259 "onClipEvent",
260 "onClose",
261 "onConnect",
262 "onData",
263 "onDragOut",
264 "onDragOver",
265 "onEnterFrame",
266 "onKeyDown",
267 "onKeyUp",
268 "onKillFocus",
269 "onLoad",
270 "onMouseDown",
271 "onMouseMove",
272 "onMouseUp",
273 "onPress",
274 "onRelease",
275 "onReleaseOutside",
276 "onResize",
277 "onRollOut",
278 "onRollOver",
279 "onScroller",
280 "onSetFocus",
281 "onSoundComplete",
282 "onUnload",
283 "onUpdate",
284 "onXML",
285 "or",
286 "ord",
287 "_parent",
288 "parentNode",
289 "parseFloat",
290 "parseInt",
291 "parseXML",
292 "password",
293 "PGDN",
294 "PGUP",
295 "PI",
296 "pixelAspectRatio",
297 "play",
298 "pop",
299 "position",
300 "POSITIVE_INFINITY",
301 "pow",
302 "prevFrame",
303 "previousSibling",
304 "prevScene",
305 "print",
306 "printAsBitmap",
307 "printAsBitmapNum",
308 "printNum",
309 "__proto__",
310 "prototype",
311 "push",
312 "_quality",
313 "random",
314 "registerClass",
315 "removeListener",
316 "removeMovieClip",
317 "removeNode",
318 "removeTextField",
319 "replaceSel",
320 "restrict",
321 "return",
322 "reverse",
323 "RIGHT",
324 "rightMargin",
325 "_root",
326 "_rotation",
327 "round",
328 "scaleMode",
329 "screenColor",
330 "screenDPI",
331 "screenResolutionX",
332 "screenResolutionY",
333 "scroll",
334 "selectable",
335 "send",
336 "sendAndLoad",
337 "set",
338 "setDate",
339 "setFocus",
340 "setFullYear",
341 "setHours",
342 "setInterval",
343 "setMask",
344 "setMilliseconds",
345 "setMinutes",
346 "setMonth",
347 "setNewTextFormat",
348 "setPan",
349 "setProperty",
350 "setRGB",
351 "setSeconds",
352 "setSelection",
353 "setTextFormat",
354 "setTime",
355 "setTransform",
356 "setUTCDate",
357 "setUTCFullYear",
358 "setUTCHours",
359 "setUTCMilliseconds",
360 "setUTCMinutes",
361 "setUTCMonth",
362 "setUTCSeconds",
363 "setVolume",
364 "setYear",
365 "shift",
366 "SHIFT",
367 "show",
368 "showMenu",
369 "sin",
370 "size",
371 "slice",
372 "sort",
373 "sortOn",
374 "Sound",
375 "_soundbuftime",
376 "SPACE",
377 "splice",
378 "split",
379 "sqrt",
380 "SQRT1_2",
381 "SQRT2",
382 "start",
383 "startDrag",
384 "status",
385 "stop",
386 "stopAllSounds",
387 "stopDrag",
388 "String",
389 "substr",
390 "substring",
391 "super",
392 "swapDepths",
393 "switch",
394 "TAB",
395 "tabChildren",
396 "tabEnabled",
397 "tabIndex",
398 "tabStops",
399 "tan",
400 "target",
401 "_target",
402 "targetPath",
403 "tellTarget",
404 "text",
405 "textColor",
406 "TextFormat",
407 "textHeight",
408 "textWidth",
409 "this",
410 "toggleHighQuality",
411 "toLowerCase",
412 "toString",
413 "_totalframes",
414 "toUpperCase",
415 "trace",
416 "trackAsMenu",
417 "true",
418 "type",
419 "typeof",
420 "undefined",
421 "underline",
422 "unescape",
423 "uninstall",
424 "unloadMovie",
425 "unloadMovieNum",
426 "unshift",
427 "unwatch",
428 "UP",
429 "updateAfterEvent",
430 "url",
431 "_url",
432 "useHandCursor",
433 "UTC",
434 "valueOf",
435 "var",
436 "variable",
437 "_visible",
438 "void",
439 "watch",
440 "while",
441 "width",
442 "_width",
443 "with",
444 "wordWrap",
445 "_x",
446 "XML",
447 "xmlDecl",
448 "XMLSocket",
449 "_xmouse",
450 "_xscale",
451 "_y",
452 "_ymouse",
453 0
454 };
455
456 /**
457 * Constructor.
458 */
ASWriter()459 ASWriter::ASWriter()
460 {
461 }
462
463 /**
464 * Destructor.
465 */
~ASWriter()466 ASWriter::~ASWriter()
467 {
468 }
469
470 /**
471 * Call this method to generate Actionscript code for a UMLClassifier.
472 * @param c the class you want to generate code for
473 */
writeClass(UMLClassifier * c)474 void ASWriter::writeClass(UMLClassifier *c)
475 {
476 if (!c) {
477 uDebug()<<"Cannot write class of NULL concept!";
478 return;
479 }
480
481 QString classname = cleanName(c->name());
482 QString fileName = c->name().toLower();
483
484 //find an appropriate name for our file
485 fileName = findFileName(c, QLatin1String(".as"));
486 if (fileName.isEmpty())
487 {
488 emit codeGenerated(c, false);
489 return;
490 }
491
492 QFile fileas;
493 if (!openFile(fileas, fileName))
494 {
495 emit codeGenerated(c, false);
496 return;
497 }
498 QTextStream as(&fileas);
499
500 //////////////////////////////
501 //Start generating the code!!
502 /////////////////////////////
503
504 //try to find a heading file (license, comments, etc)
505 QString str;
506 str = getHeadingFile(QLatin1String(".as"));
507 if (!str.isEmpty())
508 {
509 str.replace(QRegExp(QLatin1String("%filename%")), fileName + QLatin1String(".as"));
510 str.replace(QRegExp(QLatin1String("%filepath%")), fileas.fileName());
511 as << str << m_endl;
512 }
513
514 //write includes
515 UMLPackageList includes;
516 findObjectsRelated(c, includes);
517 foreach (UMLPackage* conc, includes) {
518 QString headerName = findFileName(conc, QLatin1String(".as"));
519 if (!headerName.isEmpty())
520 {
521 as << "#include \"" << findFileName(conc, QLatin1String(".as")) << "\"" << m_endl;
522 }
523 }
524 as << m_endl;
525
526 //Write class Documentation if there is something or if force option
527 if (forceDoc() || !c->doc().isEmpty())
528 {
529 as << m_endl << "/**" << m_endl;
530 as << " * class " << classname << m_endl;
531 as << formatDoc(c->doc(), QLatin1String(" * "));
532 as << " */" << m_endl << m_endl;
533 }
534
535 UMLClassifierList superclasses = c->getSuperClasses();
536 UMLAssociationList aggregations = c->getAggregations();
537 UMLAssociationList compositions = c->getCompositions();
538
539 //check if class is abstract and / or has abstract methods
540 if (c->isAbstract() && !hasAbstractOps(c))
541 as << "/******************************* Abstract Class ****************************" << m_endl << " "
542 << classname << " does not have any pure virtual methods, but its author" << m_endl
543 << " defined it as an abstract class, so you should not use it directly." << m_endl
544 << " Inherit from it instead and create only objects from the derived classes" << m_endl
545 << "*****************************************************************************/" << m_endl << m_endl;
546
547 as << classname << " = function ()" << m_endl;
548 as << "{" << m_endl;
549 as << m_indentation << "this._init ();" << m_endl;
550 as << "}" << m_endl;
551 as << m_endl;
552
553 foreach(UMLClassifier* obj, superclasses) {
554 as << classname << ".prototype = new " << cleanName(obj->name()) << " ();" << m_endl;
555 }
556
557 as << m_endl;
558
559 const bool isClass = !c->isInterface();
560 if (isClass) {
561
562 UMLAttributeList atl = c->getAttributeList();
563
564 as << "/**" << m_endl;
565 QString temp = QLatin1String("_init sets all ") + classname +
566 QLatin1String(" attributes to their default values. ") +
567 QLatin1String("Make sure to call this method within your class constructor");
568 as << formatDoc(temp, QLatin1String(" * "));
569 as << " */" << m_endl;
570 as << classname << ".prototype._init = function ()" << m_endl;
571 as << "{" << m_endl;
572 foreach (UMLAttribute* at, atl) {
573 if (forceDoc() || !at->doc().isEmpty())
574 {
575 as << m_indentation << "/**" << m_endl
576 << formatDoc(at->doc(), m_indentation + QLatin1String(" * "))
577 << m_indentation << " */" << m_endl;
578 }
579 if (!at->getInitialValue().isEmpty())
580 {
581 as << m_indentation << "this.m_" << cleanName(at->name()) << " = " << at->getInitialValue() << ";" << m_endl;
582 }
583 else
584 {
585 as << m_indentation << "this.m_" << cleanName(at->name()) << " = \"\";" << m_endl;
586 }
587 }
588 }
589
590 //associations
591 if (forceSections() || !aggregations.isEmpty())
592 {
593 as << m_endl << m_indentation << "/**Aggregations: */" << m_endl;
594 writeAssociation(classname, aggregations, as);
595 }
596
597 if (forceSections() || !compositions.isEmpty())
598 {
599 as << m_endl << m_indentation << "/**Compositions: */" << m_endl;
600 writeAssociation(classname, compositions, as);
601 }
602
603 as << m_endl;
604 as << m_indentation << "/**Protected: */" << m_endl;
605
606 if (isClass) {
607 UMLAttributeList atl = c->getAttributeList();
608 foreach (UMLAttribute* at, atl) {
609 if (at->visibility() == Uml::Visibility::Protected) {
610 as << m_indentation << "ASSetPropFlags (this, \"" << cleanName(at->name()) << "\", 1);" << m_endl;
611 }
612 }
613 }
614
615 UMLOperationList opList(c->getOpList());
616 foreach (UMLOperation* op, opList) {
617 if (op->visibility() == Uml::Visibility::Protected) {
618 as << m_indentation << "ASSetPropFlags (this, \"" << cleanName(op->name()) << "\", 1);" << m_endl;
619 }
620 }
621 as << m_endl;
622 as << m_indentation << "/**Private: */" << m_endl;
623 if (isClass) {
624 UMLAttributeList atl = c->getAttributeList();
625 foreach (UMLAttribute* at, atl) {
626 if (at->visibility() == Uml::Visibility::Private) {
627 as << m_indentation << "ASSetPropFlags (this, \"" << cleanName(at->name()) << "\", 7);" << m_endl;
628 }
629 }
630 }
631
632 foreach (UMLOperation* op, opList) {
633 if (op->visibility() == Uml::Visibility::Protected) {
634 as << m_indentation << "ASSetPropFlags (this, \"" << cleanName(op->name()) << "\", 7);" << m_endl;
635 }
636 }
637 as << "}" << m_endl;
638
639 as << m_endl;
640
641 //operations
642 UMLOperationList ops(c->getOpList());
643 writeOperations(classname, &ops, as);
644
645 as << m_endl;
646
647 //finish file
648
649 //close files and notfiy we are done
650 fileas.close();
651 emit codeGenerated(c, true);
652 emit showGeneratedFile(fileas.fileName());
653 }
654
655 ////////////////////////////////////////////////////////////////////////////////////
656 // Helper Methods
657
658 /**
659 * Write a list of associations.
660 *
661 * @param classname the name of the class
662 * @param assocList the list of associations
663 * @param as output stream for the AS file
664 */
writeAssociation(QString & classname,UMLAssociationList & assocList,QTextStream & as)665 void ASWriter::writeAssociation(QString& classname, UMLAssociationList& assocList, QTextStream &as)
666 {
667 foreach (UMLAssociation *a, assocList)
668 {
669 // association side
670 Uml::RoleType::Enum role = a->getObject(Uml::RoleType::A)->name() == classname ? Uml::RoleType::B : Uml::RoleType::A;
671
672 QString roleName(cleanName(a->getRoleName(role)));
673
674 if (!roleName.isEmpty()) {
675
676 // association doc
677 if (forceDoc() || !a->doc().isEmpty()) {
678 as << m_indentation << "/**" << m_endl
679 << formatDoc(a->doc(), m_indentation + QLatin1String(" * "))
680 << m_indentation << " */" << m_endl;
681 }
682
683 // role doc
684 if (forceDoc() || !a->getRoleDoc(role).isEmpty()) {
685 as << m_indentation << "/**" << m_endl
686 << formatDoc(a->getRoleDoc(role), m_indentation + QLatin1String(" * "))
687 << m_indentation << " */" << m_endl;
688 }
689
690 bool okCvt;
691 int nMulti = a->getMultiplicity(role).toInt(&okCvt, 10);
692 bool isNotMulti = a->getMultiplicity(role).isEmpty() || (okCvt && nMulti == 1);
693
694 QString typeName(cleanName(a->getObject(role)->name()));
695
696 if (isNotMulti)
697 as << m_indentation << "this.m_" << roleName << " = new " << typeName << "();" << m_endl;
698 else
699 as << m_indentation << "this.m_" << roleName << " = new Array();" << m_endl;
700
701 // role visibility
702 if (a->visibility(role) == Uml::Visibility::Private)
703 {
704 as << m_indentation << "ASSetPropFlags (this, \"m_" << roleName << "\", 7);" << m_endl;
705 }
706 else if (a->visibility(role)== Uml::Visibility::Protected)
707 {
708 as << m_indentation << "ASSetPropFlags (this, \"m_" << roleName << "\", 1);" << m_endl;
709 }
710 }
711 }
712 }
713
714 /**
715 * Write a list of class operations.
716 *
717 * @param classname the name of the class
718 * @param opList the list of operations
719 * @param as output stream for the AS file
720 */
writeOperations(QString classname,UMLOperationList * opList,QTextStream & as)721 void ASWriter::writeOperations(QString classname, UMLOperationList *opList, QTextStream &as)
722 {
723 UMLAttributeList atl;
724
725 foreach (UMLOperation* op, *opList) {
726 atl = op -> getParmList();
727 //write method doc if we have doc || if at least one of the params has doc
728 bool writeDoc = forceDoc() || !op->doc().isEmpty();
729 foreach (UMLAttribute* at, atl) {
730 writeDoc |= !at->doc().isEmpty();
731 }
732
733 if (writeDoc) //write method documentation
734 {
735 as << "/**" << m_endl << formatDoc(op->doc(), QLatin1String(" * "));
736
737 foreach (UMLAttribute* at, atl) {
738 if (forceDoc() || !at->doc().isEmpty()) {
739 as << " * @param " << cleanName(at->name()) << m_endl;
740 as << formatDoc(at->doc(), QLatin1String(" * "));
741 }
742 }//end for : write parameter documentation
743 as << " */" << m_endl;
744 }//end if : write method documentation
745
746 as << classname << ".prototype." << cleanName(op->name()) << " = function " << "(";
747
748 int i= atl.count();
749 int j=0;
750 for (UMLAttributeListIt atlIt(atl); atlIt.hasNext(); ++j) {
751 UMLAttribute* at = atlIt.next();
752 as << cleanName(at->name())
753 << (!(at->getInitialValue().isEmpty()) ? (QLatin1String(" = ") + at->getInitialValue()) : QString())
754 << ((j < i-1) ? QLatin1String(", ") : QString());
755 }
756 as << ")" << m_endl << "{" << m_endl;
757 QString sourceCode = op->getSourceCode();
758 if (sourceCode.isEmpty()) {
759 as << m_indentation << m_endl;
760 }
761 else {
762 as << formatSourceCode(sourceCode, m_indentation);
763 }
764 as << "}" << m_endl;
765 as << m_endl << m_endl;
766 }//end for
767 }
768
769 /**
770 * Returns "ActionScript".
771 * @return the programming language identifier
772 */
language() const773 Uml::ProgrammingLanguage::Enum ASWriter::language() const
774 {
775 return Uml::ProgrammingLanguage::ActionScript;
776 }
777
778 /**
779 * Get list of reserved keywords.
780 * @return the list of reserved keywords
781 */
reservedKeywords() const782 QStringList ASWriter::reservedKeywords() const
783 {
784 static QStringList keywords;
785
786 if (keywords.isEmpty()) {
787 for (int i = 0; reserved_words[i]; ++i) {
788 keywords.append(QLatin1String(reserved_words[i]));
789 }
790 }
791
792 return keywords;
793 }
794
795