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