/*************************************************************************** rubywriter.h - description ------------------- begin : Sat Dec 21 2002 copyright : Vincent Decorges email : vincent.decorges@eivd.ch (C) 2003-2006 Umbrello UML Modeller Authors ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "rubywriter.h" #include #include #include #include #include #include #include "classifierinfo.h" #include "../umldoc.h" #include "../umlattributelist.h" #include "../association.h" #include "../attribute.h" #include "../classifier.h" #include "../operation.h" #include "../umlnamespace.h" RubyWriter::RubyWriter() { } RubyWriter::~RubyWriter() {} void RubyWriter::writeClass(UMLClassifier *c) { if(!c) { kDebug()<<"Cannot write class of NULL concept!" << endl; return; } TQString classname = cleanName(c->getName()); UMLClassifierList superclasses = c->getSuperClasses(); UMLAssociationList aggregations = c->getAggregations(); UMLAssociationList compositions = c->getCompositions(); //find an appropriate name for our file TQString fileName = findFileName(c, ".rb"); if (fileName.isEmpty()) { emit codeGenerated(c, false); return; } TQFile fileh; if( !openFile(fileh, fileName) ) { emit codeGenerated(c, false); return; } TQTextStream h(&fileh); // preparations classifierInfo = new ClassifierInfo(c); classifierInfo->fileName = fileName; classifierInfo->className = cleanName(c->getName()); ////////////////////////////// //Start generating the code!! ///////////////////////////// //try to find a heading file (license, coments, etc) TQString str; str = getHeadingFile(".rb"); if(!str.isEmpty()) { str.replace(TQRegExp("%filename%"), fileName); str.replace(TQRegExp("%filepath%"), fileh.name()); h<getDoc().isEmpty()) { TQString docStr = c->getDoc(); docStr.replace(TQRegExp("\\n"), "\n# "); docStr.replace("@ref ", ""); docStr.replace("@see", "_See_"); docStr.replace("@short", "_Summary_"); docStr.replace("@author", "_Author_"); h<<"#"< 0 ? " < ":""); int i = 0; for (concept = superclasses.first(); concept; concept = superclasses.next()) { if (i == 0) { h << cppToRubyType(concept->getName()) << m_endl; } else { // Assume ruby modules that can be mixed in, after the first // superclass name in the list h << m_indentation << "include "<< cppToRubyType(concept->getName()) << m_endl; } i++; } h << m_endl; // write comment for sub-section IF needed if (forceDoc() || classifierInfo->hasAccessorMethods) { h << m_indentation << "#" << m_endl; h << m_indentation << "# Accessor Methods" << m_endl; h << m_indentation << "#" << m_endl << m_endl; // Accessor methods for attributes writeAttributeMethods(&(classifierInfo->atpub), Uml::Visibility::Public, h); writeAttributeMethods(&(classifierInfo->atprot), Uml::Visibility::Protected, h); writeAttributeMethods(&(classifierInfo->atpriv), Uml::Visibility::Private, h); h << m_endl; } //operations writeOperations(c, h); //finish files h << "end" << m_endl << m_endl; //close files and notfiy we are done fileh.close(); emit codeGenerated(c, true); } //////////////////////////////////////////////////////////////////////////////////// // Helper Methods TQString RubyWriter::cppToRubyType(const TQString &typeStr) { TQString type = cleanName(typeStr); type.replace("const ", ""); type.replace(TQRegExp("[*&\\s]"), ""); type.replace(TQRegExp("[<>]"), "_"); type.replace(TQSTRINGLIST_OBJECT_NAME_STRING, "Array"); type.replace(TQSTRING_OBJECT_NAME_STRING, "String"); type.replace("bool", "true|false"); type.replace(TQRegExp("^(uint|int|ushort|short|ulong|long)$"), "Integer"); type.replace(TQRegExp("^(float|double)$"), "Float"); type.replace(TQRegExp("^Q(?=[A-Z])"), "TQt::"); type.replace(TQRegExp("^K(?!(DE|Parts|IO)"), "KDE::"); return type; } TQString RubyWriter::cppToRubyName(const TQString &nameStr) { TQString name = cleanName(nameStr); name.replace(TQRegExp("^m_"), ""); name.replace(TQRegExp("^[pbn](?=[A-Z])"), ""); name = name.mid(0, 1).lower() + name.mid(1); return name; } void RubyWriter::writeOperations(UMLClassifier *c,TQTextStream &h) { //Lists to store operations sorted by scope UMLOperationList oppub,opprot,oppriv; oppub.setAutoDelete(false); opprot.setAutoDelete(false); oppriv.setAutoDelete(false); //sort operations by scope first and see if there are abstract methods UMLOperationList opl(c->getOpList()); for(UMLOperation *op = opl.first(); op ; op = opl.next()) { switch(op->getVisibility()) { case Uml::Visibility::Public: oppub.append(op); break; case Uml::Visibility::Protected: opprot.append(op); break; case Uml::Visibility::Private: oppriv.append(op); break; default: break; } } TQString classname(cleanName(c->getName())); //write operations to file if(forceSections() || !oppub.isEmpty()) { writeOperations(classname, oppub, Uml::Visibility::Public, h); } if(forceSections() || !opprot.isEmpty()) { writeOperations(classname, opprot, Uml::Visibility::Protected, h); } if(forceSections() || !oppriv.isEmpty()) { writeOperations(classname, oppriv, Uml::Visibility::Private, h); } } void RubyWriter::writeOperations(const TQString &classname, UMLOperationList &opList, Uml::Visibility permitScope, TQTextStream &h) { UMLOperation *op; UMLAttribute *at; switch (permitScope) { case Uml::Visibility::Public: h << m_indentation << "public" << m_endl << m_endl; break; case Uml::Visibility::Protected: h << m_indentation << "protected" << m_endl << m_endl; break; case Uml::Visibility::Private: h << m_indentation << "private" << m_endl << m_endl; break; default: break; } for (op=opList.first(); op ; op=opList.next()) { TQString methodName = cleanName(op->getName()); TQStringList commentedParams; // Skip destructors, and operator methods which // can't be defined in ruby if ( methodName.startsWith("~") || methodName == "operator =" || methodName == "operator --" || methodName == "operator ++" || methodName == "operator !=" ) { continue; } if (methodName == classname) { methodName = "initialize"; } methodName.replace("operator ", ""); methodName = methodName.mid(0, 1).lower() + methodName.mid(1); UMLAttributeList atl = op->getParmList(); //write method doc if we have doc || if at least one of the params has doc bool writeDoc = forceDoc() || !op->getDoc().isEmpty(); // Always write out the docs for ruby as the type of the // arguments and return value of the methods is useful writeDoc = true; // for (at = atl.first(); at; at = atl.next()) // writeDoc |= !at->getDoc().isEmpty(); if (writeDoc) { h << m_indentation << "#" << m_endl; TQString docStr = op->getDoc(); docStr.replace(TQRegExp("[\\n\\r]+ *"), m_endl); docStr.replace(TQRegExp("[\\n\\r]+\\t*"), m_endl); docStr.replace(" m_", " "); docStr.replace(TQRegExp("\\s[npb](?=[A-Z])"), " "); TQRegExp re_params("@param (\\w)(\\w*)"); int pos = re_params.search(docStr); while (pos != -1) { docStr.replace( re_params.cap(0), TQString("@param _") + re_params.cap(1).lower() + re_params.cap(2) + '_' ); commentedParams.append(re_params.cap(1).lower() + re_params.cap(2)); pos += re_params.matchedLength() + 3; pos = re_params.search(docStr, pos); } docStr.replace("\n", TQString("\n") + m_indentation + "# "); // Write parameter documentation for (at = atl.first(); at ; at = atl.next()) { // Only write an individual @param entry if one hasn't been found already // in the main doc comment if (commentedParams.contains(cppToRubyName(at->getName())) == 0) { docStr += (m_endl + m_indentation + "# @param _" + cppToRubyName(at->getName()) + '_'); if (at->getDoc().isEmpty()) { docStr += (' ' + cppToRubyType(at->getTypeName())); } else { docStr += (' ' + at->getDoc().replace(TQRegExp("[\\n\\r]+[\\t ]*"), m_endl + " ")); } } } docStr.replace("@ref ", ""); docStr.replace("@param", "*"); docStr.replace("@return", "* _returns_"); // All lines after the first '# *' in the doc comment // must be indented correctly. If they aren't a list // item starting with '# *', then indent the text with // three spaces, '# ', to line up with the list item. pos = docStr.find("# *"); TQRegExp re_linestart("# (?!\\*)"); pos = re_linestart.search(docStr, pos); while (pos > 0) { docStr.insert(pos + 1, " "); pos += re_linestart.matchedLength() + 2; pos = re_linestart.search(docStr, pos); } h << m_indentation << "# "<< docStr << m_endl; TQString typeStr = cppToRubyType(op->getTypeName()); if (!typeStr.isEmpty() && typeStr != "void" && docStr.contains("_returns_") == 0) { h << m_indentation << "# * _returns_ " << typeStr << m_endl; } } h<< m_indentation << "def " + methodName << "("; int j=0; for (at = atl.first(); at; at = atl.next(), j++) { TQString nameStr = cppToRubyName(at->getName()); if (j > 0) { h << ", " << nameStr; } else { h << nameStr; } h << (!(at->getInitialValue().isEmpty()) ? (TQString(" = ") + cppToRubyType(at->getInitialValue())) : TQString("")); } h <<")" << m_endl; h << m_indentation << m_indentation << m_endl; h << m_indentation << "end" << m_endl << m_endl; }//end for } // this is for writing file attribute methods // void RubyWriter::writeAttributeMethods(UMLAttributeList *attribs, Uml::Visibility visibility, TQTextStream &stream) { // return now if NO attributes to work on if (attribs->count() == 0 || visibility == Uml::Visibility::Private) return; UMLAttribute *at; for(at=attribs->first(); at; at=attribs->next()) { TQString varName = cppToRubyName(cleanName(at->getName())); writeSingleAttributeAccessorMethods(varName, at->getDoc(), stream); } } void RubyWriter::writeSingleAttributeAccessorMethods( const TQString &fieldName, const TQString &descr, TQTextStream &h) { TQString description = descr; description.replace(TQRegExp("m_[npb](?=[A-Z])"), ""); description.replace("m_", ""); description.replace("\n", TQString("\n") + m_indentation + "# "); if (!description.isEmpty()) { h << m_indentation << "# " << description << m_endl; } h << m_indentation << "attr_accessor :" << fieldName << m_endl << m_endl; return; } /** * returns "Ruby" */ Uml::Programming_Language RubyWriter::getLanguage() { return Uml::pl_Ruby; } const TQStringList RubyWriter::reservedKeywords() const { static TQStringList keywords; if (keywords.isEmpty()) { keywords << "__FILE__" << "__LINE__" << "BEGIN" << "END" << "alias" << "and" << "begin" << "break" << "case" << "class" << "def" << "defined?" << "do" << "else" << "elsif" << "end" << "ensure" << "false" << "for" << "if" << "in" << "module" << "next" << "nil" << "not" << "or" << "redo" << "rescue" << "retry" << "return" << "self" << "super" << "then" << "true" << "undef" << "unless" << "until" << "when" << "while" << "yield"; } return keywords; } #include "rubywriter.moc"