PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/kdesdk-4.8.97/umbrello/umbrello/codegenerators/ruby/rubywriter.cpp

#
C++ | 435 lines | 320 code | 70 blank | 45 comment | 53 complexity | 717b1ce7675e3b1538a3f78a721a2183 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-2.0, GPL-2.0, CC-BY-SA-3.0
  1. /***************************************************************************
  2. * This program is free software; you can redistribute it and/or modify *
  3. * it under the terms of the GNU General Public License as published by *
  4. * the Free Software Foundation; either version 2 of the License, or *
  5. * (at your option) any later version. *
  6. * *
  7. * copyright (C) 2005 *
  8. * Richard Dale <Richard_Dale@tipitina.demon.co.uk> *
  9. * copyright (C) 2006-2011 *
  10. * Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
  11. ***************************************************************************/
  12. #include "rubywriter.h"
  13. #include "association.h"
  14. #include "attribute.h"
  15. #include "classifier.h"
  16. #include "debug_utils.h"
  17. #include "operation.h"
  18. #include "umldoc.h"
  19. #include "umlattributelist.h"
  20. #include <klocale.h>
  21. #include <kmessagebox.h>
  22. #include <QtCore/QFile>
  23. #include <QtCore/QTextStream>
  24. #include <QtCore/QRegExp>
  25. RubyWriter::RubyWriter()
  26. {
  27. }
  28. RubyWriter::~RubyWriter()
  29. {
  30. }
  31. void RubyWriter::writeClass(UMLClassifier *c)
  32. {
  33. if (!c) {
  34. uDebug()<<"Cannot write class of NULL concept!";
  35. return;
  36. }
  37. UMLClassifierList superclasses = c->getSuperClasses();
  38. UMLAssociationList aggregations = c->getAggregations();
  39. UMLAssociationList compositions = c->getCompositions();
  40. //find an appropriate name for our file
  41. fileName_ = findFileName(c, ".rb");
  42. if (fileName_.isEmpty()) {
  43. emit codeGenerated(c, false);
  44. return;
  45. }
  46. QFile fileh;
  47. if ( !openFile(fileh, fileName_) ) {
  48. emit codeGenerated(c, false);
  49. return;
  50. }
  51. QTextStream h(&fileh);
  52. className_ = cleanName(c->name());
  53. //////////////////////////////
  54. //Start generating the code!!
  55. /////////////////////////////
  56. //try to find a heading file (license, coments, etc)
  57. QString str;
  58. str = getHeadingFile(".rb");
  59. if (!str.isEmpty()) {
  60. str.replace(QRegExp("%filename%"), fileName_);
  61. str.replace(QRegExp("%filepath%"), fileh.fileName());
  62. h<<str<<m_endl;
  63. }
  64. if (forceDoc() || !c->doc().isEmpty()) {
  65. QString docStr = c->doc();
  66. docStr.replace(QRegExp("\\n"), "\n# ");
  67. docStr.remove("@ref ");
  68. docStr.replace("@see", "_See_");
  69. docStr.replace("@short", "_Summary_");
  70. docStr.replace("@author", "_Author_");
  71. h<<"#"<<m_endl;
  72. h<<"# "<<docStr<<m_endl;
  73. h<<"#"<<m_endl<<m_endl;
  74. }
  75. // write inheritances out
  76. UMLClassifier *concept;
  77. h<< "class " << cppToRubyType(className_) << (superclasses.count() > 0 ? " < ":"");
  78. int i = 0;
  79. foreach (concept , superclasses ) {
  80. if (i == 0) {
  81. h << cppToRubyType(concept->name()) << m_endl;
  82. } else {
  83. // Assume ruby modules that can be mixed in, after the first
  84. // superclass name in the list
  85. h << m_indentation << "include "<< cppToRubyType(concept->name()) << m_endl;
  86. }
  87. i++;
  88. }
  89. h << m_endl;
  90. // write comment for sub-section IF needed
  91. if (forceDoc() || c->hasAccessorMethods()) {
  92. h << m_indentation << "#" << m_endl;
  93. h << m_indentation << "# Accessor Methods" << m_endl;
  94. h << m_indentation << "#" << m_endl << m_endl;
  95. // Accessor methods for attributes
  96. writeAttributeMethods(c->getAttributeList(Uml::Visibility::Public), Uml::Visibility::Public, h);
  97. writeAttributeMethods(c->getAttributeList(Uml::Visibility::Protected), Uml::Visibility::Protected, h);
  98. writeAttributeMethods(c->getAttributeList(Uml::Visibility::Private), Uml::Visibility::Private, h);
  99. h << m_endl;
  100. }
  101. //operations
  102. writeOperations(c, h);
  103. //finish files
  104. h << "end" << m_endl << m_endl;
  105. //close files and notfiy we are done
  106. fileh.close();
  107. emit codeGenerated(c, true);
  108. }
  109. QString RubyWriter::cppToRubyType(const QString &typeStr)
  110. {
  111. QString type = cleanName(typeStr);
  112. type.remove("const ");
  113. type.remove(QRegExp("[*&\\s]"));
  114. type.replace(QRegExp("[<>]"), "_");
  115. type.replace("QStringList", "Array");
  116. type.replace("QString", "String");
  117. type.replace("bool", "true|false");
  118. type.replace(QRegExp("^(uint|int|ushort|short|ulong|long)$"), "Integer");
  119. type.replace(QRegExp("^(float|double)$"), "Float");
  120. type.replace(QRegExp("^Q(?=[A-Z])"), "Qt::");
  121. type.replace(QRegExp("^K(?!(DE|Parts|IO)"), "KDE::");
  122. return type;
  123. }
  124. QString RubyWriter::cppToRubyName(const QString &nameStr)
  125. {
  126. QString name = cleanName(nameStr);
  127. name.remove(QRegExp("^m_"));
  128. name.remove(QRegExp("^[pbn](?=[A-Z])"));
  129. name = name.mid(0, 1).toLower() + name.mid(1);
  130. return name;
  131. }
  132. void RubyWriter::writeOperations(UMLClassifier *c,QTextStream &h)
  133. {
  134. //Lists to store operations sorted by scope
  135. UMLOperationList oppub,opprot,oppriv;
  136. //sort operations by scope first and see if there are abstract methods
  137. UMLOperationList opl(c->getOpList());
  138. foreach (UMLOperation *op , opl ) {
  139. switch(op->visibility()) {
  140. case Uml::Visibility::Public:
  141. oppub.append(op);
  142. break;
  143. case Uml::Visibility::Protected:
  144. opprot.append(op);
  145. break;
  146. case Uml::Visibility::Private:
  147. oppriv.append(op);
  148. break;
  149. default:
  150. break;
  151. }
  152. }
  153. QString classname(cleanName(c->name()));
  154. //write operations to file
  155. if (forceSections() || !oppub.isEmpty()) {
  156. writeOperations(classname, oppub, Uml::Visibility::Public, h);
  157. }
  158. if (forceSections() || !opprot.isEmpty()) {
  159. writeOperations(classname, opprot, Uml::Visibility::Protected, h);
  160. }
  161. if (forceSections() || !oppriv.isEmpty()) {
  162. writeOperations(classname, oppriv, Uml::Visibility::Private, h);
  163. }
  164. }
  165. void RubyWriter::writeOperations(const QString &classname, const UMLOperationList &opList,
  166. Uml::Visibility permitScope, QTextStream &h)
  167. {
  168. // UMLOperation *op;
  169. // UMLAttribute *at;
  170. switch (permitScope) {
  171. case Uml::Visibility::Public:
  172. h << m_indentation << "public" << m_endl << m_endl;
  173. break;
  174. case Uml::Visibility::Protected:
  175. h << m_indentation << "protected" << m_endl << m_endl;
  176. break;
  177. case Uml::Visibility::Private:
  178. h << m_indentation << "private" << m_endl << m_endl;
  179. break;
  180. default:
  181. break;
  182. }
  183. foreach (const UMLOperation* op, opList) {
  184. QString methodName = cleanName(op->name());
  185. QStringList commentedParams;
  186. // Skip destructors, and operator methods which
  187. // can't be defined in ruby
  188. if ( methodName.startsWith('~')
  189. || methodName == "operator ="
  190. || methodName == "operator --"
  191. || methodName == "operator ++"
  192. || methodName == "operator !=" )
  193. {
  194. continue;
  195. }
  196. if (methodName == classname) {
  197. methodName = "initialize";
  198. }
  199. methodName.remove("operator ");
  200. methodName = methodName.mid(0, 1).toLower() + methodName.mid(1);
  201. UMLAttributeList atl = op->getParmList();
  202. //write method doc if we have doc || if at least one of the params has doc
  203. bool writeDoc = forceDoc() || !op->doc().isEmpty();
  204. // Always write out the docs for ruby as the type of the
  205. // arguments and return value of the methods is useful
  206. writeDoc = true;
  207. // for (UMLAttribute& at = atl.first(); at; at = atl.next())
  208. // writeDoc |= !at->getDoc().isEmpty();
  209. if (writeDoc) {
  210. h << m_indentation << "#" << m_endl;
  211. QString docStr = op->doc();
  212. docStr.replace(QRegExp("[\\n\\r]+ *"), m_endl);
  213. docStr.replace(QRegExp("[\\n\\r]+\\t*"), m_endl);
  214. docStr.replace(" m_", " ");
  215. docStr.replace(QRegExp("\\s[npb](?=[A-Z])"), " ");
  216. QRegExp re_params("@param (\\w)(\\w*)");
  217. int pos = re_params.indexIn(docStr);
  218. while (pos != -1) {
  219. docStr.replace( re_params.cap(0),
  220. QString("@param _") + re_params.cap(1).toLower() + re_params.cap(2) + '_' );
  221. commentedParams.append(re_params.cap(1).toLower() + re_params.cap(2));
  222. pos += re_params.matchedLength() + 3;
  223. pos = re_params.indexIn(docStr, pos);
  224. }
  225. docStr.replace('\n', QString("\n") + m_indentation + "# ");
  226. // Write parameter documentation
  227. foreach (const UMLAttribute& at , atl) {
  228. // Only write an individual @param entry if one hasn't been found already
  229. // in the main doc comment
  230. if (commentedParams.contains(cppToRubyName(at.name())) == 0) {
  231. docStr += (m_endl + m_indentation + "# @param _" + cppToRubyName(at.name()) + '_');
  232. if (at.doc().isEmpty()) {
  233. docStr += (' ' + cppToRubyType(at.getTypeName()));
  234. } else {
  235. docStr += (' ' + at.doc().replace(QRegExp("[\\n\\r]+[\\t ]*"), m_endl + " "));
  236. }
  237. }
  238. }
  239. docStr.remove("@ref ");
  240. docStr.replace("@param", "*");
  241. docStr.replace("@return", "* _returns_");
  242. // All lines after the first '# *' in the doc comment
  243. // must be indented correctly. If they aren't a list
  244. // item starting with '# *', then indent the text with
  245. // three spaces, '# ', to line up with the list item.
  246. pos = docStr.indexOf("# *");
  247. QRegExp re_linestart("# (?!\\*)");
  248. pos = re_linestart.indexIn(docStr, pos);
  249. while (pos > 0) {
  250. docStr.insert(pos + 1, " ");
  251. pos += re_linestart.matchedLength() + 2;
  252. pos = re_linestart.indexIn(docStr, pos);
  253. }
  254. h << m_indentation << "# "<< docStr << m_endl;
  255. QString typeStr = cppToRubyType(op->getTypeName());
  256. if (!typeStr.isEmpty() && typeStr != "void" && docStr.contains("_returns_") == 0) {
  257. h << m_indentation << "# * _returns_ " << typeStr << m_endl;
  258. }
  259. }
  260. h<< m_indentation << "def " + methodName << "(";
  261. int j=0;
  262. foreach (const UMLAttribute& at , atl) {
  263. QString nameStr = cppToRubyName(at.name());
  264. if (j > 0) {
  265. h << ", " << nameStr;
  266. } else {
  267. h << nameStr;
  268. }
  269. h << (!(at.getInitialValue().isEmpty()) ?
  270. (QString(" = ") + cppToRubyType(at.getInitialValue())) :
  271. QString(""));
  272. j++;
  273. }
  274. h <<")" << m_endl;
  275. // write body
  276. QString sourceCode = op->getSourceCode();
  277. if (sourceCode.isEmpty()) { // empty method body
  278. h << m_indentation << m_indentation << m_endl;
  279. }
  280. else {
  281. h << formatSourceCode(sourceCode, m_indentation + m_indentation);
  282. }
  283. h << m_indentation << "end" << m_endl << m_endl;
  284. }//end for
  285. }
  286. void RubyWriter::writeAttributeMethods(UMLAttributeList attribs,
  287. Uml::Visibility visibility, QTextStream &stream)
  288. {
  289. // return now if NO attributes to work on
  290. if (attribs.count() == 0 || visibility == Uml::Visibility::Private)
  291. return;
  292. UMLAttribute *at;
  293. foreach (at , attribs)
  294. {
  295. QString varName = cppToRubyName(cleanName(at->name()));
  296. writeSingleAttributeAccessorMethods(varName, at->doc(), stream);
  297. }
  298. }
  299. void RubyWriter::writeSingleAttributeAccessorMethods(
  300. const QString &fieldName,
  301. const QString &descr,
  302. QTextStream &h)
  303. {
  304. QString description = descr;
  305. description.remove(QRegExp("m_[npb](?=[A-Z])"));
  306. description.remove("m_");
  307. description.replace('\n', QString("\n") + m_indentation + "# ");
  308. if (!description.isEmpty()) {
  309. h << m_indentation << "# " << description << m_endl;
  310. }
  311. h << m_indentation << "attr_accessor :" << fieldName << m_endl << m_endl;
  312. return;
  313. }
  314. Uml::ProgrammingLanguage RubyWriter::language() const
  315. {
  316. return Uml::ProgrammingLanguage::Ruby;
  317. }
  318. QStringList RubyWriter::reservedKeywords() const
  319. {
  320. static QStringList keywords;
  321. if (keywords.isEmpty()) {
  322. keywords << "__FILE__"
  323. << "__LINE__"
  324. << "BEGIN"
  325. << "END"
  326. << "alias"
  327. << "and"
  328. << "begin"
  329. << "break"
  330. << "case"
  331. << "class"
  332. << "def"
  333. << "defined?"
  334. << "do"
  335. << "else"
  336. << "elsif"
  337. << "end"
  338. << "ensure"
  339. << "false"
  340. << "for"
  341. << "if"
  342. << "in"
  343. << "module"
  344. << "next"
  345. << "nil"
  346. << "not"
  347. << "or"
  348. << "redo"
  349. << "rescue"
  350. << "retry"
  351. << "return"
  352. << "self"
  353. << "super"
  354. << "then"
  355. << "true"
  356. << "undef"
  357. << "unless"
  358. << "until"
  359. << "when"
  360. << "while"
  361. << "yield";
  362. }
  363. return keywords;
  364. }
  365. #include "rubywriter.moc"