umbrello API Documentation

rubywriter.cpp

00001 /***************************************************************************
00002                           rubywriter.h  -  description
00003                              -------------------
00004     begin                : Sat Dec 21 2002
00005     copyright            : Vincent Decorges
00006     email                : vincent.decorges@eivd.ch
00007       (C) 2003-2006  Umbrello UML Modeller Authors <uml-devel@uml.sf.net>
00008  ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 
00019 #include "rubywriter.h"
00020 
00021 #include <kdebug.h>
00022 #include <klocale.h>
00023 #include <kmessagebox.h>
00024 #include <qfile.h>
00025 #include <qtextstream.h>
00026 #include <qregexp.h>
00027 
00028 #include "classifierinfo.h"
00029 #include "../umldoc.h"
00030 #include "../umlattributelist.h"
00031 #include "../association.h"
00032 #include "../attribute.h"
00033 #include "../classifier.h"
00034 #include "../operation.h"
00035 #include "../umlnamespace.h"
00036 
00037 RubyWriter::RubyWriter() {
00038 }
00039 
00040 RubyWriter::~RubyWriter() {}
00041 
00042 void RubyWriter::writeClass(UMLClassifier *c) {
00043     if(!c) {
00044         kDebug()<<"Cannot write class of NULL concept!" << endl;
00045         return;
00046     }
00047 
00048     QString classname = cleanName(c->getName());
00049 
00050     UMLClassifierList superclasses = c->getSuperClasses();
00051     UMLAssociationList aggregations = c->getAggregations();
00052     UMLAssociationList compositions = c->getCompositions();
00053 
00054     //find an appropriate name for our file
00055     QString fileName = findFileName(c, ".rb");
00056     if (fileName.isEmpty()) {
00057         emit codeGenerated(c, false);
00058         return;
00059     }
00060 
00061     QFile fileh;
00062     if( !openFile(fileh, fileName) ) {
00063         emit codeGenerated(c, false);
00064         return;
00065     }
00066     QTextStream h(&fileh);
00067 
00068     // preparations
00069     classifierInfo = new ClassifierInfo(c, m_doc);
00070     classifierInfo->fileName = fileName;
00071     classifierInfo->className = cleanName(c->getName());
00072 
00074     //Start generating the code!!
00076 
00077 
00078     //try to find a heading file (license, coments, etc)
00079     QString str;
00080 
00081     str = getHeadingFile(".rb");
00082     if(!str.isEmpty()) {
00083         str.replace(QRegExp("%filename%"), fileName);
00084         str.replace(QRegExp("%filepath%"), fileh.name());
00085         h<<str<<m_endl;
00086     }
00087 
00088     if(forceDoc() || !c->getDoc().isEmpty()) {
00089         QString docStr = c->getDoc();
00090         docStr.replace(QRegExp("\\n"), "\n# ");
00091         docStr.replace("@ref ", "");
00092         docStr.replace("@see", "_See_");
00093         docStr.replace("@short", "_Summary_");
00094         docStr.replace("@author", "_Author_");
00095         h<<"#"<<m_endl;
00096         h<<"# "<<docStr<<m_endl;
00097         h<<"#"<<m_endl<<m_endl;
00098     }
00099 
00100     // write inheritances out
00101     UMLClassifier *concept;
00102 
00103     h<< "class " << cppToRubyType(classname) << (superclasses.count() > 0 ? " < ":"");
00104 
00105     int i = 0;
00106     for (concept = superclasses.first(); concept; concept = superclasses.next()) {
00107         if (i == 0) {
00108             h << cppToRubyType(concept->getName()) << m_endl;
00109         } else {
00110             // Assume ruby modules that can be mixed in, after the first
00111             // superclass name in the list
00112             h << m_indentation << "include "<< cppToRubyType(concept->getName()) << m_endl;
00113         }
00114         i++;
00115     }
00116 
00117     h << m_endl;
00118 
00119     // write comment for sub-section IF needed
00120     if (forceDoc() || classifierInfo->hasAccessorMethods) {
00121         h << m_indentation << "#" << m_endl;
00122         h << m_indentation << "# Accessor Methods" << m_endl;
00123         h << m_indentation << "#" << m_endl << m_endl;
00124 
00125         // Accessor methods for attributes
00126         writeAttributeMethods(&(classifierInfo->atpub), Uml::Visibility::Public, h);
00127         writeAttributeMethods(&(classifierInfo->atprot), Uml::Visibility::Protected, h);
00128         writeAttributeMethods(&(classifierInfo->atpriv), Uml::Visibility::Private, h);
00129         h << m_endl;
00130     }
00131 
00132     //operations
00133     writeOperations(c, h);
00134 
00135     //finish files
00136     h << "end" << m_endl << m_endl;
00137 
00138     //close files and notfiy we are done
00139     fileh.close();
00140     emit codeGenerated(c, true);
00141 }
00142 
00143 
00145 //  Helper Methods
00146 
00147 QString RubyWriter::cppToRubyType(const QString &typeStr) {
00148     QString type = cleanName(typeStr);
00149     type.replace("const ", "");
00150     type.replace(QRegExp("[*&\\s]"), "");
00151     type.replace(QRegExp("[<>]"), "_");
00152     type.replace("QStringList", "Array");
00153     type.replace("QString", "String");
00154     type.replace("bool", "true|false");
00155     type.replace(QRegExp("^(uint|int|ushort|short|ulong|long)$"), "Integer");
00156     type.replace(QRegExp("^(float|double)$"), "Float");
00157     type.replace(QRegExp("^Q(?=[A-Z])"), "Qt::");
00158     type.replace(QRegExp("^K(?!(DE|Parts|IO)"), "KDE::");
00159 
00160     return type;
00161 }
00162 
00163 QString RubyWriter::cppToRubyName(const QString &nameStr) {
00164     QString name = cleanName(nameStr);
00165     name.replace(QRegExp("^m_"), "");
00166     name.replace(QRegExp("^[pbn](?=[A-Z])"), "");
00167     name = name.mid(0, 1).lower() + name.mid(1);
00168     return name;
00169 }
00170 
00171 void RubyWriter::writeOperations(UMLClassifier *c,QTextStream &h) {
00172 
00173     //Lists to store operations  sorted by scope
00174     UMLOperationList oppub,opprot,oppriv;
00175 
00176     oppub.setAutoDelete(false);
00177     opprot.setAutoDelete(false);
00178     oppriv.setAutoDelete(false);
00179 
00180     //sort operations by scope first and see if there are abstract methods
00181     UMLOperationList opl(c->getOpList());
00182     for(UMLOperation *op = opl.first(); op ; op = opl.next()) {
00183         switch(op->getVisibility()) {
00184         case Uml::Visibility::Public:
00185             oppub.append(op);
00186             break;
00187         case Uml::Visibility::Protected:
00188             opprot.append(op);
00189             break;
00190         case Uml::Visibility::Private:
00191             oppriv.append(op);
00192             break;
00193         default:
00194             break;
00195         }
00196     }
00197 
00198     QString classname(cleanName(c->getName()));
00199 
00200     //write operations to file
00201     if(forceSections() || !oppub.isEmpty()) {
00202         writeOperations(classname, oppub, Uml::Visibility::Public, h);
00203     }
00204 
00205     if(forceSections() || !opprot.isEmpty()) {
00206         writeOperations(classname, opprot, Uml::Visibility::Protected, h);
00207     }
00208 
00209     if(forceSections() || !oppriv.isEmpty()) {
00210         writeOperations(classname, oppriv, Uml::Visibility::Private, h);
00211     }
00212 
00213 }
00214 
00215 void RubyWriter::writeOperations(const QString &classname, UMLOperationList &opList,
00216                                  Uml::Visibility permitScope, QTextStream &h)
00217 {
00218     UMLOperation *op;
00219     UMLAttribute *at;
00220 
00221     switch (permitScope) {
00222     case Uml::Visibility::Public:
00223         h << m_indentation << "public" << m_endl << m_endl;
00224         break;
00225     case Uml::Visibility::Protected:
00226         h << m_indentation << "protected" << m_endl << m_endl;
00227         break;
00228     case Uml::Visibility::Private:
00229         h << m_indentation << "private" << m_endl << m_endl;
00230         break;
00231     default:
00232         break;
00233     }
00234 
00235     for (op=opList.first(); op ; op=opList.next()) {
00236         QString methodName = cleanName(op->getName());
00237         QStringList commentedParams;
00238 
00239         // Skip destructors, and operator methods which
00240         // can't be defined in ruby
00241         if (    methodName.startsWith("~")
00242                 || methodName == "operator ="
00243                 || methodName == "operator --"
00244                 || methodName == "operator ++"
00245                 || methodName == "operator !=" )
00246         {
00247             continue;
00248         }
00249 
00250         if (methodName == classname) {
00251             methodName = "initialize";
00252         }
00253 
00254         methodName.replace("operator ", "");
00255         methodName = methodName.mid(0, 1).lower() + methodName.mid(1);
00256 
00257         UMLAttributeList atl = op->getParmList();
00258         //write method doc if we have doc || if at least one of the params has doc
00259         bool writeDoc = forceDoc() || !op->getDoc().isEmpty();
00260         // Always write out the docs for ruby as the type of the
00261         // arguments and return value of the methods is useful
00262         writeDoc = true;
00263 //        for (at = atl.first(); at; at = atl.next())
00264 //            writeDoc |= !at->getDoc().isEmpty();
00265 
00266         if (writeDoc) {
00267             h << m_indentation << "#" << m_endl;
00268             QString docStr = op->getDoc();
00269 
00270             docStr.replace(QRegExp("[\\n\\r]+ *"), m_endl);
00271             docStr.replace(QRegExp("[\\n\\r]+\\t*"), m_endl);
00272 
00273             docStr.replace(" m_", " ");
00274             docStr.replace(QRegExp("\\s[npb](?=[A-Z])"), " ");
00275             QRegExp re_params("@param (\\w)(\\w*)");
00276             int pos = re_params.search(docStr);
00277             while (pos != -1) {
00278                 docStr.replace( re_params.cap(0),
00279                                 QString("@param _") + re_params.cap(1).lower() + re_params.cap(2) + '_' );
00280                 commentedParams.append(re_params.cap(1).lower() + re_params.cap(2));
00281 
00282                 pos += re_params.matchedLength() + 3;
00283                 pos = re_params.search(docStr, pos);
00284             }
00285 
00286             docStr.replace("\n", QString("\n") + m_indentation + "# ");
00287 
00288             // Write parameter documentation
00289             for (at = atl.first(); at ; at = atl.next()) {
00290                 // Only write an individual @param entry if one hasn't been found already
00291                 // in the main doc comment
00292                 if (commentedParams.contains(cppToRubyName(at->getName())) == 0) {
00293                     docStr += (m_endl + m_indentation + "# @param _" + cppToRubyName(at->getName()) + '_');
00294                     if (at->getDoc().isEmpty()) {
00295                         docStr += (' ' + cppToRubyType(at->getTypeName()));
00296                     } else {
00297                         docStr += (' ' + at->getDoc().replace(QRegExp("[\\n\\r]+[\\t ]*"), m_endl + "   "));
00298                     }
00299                 }
00300             }
00301 
00302             docStr.replace("@ref ", "");
00303             docStr.replace("@param", "*");
00304             docStr.replace("@return", "* _returns_");
00305 
00306             // All lines after the first '# *' in the doc comment
00307             // must be indented correctly. If they aren't a list
00308             // item starting with '# *', then indent the text with
00309             // three spaces, '#   ', to line up with the list item.
00310             pos = docStr.find("# *");
00311             QRegExp re_linestart("# (?!\\*)");
00312             pos = re_linestart.search(docStr, pos);
00313             while (pos > 0) {
00314                 docStr.insert(pos + 1, "  ");
00315 
00316                 pos += re_linestart.matchedLength() + 2;
00317                 pos = re_linestart.search(docStr, pos);
00318             }
00319 
00320             h << m_indentation << "# "<< docStr << m_endl;
00321 
00322             QString typeStr = cppToRubyType(op->getTypeName());
00323             if (!typeStr.isEmpty() && typeStr != "void" && docStr.contains("_returns_") == 0) {
00324                 h << m_indentation << "# * _returns_ " << typeStr << m_endl;
00325             }
00326         }
00327 
00328         h<< m_indentation << "def " + methodName << "(";
00329 
00330         int j=0;
00331         for (at = atl.first(); at; at = atl.next(), j++) {
00332             QString nameStr = cppToRubyName(at->getName());
00333             if (j > 0) {
00334                 h << ", " << nameStr;
00335             } else {
00336                 h << nameStr;
00337             }
00338             h << (!(at->getInitialValue().isEmpty()) ?
00339                 (QString(" = ") + cppToRubyType(at->getInitialValue())) :
00340                 QString(""));
00341         }
00342 
00343         h <<")" << m_endl;
00344 
00345         h << m_indentation << m_indentation << m_endl;
00346 
00347         h << m_indentation << "end" << m_endl << m_endl;
00348 
00349     }//end for
00350 
00351 }
00352 
00353 // this is for writing file attribute methods
00354 //
00355 void RubyWriter::writeAttributeMethods(UMLAttributeList *attribs,
00356                                       Uml::Visibility visibility, QTextStream &stream)
00357 {
00358     // return now if NO attributes to work on
00359     if (attribs->count() == 0 || visibility == Uml::Visibility::Private)
00360         return;
00361 
00362     UMLAttribute *at;
00363     for(at=attribs->first(); at; at=attribs->next())
00364     {
00365         QString varName = cppToRubyName(cleanName(at->getName()));
00366 
00367         writeSingleAttributeAccessorMethods(varName, at->getDoc(), stream);
00368     }
00369 
00370 }
00371 
00372 void RubyWriter::writeSingleAttributeAccessorMethods(
00373         const QString &fieldName,
00374         const QString &descr,
00375         QTextStream &h)
00376 {
00377     QString description = descr;
00378     description.replace(QRegExp("m_[npb](?=[A-Z])"), "");
00379     description.replace("m_", "");
00380     description.replace("\n", QString("\n") + m_indentation + "# ");
00381 
00382     if (!description.isEmpty()) {
00383         h << m_indentation << "# " << description << m_endl;
00384     }
00385 
00386     h << m_indentation << "attr_accessor :" << fieldName << m_endl << m_endl;
00387 
00388     return;
00389 }
00390 
00394 Uml::Programming_Language RubyWriter::getLanguage() {
00395     return Uml::pl_Ruby;
00396 }
00397 
00398 const QStringList RubyWriter::reservedKeywords() const {
00399 
00400     static QStringList keywords;
00401 
00402     if (keywords.isEmpty()) {
00403         keywords << "__FILE__"
00404         << "__LINE__"
00405         << "BEGIN"
00406         << "END"
00407         << "alias"
00408         << "and"
00409         << "begin"
00410         << "break"
00411         << "case"
00412         << "class"
00413         << "def"
00414         << "defined?"
00415         << "do"
00416         << "else"
00417         << "elsif"
00418         << "end"
00419         << "ensure"
00420         << "false"
00421         << "for"
00422         << "if"
00423         << "in"
00424         << "module"
00425         << "next"
00426         << "nil"
00427         << "not"
00428         << "or"
00429         << "redo"
00430         << "rescue"
00431         << "retry"
00432         << "return"
00433         << "self"
00434         << "super"
00435         << "then"
00436         << "true"
00437         << "undef"
00438         << "unless"
00439         << "until"
00440         << "when"
00441         << "while"
00442         << "yield";
00443     }
00444 
00445     return keywords;
00446 }
00447 
00448 #include "rubywriter.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:08:00 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003