You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/umbrello/umbrello/codegenerators/rubycodeoperation.cpp

227 lines
8.5 KiB

/***************************************************************************
rubycodeoperation.cpp
Derived from the Java code generator by thomas
begin : Thur Jul 21 2005
author : Richard Dale
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
* copyright (C) 2006-2007 *
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
***************************************************************************/
// own header
#include "rubycodeoperation.h"
// qt/kde includes
#include <tqregexp.h>
// local includes
#include "rubyclassifiercodedocument.h"
#include "rubycodedocumentation.h"
#include "rubycodegenerator.h"
#include "../uml.h"
// Constructors/Destructors
//
RubyCodeOperation::RubyCodeOperation ( RubyClassifierCodeDocument * doc, UMLOperation *parent, const TQString & body, const TQString & comment )
: CodeOperation (doc, parent, body, comment)
{
// lets not go with the default comment and instead use
// full-blown ruby documentation object instead
setComment(new RubyCodeDocumentation(doc));
// these things never change..
setOverallIndentationLevel(1);
updateMethodDeclaration();
updateContent();
}
RubyCodeOperation::~RubyCodeOperation ( ) { }
// Other methods
//
// we basically want to update the doc and start text of this method
void RubyCodeOperation::updateMethodDeclaration()
{
CodeDocument * doc = getParentDocument();
RubyClassifierCodeDocument * rubydoc = dynamic_cast<RubyClassifierCodeDocument*>(doc);
UMLClassifier *c = rubydoc->getParentClassifier();
UMLOperation * o = getParentOperation();
bool isInterface = rubydoc->getParentClassifier()->isInterface();
TQString endLine = getNewLineEndingChars();
// now, the starting text.
TQString strVis = rubydoc->scopeToRubyDecl(o->getVisibility());
// no return type for constructors
TQString fixedReturn = RubyCodeGenerator::cppToRubyType(o->getTypeName());
TQString returnType = o->isConstructorOperation() ? TQString("") : (fixedReturn + TQString(" "));
TQString methodName = o->getName();
TQString RubyClassName = rubydoc->getRubyClassName(c->getName());
// Skip destructors, and operator methods which
// can't be defined in ruby
if ( methodName.startsWith("~")
|| TQRegExp("operator\\s*(=|--|\\+\\+|!=)$").exactMatch(methodName) )
{
getComment()->setText("");
return;
}
if (RubyClassName == methodName) {
methodName = "initialize";
}
methodName.replace(TQRegExp("operator\\s*"), "");
methodName = methodName.mid(0, 1).lower() + methodName.mid(1);
TQString paramStr = TQString("");
TQStringList commentedParams;
// assemble parameters
UMLAttributeList list = getParentOperation()->getParmList();
int nrofParam = list.count();
int paramNum = 0;
for(UMLAttribute* parm = list.first(); parm; parm = list.next())
{
TQString paramName = RubyCodeGenerator::cppToRubyName(parm->getName());
paramStr += paramName;
if (! parm->getInitialValue().isEmpty()) {
paramStr += TQString(" = ") + RubyCodeGenerator::cppToRubyType(parm->getInitialValue());
}
paramNum++;
if (paramNum != nrofParam )
paramStr += ", ";
}
TQString startText;
if (isInterface) {
// Assume 'isInterface' means a module in Ruby, so
// generate module methods
startText = "def "+ RubyClassName + '.' + methodName + '(' + paramStr +')';
} else {
startText = "def "+ methodName + '(' + paramStr +')';
}
startText += "";
setEndMethodText("end");
setStartMethodText(startText);
// Lastly, for text content generation, we fix the comment on the
// operation, IF the codeop is autogenerated & currently empty
TQString comment = o->getDoc();
if (comment.isEmpty()) {
if (getContentType() == CodeBlock::AutoGenerated) {
UMLAttributeList parameters = o->getParmList();
for(UMLAttributeListIt iterator(parameters); iterator.current(); ++iterator) {
comment += endLine + "* _" + iterator.current()->getName() + "_ ";
comment += (' ' + iterator.current()->getDoc().replace( TQRegExp("[\\n\\r]+[\\t ]*"),
endLine + " " ) );
}
// add a returns statement too
if(!returnType.isEmpty() && !TQRegExp("^void\\s*$").exactMatch(returnType))
comment += endLine + "* _returns_ " + returnType + ' ';
getComment()->setText(comment);
}
} else {
comment.replace(TQRegExp("[\\n\\r]+ *"), endLine);
comment.replace(TQRegExp("[\\n\\r]+\\t*"), endLine);
comment.replace(" m_", " ");
comment.replace(TQRegExp("\\s[npb](?=[A-Z])"), " ");
TQRegExp re_params("@param (\\w)(\\w*)");
int pos = re_params.search(comment);
while (pos != -1) {
comment.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(comment, pos);
}
UMLAttributeList parameters = o->getParmList();
for (UMLAttributeListIt iterator(parameters); iterator.current(); ++iterator) {
// Only write an individual @param entry if one hasn't been found already
// in the main doc comment
if (commentedParams.contains(RubyCodeGenerator::cppToRubyName(iterator.current()->getName())) == 0) {
comment += (endLine + "@param _" + RubyCodeGenerator::cppToRubyName(iterator.current()->getName()) + '_');
if (iterator.current()->getDoc().isEmpty()) {
comment += (' ' + RubyCodeGenerator::cppToRubyType(iterator.current()->getTypeName()));
} else {
comment += (' ' + iterator.current()->getDoc().replace(TQRegExp("[\\n\\r]+[\\t ]*"), endLine + " "));
}
}
}
comment.replace("@ref ", "");
comment.replace("@param", "*");
comment.replace("@return", "* _returns_");
// All lines after the first one starting with '*' in the doc comment
// must be indented correctly. If they aren't a list
// item starting with '*', then indent the text with
// two spaces, ' ', to line up with the list item.
pos = comment.find(endLine + '*');
if (pos != -1) {
pos += endLine.length() + 1;
pos = comment.find(endLine, pos);
}
while (pos > 0) {
pos += endLine.length();
if (comment[pos] != '*') {
comment.insert(pos, " ");
pos += 2;
}
pos = comment.find(endLine, pos);
}
TQString typeStr = RubyCodeGenerator::cppToRubyType(o->getTypeName());
if ( !typeStr.isEmpty()
&& !TQRegExp("^void\\s*$").exactMatch(typeStr)
&& comment.contains("_returns_") == 0 )
{
comment += endLine + "* _returns_ " + typeStr;
}
getComment()->setText(comment);
}
// In Java, for interfaces..we DONT write out non-public
// method declarations. And for Ruby modules?
if (isInterface) {
UMLOperation * o = getParentOperation();
if(o->getVisibility() != Uml::Visibility::Public)
setWriteOutText(false);
}
}
int RubyCodeOperation::lastEditableLine() {
ClassifierCodeDocument * doc = dynamic_cast<ClassifierCodeDocument*>(getParentDocument());
if(doc->parentIsInterface())
return -1; // very last line is NOT editable as its a one-line declaration w/ no body in
// an interface.
return 0;
}
#include "rubycodeoperation.moc"