umbrello API Documentation

perlwriter.cpp

00001 /***************************************************************************
00002     begin                : Wed Jan 22 2003
00003     copyright            : (C) 2003 by David Hugh-Jones
00004       (C) 2004-2006  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>
00005     email                : hughjonesd@yahoo.co.uk
00006  ***************************************************************************/
00007 
00008 /***************************************************************************
00009  *                                                                         *
00010  *   This program is free software; you can redistribute it and/or modify  *
00011  *   it under the terms of the GNU General Public License as published by  *
00012  *   the Free Software Foundation; either version 2 of the License, or     *
00013  *   (at your option) any later version.                                   *
00014  *                                                                         *
00015  ***************************************************************************/
00016 
00017 #include "perlwriter.h"
00018 #include "../classifier.h"
00019 #include "../operation.h"
00020 #include "../umldoc.h"
00021 #include "../association.h"
00022 #include "../attribute.h"
00023 #include "../uml.h"
00024 
00025 #include <kdebug.h>
00026 #include <qregexp.h>
00027 #include <qstring.h>
00028 #include <qdir.h>
00029 #include <qdatetime.h>
00030 #include <qtextstream.h>
00031 
00032 PerlWriter::PerlWriter()
00033 {
00034 }
00035 
00036 PerlWriter::~PerlWriter() {}
00037 
00038 bool PerlWriter::GetUseStatements(UMLClassifier *c, QString &Ret,
00039                                   QString &ThisPkgName){
00040 
00041   if(!c){
00042     return(false);
00043   }
00044 
00045   UMLPackageList includes;
00046   findObjectsRelated(c,includes);
00047   UMLPackage *conc;
00048   QString AV = "@";
00049   QString SV = "$";
00050   QString HV = "%";
00051   for(conc = includes.first(); conc ;conc = includes.next()) {
00052     if (conc->getBaseType() == Uml::ot_Datatype)
00053         continue;
00054     QString neatName = cleanName(conc->getName());
00055     if (neatName != AV && neatName != SV && neatName != HV) {
00056       QString OtherPkgName =  conc->getPackage(".");
00057       OtherPkgName.replace(QRegExp("\\."),"::");
00058       QString OtherName = OtherPkgName + "::" + cleanName(conc->getName());
00059 
00060       // Only print out the use statement if the other package isn't the
00061       // same as the one we are working on. (This happens for the
00062       // "Singleton" design pattern.)
00063       if(OtherName != ThisPkgName){
00064         Ret += "use ";
00065         Ret += OtherName;
00066         Ret +=  ';';
00067         Ret += m_endl;
00068       }
00069     }
00070   }
00071   UMLClassifierList  superclasses = c->getSuperClasses();
00072   if (superclasses.count()) {
00073     Ret += m_endl;
00074     Ret += "use base qw( ";
00075     for (UMLClassifier *obj = superclasses.first();
00076          obj; obj = superclasses.next()) {
00077       QString packageName =  obj->getPackage(".");
00078       packageName.replace(QRegExp("\\."),"::");
00079 
00080       Ret += packageName + "::" + cleanName(obj->getName()) + ' ';
00081     }
00082     Ret += ");" + m_endl;
00083   }
00084 
00085   return(true);
00086 }
00087 
00088 void PerlWriter::writeClass(UMLClassifier *c) {
00089 
00090   /*  if(!c) {
00091       kDebug()<<"Cannot write class of NULL concept!" << endl;
00092       return;
00093       }
00094   */
00095   QString classname = cleanName(c->getName());// this is fine: cleanName is "::-clean"
00096   QString packageName =  c->getPackage(".");
00097   QString fileName;
00098 
00099   // Replace all white spaces with blanks
00100   packageName.simplifyWhiteSpace();
00101 
00102   // Replace all blanks with underscore
00103   packageName.replace(QRegExp(" "), "_");
00104 
00105   // Replace all dots (".") with double colon scope resolution operators
00106   // ("::")
00107   packageName.replace(QRegExp("\\."),"::");
00108 
00109   // Store complete package name
00110   QString ThisPkgName = packageName + "::" + classname;
00111 
00112   fileName = findFileName(c, ".pm");
00113   // the above lower-cases my nice class names. That is bad.
00114   // correct solution: refactor,
00115   // split massive findFileName up, reimplement
00116   // parts here
00117   // actual solution: shameful ".pm" hack in codegenerator
00118 
00119   CodeGenerationPolicy *pol = UMLApp::app()->getCommonPolicy();
00120   QString curDir = pol->getOutputDirectory().absPath();
00121   if (fileName.contains("::")) {
00122     // create new directories for each level
00123     QString newDir;
00124     newDir = curDir;
00125     QString fragment = fileName;
00126     QDir* existing = new QDir (curDir);
00127     QRegExp regEx("(.*)(::)");
00128     regEx.setMinimal(true);
00129     while (regEx.search(fragment) > -1) {
00130       newDir = regEx.cap(1);
00131       fragment.remove(0, (regEx.pos(2) + 2)); // get round strange minimal matching bug
00132       existing->setPath(curDir + '/' + newDir);
00133       if (! existing->exists()) {
00134         existing->setPath(curDir);
00135         if (! existing->mkdir(newDir)) {
00136           emit codeGenerated(c, false);
00137           return;
00138         }
00139       }
00140       curDir += '/' + newDir;
00141     }
00142     fileName = fragment + ".pm";
00143   }
00144   if (fileName.isEmpty()) {
00145     emit codeGenerated(c, false);
00146     return;
00147   }
00148   QString oldDir = pol->getOutputDirectory().absPath();
00149   pol->setOutputDirectory(curDir);
00150   QFile fileperl;
00151   if(!openFile(fileperl, fileName)) {
00152     emit codeGenerated(c, false);
00153     return;
00154   }
00155   QTextStream perl(&fileperl);
00156   pol->setOutputDirectory(oldDir);
00157 
00158   //======================================================================
00159   // Start generating the code!!
00160   //======================================================================
00161 
00162   // try to find a heading file (license, comments, etc)
00163   QString str;
00164   bool bPackageDeclared = false;
00165   bool bUseStmsWritten  = false;
00166 
00167   str = getHeadingFile(".pm");   // what this mean?
00168   if(!str.isEmpty()) {
00169     str.replace(QRegExp("%filename%"),fileName);
00170     str.replace(QRegExp("%filepath%"),fileperl.name());
00171     str.replace(QRegExp("%year%"),QDate::currentDate().toString("yyyy"));
00172     str.replace(QRegExp("%date%"),QDate::currentDate().toString());
00173     str.replace(QRegExp("%time%"),QTime::currentTime().toString());
00174     str.replace(QRegExp("%package-name%"),ThisPkgName);
00175     if(str.find(QRegExp("%PACKAGE-DECLARE%"))){
00176       str.replace(QRegExp("%PACKAGE-DECLARE%"),
00177                   "package " + ThisPkgName + ';'
00178                   + m_endl + m_endl
00179                   + "#UML_MODELER_BEGIN_PERSONAL_VARS_" + classname
00180                   + m_endl + m_endl
00181                   + "#UML_MODELER_END_PERSONAL_VARS_" + classname
00182                   + m_endl
00183                   );
00184       bPackageDeclared = true;
00185     }
00186 
00187     if(str.find(QRegExp("%USE-STATEMENTS%"))){
00188       QString UseStms;
00189       if(GetUseStatements(c,UseStms,ThisPkgName)){
00190         str.replace(QRegExp("%USE-STATEMENTS%"), UseStms);
00191         bUseStmsWritten = true;
00192       }
00193     }
00194 
00195     perl<<str<<m_endl;
00196   }
00197 
00198   // if the package wasn't declared above during keyword substitution,
00199   // add it now. (At the end of the file.)
00200   if(! bPackageDeclared){
00201     perl << m_endl << m_endl << "package " <<ThisPkgName << ";" << m_endl
00202          << m_endl;
00203     //write includes
00204     perl << m_endl << "#UML_MODELER_BEGIN_PERSONAL_VARS_" << classname
00205          << m_endl ;
00206     perl << m_endl << "#UML_MODELER_END_PERSONAL_VARS_" << classname
00207          << m_endl << m_endl ;
00208   }
00209 
00210   if(! bUseStmsWritten){
00211     QString UseStms;
00212     if(GetUseStatements(c,UseStms,ThisPkgName)){
00213       perl<<UseStms<<m_endl;
00214     }
00215   }
00216 
00217   perl << m_endl;
00218 
00219   // Do we really need these for anything???
00220   UMLAssociationList aggregations = c->getAggregations();
00221   UMLAssociationList compositions = c->getCompositions();
00222 
00223     //Write class Documentation
00224   if(forceDoc() || !c->getDoc().isEmpty()) {
00225     perl << m_endl << "=head1";
00226     perl << " " << classname.upper() << m_endl << m_endl;
00227     perl << c->getDoc();
00228     perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00229   }
00230 
00231   //check if class is abstract and / or has abstract methods
00232   if(c->getAbstract())
00233     perl << "=head1 ABSTRACT CLASS" << m_endl << m_endl << "=cut" << m_endl;
00234 
00235   //attributes
00236   if (! c->isInterface())
00237     writeAttributes(c, perl);      // keep for documentation's sake
00238 
00239   //operations
00240   writeOperations(c,perl);
00241 
00242   perl << m_endl;
00243 
00244   //finish file
00245   //perl << m_endl << m_endl << "=cut" << m_endl;
00246   perl << m_endl << m_endl << "return 1;" << m_endl;
00247 
00248   //close files and notify we are done
00249   fileperl.close();
00250   emit codeGenerated(c, true);
00251 }
00252 
00256 Uml::Programming_Language PerlWriter::getLanguage() {
00257     return Uml::pl_Perl;
00258 }
00259 
00261 //  Helper Methods
00262 
00263 void PerlWriter::writeOperations(UMLClassifier *c, QTextStream &perl) {
00264 
00265     //Lists to store operations  sorted by scope
00266     UMLOperationList oppub,opprot,oppriv;
00267 
00268     oppub.setAutoDelete(false);
00269     opprot.setAutoDelete(false);
00270     oppriv.setAutoDelete(false);
00271 
00272     //sort operations by scope first and see if there are abstract methods
00273     //keep this for documentation only!
00274     UMLOperationList opl(c->getOpList());
00275     for(UMLOperation *op = opl.first(); op ; op = opl.next()) {
00276         switch(op->getVisibility()) {
00277           case Uml::Visibility::Public:
00278             oppub.append(op);
00279             break;
00280           case Uml::Visibility::Protected:
00281             opprot.append(op);
00282             break;
00283           case Uml::Visibility::Private:
00284             oppriv.append(op);
00285             break;
00286           default:
00287             break;
00288         }
00289     }
00290 
00291     QString classname(cleanName(c->getName()));
00292 
00293     //write operations to file
00294     if(forceSections() || !oppub.isEmpty()) {
00295         perl << m_endl << "=head1 PUBLIC METHODS" << m_endl << m_endl ;
00296         writeOperations(classname,oppub,perl);
00297         perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00298     }
00299 
00300     if(forceSections() || !opprot.isEmpty()) {
00301         perl << m_endl << "=head1 METHODS FOR SUBCLASSING" << m_endl << m_endl ;
00302         //perl << "=pod "  << m_endl << m_endl << "=head3 " ;
00303         writeOperations(classname,opprot,perl);
00304         perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00305     }
00306 
00307     if(forceSections() || !oppriv.isEmpty()) {
00308         perl << m_endl << "=head1 PRIVATE METHODS" << m_endl << m_endl ;
00309         //perl << "=pod "  << m_endl << m_endl << "=head3 " ;
00310         writeOperations(classname,oppriv,perl);
00311         perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00312     }
00313 
00314     // moved here for perl
00315     if (!c->isInterface() && hasDefaultValueAttr(c)) {
00316         UMLAttributeList atl = c->getAttributeList();
00317 
00318         perl << m_endl;
00319         perl << m_endl << "=head2 _init" << m_endl << m_endl << m_endl;
00320         perl << "_init sets all " + classname + " attributes to their default values unless already set" << m_endl << m_endl << "=cut" << m_endl << m_endl;
00321         perl << "sub _init {" << m_endl << m_indentation << "my $self = shift;" << m_endl<<m_endl;
00322 
00323         for(UMLAttribute *at = atl.first(); at ; at = atl.next()) {
00324             if(!at->getInitialValue().isEmpty())
00325                 perl << m_indentation << "defined $self->{" << cleanName(at->getName())<<"}"
00326                 << " or $self->{" << cleanName(at->getName()) << "} = "
00327                 << at->getInitialValue() << ";" << m_endl;
00328         }
00329 
00330         perl << " }" << m_endl;
00331     }
00332 
00333     perl << m_endl << m_endl;
00334 }
00335 
00336 void PerlWriter::writeOperations(const QString &/* classname */, UMLOperationList &opList, QTextStream &perl) {
00337     UMLOperation *op;
00338     UMLAttribute *at;
00339 
00340     for(op=opList.first(); op ; op=opList.next())
00341     {
00342         UMLAttributeList atl = op->getParmList();
00343         //write method doc if we have doc || if at least one of the params has doc
00344         bool writeDoc = forceDoc() || !op->getDoc().isEmpty();
00345         for (at = atl.first(); at ; at = atl.next())
00346             writeDoc |= !at->getDoc().isEmpty();
00347 
00348         if( writeDoc )  //write method documentation
00349         {
00350             perl << "=pod "  << m_endl << m_endl << "=head3 " ;
00351             perl << cleanName(op->getName()) << m_endl << m_endl;
00352 
00353             perl << "   Parameters :" << m_endl ;
00354           //write parameter documentation
00355           for (at = atl.first(); at ; at = atl.next()) {
00356             if(forceDoc() || !at->getDoc().isEmpty()) {
00357               perl << "      "
00358                    << cleanName(at->getName()) << " : "
00359                    << at->getTypeName() << " : "
00360                    << at->getDoc()
00361                    << m_endl;
00362                 }
00363             }//end for : write parameter documentation
00364 
00365             perl << m_endl;
00366             perl << "   Return : " << m_endl;
00367             perl << "      " << op->getTypeName();
00368             perl << m_endl << m_endl;
00369             perl << "   Description : " << m_endl;
00370             perl << "      " << op->getDoc();
00371             perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00372         }//end if : write method documentation
00373 
00374         perl <<  "sub " << cleanName(op->getName()) << m_endl << "{" << m_endl;
00375         perl << "  my($self";
00376 
00377         bool bStartPrinted = false;
00378         //write parameters
00379         for (at = atl.first(); at; at = atl.next()) {
00380           if (!bStartPrinted) {
00381               bStartPrinted = true;
00382               perl << "," << m_endl;
00383           }
00384           perl << "     $"<< cleanName(at->getName()) << ", # "
00385                << at->getTypeName() << " : " << at->getDoc() << m_endl;
00386         }
00387 
00388         perl << "    ) = @_;" << m_endl;
00389 
00390         perl << "#UML_MODELER_BEGIN_PERSONAL_CODE_" << cleanName(op->getName());
00391         perl << m_endl << "#UML_MODELER_END_PERSONAL_CODE_" << cleanName(op->getName()) << m_endl;
00392         perl << "}" << m_endl;
00393         perl << m_endl << m_endl;
00394     }//end for
00395 }
00396 
00397 
00398 void PerlWriter::writeAttributes(UMLClassifier *c, QTextStream &perl) {
00399     UMLAttributeList  atpub, atprot, atpriv, atdefval;
00400     atpub.setAutoDelete(false);
00401     atprot.setAutoDelete(false);
00402     atpriv.setAutoDelete(false);
00403     atdefval.setAutoDelete(false);
00404 
00405     //sort attributes by scope and see if they have a default value
00406     UMLAttributeList atl = c->getAttributeList();
00407     UMLAttribute *at;
00408     for(at = atl.first(); at ; at = atl.next()) {
00409         if(!at->getInitialValue().isEmpty())
00410             atdefval.append(at);
00411         switch(at->getVisibility()) {
00412           case Uml::Visibility::Public:
00413             atpub.append(at);
00414             break;
00415           case Uml::Visibility::Protected:
00416             atprot.append(at);
00417             break;
00418           case Uml::Visibility::Private:
00419             atpriv.append(at);
00420             break;
00421           default:
00422             break;
00423         }
00424     }
00425 
00426 
00427     if(forceSections() || atpub.count()) {
00428         writeAttributes(atpub,perl);
00429     }
00430     /* not needed as writeAttributes only writes documentation
00431     if(forceSections() || atprot.count()) {
00432     writeAttributes(atprot,perl);
00433     }
00434 
00435     if(forceSections() || atpriv.count()) {
00436     writeAttributes(atpriv,perl);
00437     }
00438     */
00439 }
00440 
00441 
00442 void PerlWriter::writeAttributes(UMLAttributeList &atList, QTextStream &perl)
00443 {
00444     perl << m_endl << "=head1 PUBLIC ATTRIBUTES" << m_endl << m_endl;
00445     perl << "=pod "  << m_endl << m_endl ;
00446     for (UMLAttribute *at = atList.first(); at ; at = atList.next())
00447     {
00448         if (forceDoc() || !at->getDoc().isEmpty())
00449         {
00450             perl  << "=head3 " << cleanName(at->getName()) << m_endl << m_endl ;
00451             perl  << "   Description : " << at->getDoc() << m_endl << m_endl;
00452         }
00453     } // end for
00454     perl << m_endl << m_endl << "=cut" << m_endl << m_endl;
00455     return;
00456 }
00457 
00458 QStringList PerlWriter::defaultDatatypes() {
00459     QStringList l;
00460     l.append("$");
00461     l.append("@");
00462     l.append("%");
00463     return l;
00464 }
00465 
00466 const QStringList PerlWriter::reservedKeywords() const {
00467 
00468     static QStringList keywords;
00469 
00470     if (keywords.isEmpty()) {
00471         keywords << "abs"
00472         << "accept"
00473         << "alarm"
00474         << "and"
00475         << "atan2"
00476         << "BEGIN"
00477         << "bind"
00478         << "binmode"
00479         << "bless"
00480         << "byte"
00481         << "caller"
00482         << "carp"
00483         << "chdir"
00484         << "chmod"
00485         << "chomp"
00486         << "chop"
00487         << "chown"
00488         << "chr"
00489         << "chroot"
00490         << "close"
00491         << "closedir"
00492         << "cmp"
00493         << "confess"
00494         << "connect"
00495         << "continue"
00496         << "cos"
00497         << "croak"
00498         << "crypt"
00499         << "dbmclose"
00500         << "dbmopen"
00501         << "defined"
00502         << "delete"
00503         << "die"
00504         << "do"
00505         << "dump"
00506         << "each"
00507         << "else"
00508         << "elsif"
00509         << "END"
00510         << "endgrent"
00511         << "endhostent"
00512         << "endnetent"
00513         << "endprotoent"
00514         << "endpwent"
00515         << "endservent"
00516         << "eof"
00517         << "eq"
00518         << "eval"
00519         << "exec"
00520         << "exists"
00521         << "exit"
00522         << "exp"
00523         << "fcntl"
00524         << "fileno"
00525         << "flock"
00526         << "for"
00527         << "foreach"
00528         << "fork"
00529         << "format"
00530         << "formline"
00531         << "ge"
00532         << "getc"
00533         << "getgrent"
00534         << "getgrgid"
00535         << "getgrnam"
00536         << "gethostbyaddr"
00537         << "gethostbyname"
00538         << "gethostent"
00539         << "getlogin"
00540         << "getnetbyaddr"
00541         << "getnetbyname"
00542         << "getnetent"
00543         << "getpeername"
00544         << "getpgrp"
00545         << "getppid"
00546         << "getpriority"
00547         << "getprotobyname"
00548         << "getprotobynumber"
00549         << "getprotoent"
00550         << "getpwent"
00551         << "getpwnam"
00552         << "getpwuid"
00553         << "getservbyname"
00554         << "getservbyport"
00555         << "getservent"
00556         << "getsockname"
00557         << "getsockopt"
00558         << "glob"
00559         << "gmtime"
00560         << "goto"
00561         << "grep"
00562         << "gt"
00563         << "hex"
00564         << "if"
00565         << "import"
00566         << "index"
00567         << "int"
00568         << "integer"
00569         << "ioctl"
00570         << "join"
00571         << "keys"
00572         << "kill"
00573         << "last"
00574         << "lc"
00575         << "lcfirst"
00576         << "le"
00577         << "length"
00578         << "lib"
00579         << "link"
00580         << "listen"
00581         << "local"
00582         << "localtime"
00583         << "lock"
00584         << "log"
00585         << "lstat"
00586         << "lt"
00587         << "map"
00588         << "mkdir"
00589         << "msgctl"
00590         << "msgget"
00591         << "msgrcv"
00592         << "msgsnd"
00593         << "my"
00594         << "ne"
00595         << "new"
00596         << "next"
00597         << "no"
00598         << "not"
00599         << "oct"
00600         << "open"
00601         << "opendir"
00602         << "or"
00603         << "ord"
00604         << "our"
00605         << "pack"
00606         << "package"
00607         << "pipe"
00608         << "pop"
00609         << "pos"
00610         << "print"
00611         << "printf"
00612         << "prototype"
00613         << "push"
00614         << "quotemeta"
00615         << "rand"
00616         << "read"
00617         << "readdir"
00618         << "readline"
00619         << "readlink"
00620         << "readpipe"
00621         << "recv"
00622         << "redo"
00623         << "ref"
00624         << "rename"
00625         << "require"
00626         << "reset"
00627         << "return"
00628         << "reverse"
00629         << "rewinddir"
00630         << "rindex"
00631         << "rmdir"
00632         << "scalar"
00633         << "seek"
00634         << "seekdir"
00635         << "select"
00636         << "semctl"
00637         << "semget"
00638         << "semop"
00639         << "send"
00640         << "setgrent"
00641         << "sethostent"
00642         << "setnetent"
00643         << "setpgrp"
00644         << "setpriority"
00645         << "setprotoent"
00646         << "setpwent"
00647         << "setservent"
00648         << "setsockopt"
00649         << "shift"
00650         << "shmctl"
00651         << "shmget"
00652         << "shmread"
00653         << "shmwrite"
00654         << "shutdown"
00655         << "sigtrap"
00656         << "sin"
00657         << "sleep"
00658         << "socket"
00659         << "socketpair"
00660         << "sort"
00661         << "splice"
00662         << "split"
00663         << "sprintf"
00664         << "sqrt"
00665         << "srand"
00666         << "stat"
00667         << "strict"
00668         << "study"
00669         << "sub"
00670         << "subs"
00671         << "substr"
00672         << "switch"
00673         << "symlink"
00674         << "syscall"
00675         << "sysopen"
00676         << "sysread"
00677         << "sysseek"
00678         << "system"
00679         << "syswrite"
00680         << "tell"
00681         << "telldir"
00682         << "tie"
00683         << "tied"
00684         << "time"
00685         << "times"
00686         << "truncate"
00687         << "uc"
00688         << "ucfirst"
00689         << "umask"
00690         << "undef"
00691         << "unless"
00692         << "unlink"
00693         << "unpack"
00694         << "unshift"
00695         << "untie"
00696         << "until"
00697         << "use"
00698         << "utf8"
00699         << "utime"
00700         << "values"
00701         << "vars"
00702         << "vec"
00703         << "wait"
00704         << "waitpid"
00705         << "wantarray"
00706         << "warn"
00707         << "warnings"
00708         << "while"
00709         << "write"
00710         << "xor";
00711     }
00712 
00713     return keywords;
00714 }
00715 
00716 #include "perlwriter.moc"
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:07:59 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003