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/rubyclassifiercodedocument.cpp

647 lines
28 KiB

/***************************************************************************
rubyclassifiercodedocument.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> *
***************************************************************************/
/**
We carve the Ruby document up into sections as follows:
* header
* class declaration
* guts of the class (e.g. accessor methods, operations, dependant classes)
*/
// own header
#include "rubyclassifiercodedocument.h"
// qt/kde includes
#include <tqregexp.h>
#include <kdebug.h>
// local includes
#include "rubycodegenerator.h"
#include "rubycodecomment.h"
#include "rubyclassdeclarationblock.h"
#include "rubycodeclassfielddeclarationblock.h"
#include "rubycodeoperation.h"
#include "codegen_utils.h"
#include "../classifier.h"
#include "../uml.h"
// Constructors/Destructors
//
RubyClassifierCodeDocument::RubyClassifierCodeDocument ( UMLClassifier * concept )
: ClassifierCodeDocument (concept) {
init();
}
RubyClassifierCodeDocument::~RubyClassifierCodeDocument ( ) { }
//
// Methods
//
// Accessor methods
//
// Make it easier on ourselves
RubyCodeGenerationPolicy * RubyClassifierCodeDocument::getRubyPolicy() {
CodeGenPolicyExt *pe = UMLApp::app()->getPolicyExt();
RubyCodeGenerationPolicy * policy = dynamic_cast<RubyCodeGenerationPolicy*>(pe);
return policy;
}
/**
* Get the dialog widget which allows user interaction with the object parameters.
* @return CodeDocumentDialog
*/
/*
CodeDocumentDialog RubyClassifierCodeDocument::getDialog ( ) {
}
*/
// We overwritten by Ruby language implementation to get lowercase path
TQString RubyClassifierCodeDocument::getPath ( )
{
TQString path = getPackage();
// Replace all white spaces with blanks
path.simplifyWhiteSpace();
// Replace all blanks with underscore
path.replace(TQRegExp(" "), "_");
path.replace(TQRegExp("\\."),"/");
path.replace(TQRegExp("::"), "/");
path.lower();
return path;
}
// Other methods
//
TQString RubyClassifierCodeDocument::getRubyClassName (const TQString &name) {
CodeGenerator *g = UMLApp::app()->getGenerator();
return Codegen_Utils::capitalizeFirstLetter(g->cleanName(name));
}
// Initialize this ruby classifier code document
void RubyClassifierCodeDocument::init ( ) {
setFileExtension(".rb");
//initCodeClassFields(); // this is dubious because it calls down to
// CodeGenFactory::newCodeClassField(this)
// but "this" is still in construction at that time.
classDeclCodeBlock = 0;
publicBlock = 0;
protectedBlock = 0;
privateBlock = 0;
pubConstructorBlock = 0;
protConstructorBlock = 0;
privConstructorBlock = 0;
pubOperationsBlock = 0;
privOperationsBlock = 0;
protOperationsBlock = 0;
// this will call updateContent() as well as other things that sync our document.
synchronize();
}
/**
* @param op
*/
// in the vanilla version, we just tack all operations on the end
// of the document
bool RubyClassifierCodeDocument::addCodeOperation (CodeOperation * op )
{
Uml::Visibility scope = op->getParentOperation()->getVisibility();
if(!op->getParentOperation()->isConstructorOperation())
{
switch (scope) {
default:
case Uml::Visibility::Public:
return pubOperationsBlock->addTextBlock(op);
break;
case Uml::Visibility::Protected:
return protOperationsBlock->addTextBlock(op);
break;
case Uml::Visibility::Private:
return privOperationsBlock->addTextBlock(op);
break;
}
} else {
switch (scope) {
default:
case Uml::Visibility::Public:
return pubConstructorBlock->addTextBlock(op);
break;
case Uml::Visibility::Protected:
return protConstructorBlock->addTextBlock(op);
break;
case Uml::Visibility::Private:
return privConstructorBlock->addTextBlock(op);
break;
}
}
}
// Sigh. NOT optimal. The only reason that we need to have this
// is so we can create the RubyClassDeclarationBlock.
// would be better if we could create a handler interface that each
// codeblock used so all we have to do here is add the handler
// for "rubyclassdeclarationblock"
void RubyClassifierCodeDocument::loadChildTextBlocksFromNode ( TQDomElement & root)
{
TQDomNode tnode = root.firstChild();
TQDomElement telement = tnode.toElement();
bool loadCheckForChildrenOK = false;
while( !telement.isNull() ) {
TQString nodeName = telement.tagName();
if( nodeName == "textblocks" ) {
TQDomNode node = telement.firstChild();
TQDomElement element = node.toElement();
// if there is nothing to begin with, then we don't worry about it
loadCheckForChildrenOK = element.isNull() ? true : false;
while( !element.isNull() ) {
TQString name = element.tagName();
if( name == "codecomment" ) {
CodeComment * block = new RubyCodeComment(this);
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"loadFromXMI : unable to add codeComment to :"<<this<<endl;
block->deleteLater();
} else
loadCheckForChildrenOK= true;
} else
if( name == "codeaccessormethod" ||
name == "ccfdeclarationcodeblock"
) {
TQString acctag = element.attribute("tag","");
// search for our method in the
TextBlock * tb = findCodeClassFieldTextBlockByTag(acctag);
if(!tb || !addTextBlock(tb))
{
kError()<<"loadFromXMI : unable to add codeclassfield child method to:"<<this<<endl;
// DON'T delete
} else
loadCheckForChildrenOK= true;
} else
if( name == "codeblock" ) {
CodeBlock * block = newCodeBlock();
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"loadFromXMI : unable to add codeBlock to :"<<this<<endl;
block->deleteLater();
} else
loadCheckForChildrenOK= true;
} else
if( name == "codeblockwithcomments" ) {
CodeBlockWithComments * block = newCodeBlockWithComments();
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"loadFromXMI : unable to add codeBlockwithcomments to:"<<this<<endl;
block->deleteLater();
} else
loadCheckForChildrenOK= true;
} else
if( name == "header" ) {
// do nothing.. this is treated elsewhere
} else
if( name == "hierarchicalcodeblock" ) {
HierarchicalCodeBlock * block = newHierarchicalCodeBlock();
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"Unable to add hierarchicalcodeBlock to:"<<this<<endl;
block->deleteLater();
} else
loadCheckForChildrenOK= true;
} else
if( name == "codeoperation" ) {
// find the code operation by id
TQString id = element.attribute("parent_id","-1");
UMLObject * obj = UMLApp::app()->getDocument()->findObjectById(STR2ID(id));
UMLOperation * op = dynamic_cast<UMLOperation*>(obj);
if(op) {
CodeOperation * block = new RubyCodeOperation(this, op);
block->loadFromXMI(element);
if(addTextBlock(block))
loadCheckForChildrenOK= true;
else
{
kError()<<"Unable to add codeoperation to:"<<this<<endl;
block->deleteLater();
}
} else
kError()<<"Unable to find operation create codeoperation for:"<<this<<endl;
} else
if( name == "rubyclassdeclarationblock" )
{
RubyClassDeclarationBlock * block = getClassDecl();
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"Unable to add ruby code declaration block to:"<<this<<endl;
// DON'T delete.
// block->deleteLater();
} else
loadCheckForChildrenOK= true;
}
// This last item is only needed for extreme debugging conditions
// (E.g. making new codeclassdocument loader)
// else
// kDebug()<<" LoadFromXMI: Got strange tag in text block stack:"<<name<<", ignorning"<<endl;
node = element.nextSibling();
element = node.toElement();
}
break;
}
tnode = telement.nextSibling();
telement = tnode.toElement();
}
if(!loadCheckForChildrenOK)
{
CodeDocument * test = dynamic_cast<CodeDocument*>(this);
if(test)
{
kWarning()<<" loadChildBlocks : unable to initialize any child blocks in doc: "<<test->getFileName()<<" "<<this<<endl;
} else {
HierarchicalCodeBlock * hb = dynamic_cast<HierarchicalCodeBlock*>(this);
if(hb)
kWarning()<<" loadChildBlocks : unable to initialize any child blocks in Hblock: "<<hb->getTag()<<" "<<this<<endl;
else
kDebug()<<" loadChildBlocks : unable to initialize any child blocks in UNKNOWN OBJ:"<<this<<endl;
}
}
}
TQString RubyClassifierCodeDocument::scopeToRubyDecl(Uml::Visibility scope)
{
TQString scopeString;
switch(scope)
{
case Uml::Visibility::Public:
scopeString = "public";
break;
case Uml::Visibility::Protected:
scopeString = "protected";
break;
case Uml::Visibility::Private:
default:
scopeString = "private";
break;
}
return scopeString;
}
RubyClassDeclarationBlock * RubyClassifierCodeDocument::getClassDecl()
{
if(!classDeclCodeBlock)
{
classDeclCodeBlock = new RubyClassDeclarationBlock (this);
classDeclCodeBlock->setTag("ClassDeclBlock");
}
return classDeclCodeBlock;
}
void RubyClassifierCodeDocument::resetTextBlocks()
{
// all special pointers to text blocks need to be zero'd out
operationsBlock = 0;
constructorBlock = 0;
classDeclCodeBlock = 0;
// now do traditional release of text blocks.
ClassifierCodeDocument::resetTextBlocks();
}
// This method will cause the class to rebuild its text representation.
// based on the parent classifier object.
// For any situation in which this is called, we are either building the code
// document up, or replacing/regenerating the existing auto-generated parts. As
// such, we will want to insert everything we resonablely will want
// during creation. We can set various parts of the document (esp. the
// comments) to appear or not, as needed.
void RubyClassifierCodeDocument::updateContent( )
{
// Gather info on the various fields and parent objects of this class...
UMLClassifier * c = getParentClassifier();
RubyCodeGenerator * gen = dynamic_cast<RubyCodeGenerator*>(UMLApp::app()->getGenerator());
// first, set the global flag on whether or not to show classfield info
// This depends on whether or not we have attribute/association classes
CodeClassFieldList * cfList = getCodeClassFieldList();
for(CodeClassField * field = cfList->first(); field; field = cfList->next())
if(field->parentIsAttribute())
field->setWriteOutMethods(gen->getAutoGenerateAttribAccessors());
else
field->setWriteOutMethods(gen->getAutoGenerateAssocAccessors());
// attribute-based ClassFields
// we do it this way to have the static fields sorted out from regular ones
CodeClassFieldList staticPublicAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, true, Uml::Visibility::Public );
CodeClassFieldList publicAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, false, Uml::Visibility::Public );
CodeClassFieldList staticProtectedAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, true, Uml::Visibility::Protected );
CodeClassFieldList protectedAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, false, Uml::Visibility::Protected );
CodeClassFieldList staticPrivateAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, true, Uml::Visibility::Private );
CodeClassFieldList privateAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, false, Uml::Visibility::Private);
// association-based ClassFields
// don't care if they are static or not..all are lumped together
CodeClassFieldList publicPlainAssocClassFields = getSpecificClassFields ( CodeClassField::PlainAssociation , Uml::Visibility::Public);
CodeClassFieldList publicAggregationClassFields = getSpecificClassFields ( CodeClassField::Aggregation, Uml::Visibility::Public);
CodeClassFieldList publicCompositionClassFields = getSpecificClassFields ( CodeClassField::Composition, Uml::Visibility::Public );
CodeClassFieldList protPlainAssocClassFields = getSpecificClassFields ( CodeClassField::PlainAssociation , Uml::Visibility::Protected);
CodeClassFieldList protAggregationClassFields = getSpecificClassFields ( CodeClassField::Aggregation, Uml::Visibility::Protected);
CodeClassFieldList protCompositionClassFields = getSpecificClassFields ( CodeClassField::Composition, Uml::Visibility::Protected);
CodeClassFieldList privPlainAssocClassFields = getSpecificClassFields ( CodeClassField::PlainAssociation , Uml::Visibility::Private);
CodeClassFieldList privAggregationClassFields = getSpecificClassFields ( CodeClassField::Aggregation, Uml::Visibility::Private);
CodeClassFieldList privCompositionClassFields = getSpecificClassFields ( CodeClassField::Composition, Uml::Visibility::Private);
bool isInterface = parentIsInterface();
bool hasOperationMethods = c->getOpList().last() ? true : false;
CodeGenerationPolicy *pol = UMLApp::app()->getCommonPolicy();
TQString endLine = pol->getNewLineEndingChars(); // a shortcut..so we don't have to call this all the time
//
// START GENERATING CODE/TEXT BLOCKS and COMMENTS FOR THE DOCUMENT
//
// CLASS DECLARATION BLOCK
//
// get the declaration block. If its not already present, add it too
RubyClassDeclarationBlock * myClassDeclCodeBlock = getClassDecl();
addTextBlock(myClassDeclCodeBlock); // note: wont add if already present
// declare public, protected and private methods, attributes (fields).
// set the start text ONLY if this is the first time we created the objects.
bool createdPublicBlock = publicBlock == 0 ? true : false;
publicBlock = myClassDeclCodeBlock->getHierarchicalCodeBlock("publicBlock","Public Items",0);
if (createdPublicBlock)
publicBlock->setStartText("public");
bool createdProtBlock = protectedBlock == 0 ? true : false;
protectedBlock = myClassDeclCodeBlock->getHierarchicalCodeBlock("protectedBlock","Protected Items",0);
if(createdProtBlock)
protectedBlock->setStartText("protected");
bool createdPrivBlock = privateBlock == 0 ? true : false;
privateBlock = myClassDeclCodeBlock->getHierarchicalCodeBlock("privateBlock","Private Items",0);
if(createdPrivBlock)
privateBlock->setStartText("private");
// NOW create document in sections..
// now we want to populate the body of our class
// our layout is the following general groupings of code blocks:
// start ruby classifier document
// header comment
// class declaration
// section:
// section:
// - methods section comment
// sub-section: constructor ops
// - constructor method section comment
// - constructor methods (0+ codeblocks)
// sub-section: accessors
// - accessor method section comment
// - static accessor methods (0+ codeblocks)
// - non-static accessor methods (0+ codeblocks)
// sub-section: non-constructor ops
// - operation method section comment
// - operations (0+ codeblocks)
// end class declaration
// end ruby classifier document
// Q: Why use the more complicated scheme of arranging code blocks within codeblocks?
// A: This will allow us later to preserve the format of our document so that if
// codeblocks are added, they may be easily added in the correct place, rather than at
// the end of the document, or by using a difficult algorithm to find the location of
// the last appropriate code block sibling (which may not exist.. for example user adds
// a constructor operation, but there currently are no constructor code blocks
// within the document).
//
// METHODS section
//
// get/create the method codeblock
// public methods
HierarchicalCodeBlock * pubMethodsBlock = publicBlock->getHierarchicalCodeBlock("pubMethodsBlock", "", 1);
CodeComment * pubMethodsComment = pubMethodsBlock->getComment();
bool forceDoc = pol->getCodeVerboseDocumentComments();
// set conditions for showing this comment
if (!forceDoc && !hasClassFields() && !hasOperationMethods)
pubMethodsComment->setWriteOutText(false);
else
pubMethodsComment->setWriteOutText(true);
// protected methods
HierarchicalCodeBlock * protMethodsBlock = protectedBlock->getHierarchicalCodeBlock("protMethodsBlock", "", 1);
CodeComment * protMethodsComment = protMethodsBlock->getComment();
// set conditions for showing this comment
if (!forceDoc && !hasClassFields() && !hasOperationMethods)
protMethodsComment->setWriteOutText(false);
else
protMethodsComment->setWriteOutText(true);
// private methods
HierarchicalCodeBlock * privMethodsBlock = privateBlock->getHierarchicalCodeBlock("privMethodsBlock", "", 1);
CodeComment * privMethodsComment = privMethodsBlock->getComment();
// set conditions for showing this comment
if (!forceDoc && !hasClassFields() && !hasOperationMethods)
privMethodsComment->setWriteOutText(false);
else
privMethodsComment->setWriteOutText(true);
// METHODS sub-section : constructor methods
//
// public
pubConstructorBlock = pubMethodsBlock->getHierarchicalCodeBlock("constructionMethods", "Constructors", 1);
// special condiions for showing comment: only when autogenerateding empty constructors
// Although, we *should* check for other constructor methods too
CodeComment * pubConstComment = pubConstructorBlock->getComment();
if (!forceDoc && (isInterface || !pol->getAutoGenerateConstructors()))
pubConstComment->setWriteOutText(false);
else
pubConstComment->setWriteOutText(true);
// protected
protConstructorBlock = protMethodsBlock->getHierarchicalCodeBlock("constructionMethods", "Constructors", 1);
// special condiions for showing comment: only when autogenerateding empty constructors
// Although, we *should* check for other constructor methods too
CodeComment * protConstComment = protConstructorBlock->getComment();
if (!forceDoc && (isInterface || !pol->getAutoGenerateConstructors()))
protConstComment->setWriteOutText(false);
else
protConstComment->setWriteOutText(true);
// private
privConstructorBlock = privMethodsBlock->getHierarchicalCodeBlock("constructionMethods", "Constructors", 1);
// special condiions for showing comment: only when autogenerateding empty constructors
// Although, we *should* check for other constructor methods too
CodeComment * privConstComment = privConstructorBlock->getComment();
if (!forceDoc && (isInterface || !pol->getAutoGenerateConstructors()))
privConstComment->setWriteOutText(false);
else
privConstComment->setWriteOutText(true);
// get/create the accessor codeblock
// public
HierarchicalCodeBlock * pubAccessorBlock = pubMethodsBlock->getHierarchicalCodeBlock("accessorMethods", "Accessor Methods", 1);
// set conditions for showing section comment
CodeComment * pubAccessComment = pubAccessorBlock->getComment();
if (!forceDoc && !hasClassFields())
pubAccessComment->setWriteOutText(false);
else
pubAccessComment->setWriteOutText(true);
// protected
HierarchicalCodeBlock * protAccessorBlock = protMethodsBlock->getHierarchicalCodeBlock("accessorMethods", "Accessor Methods", 1);
// set conditions for showing section comment
CodeComment * protAccessComment = protAccessorBlock->getComment();
if (!forceDoc && !hasClassFields())
protAccessComment->setWriteOutText(false);
else
protAccessComment->setWriteOutText(true);
// private
HierarchicalCodeBlock * privAccessorBlock = privMethodsBlock->getHierarchicalCodeBlock("accessorMethods", "Accessor Methods", 1);
// set conditions for showing section comment
CodeComment * privAccessComment = privAccessorBlock->getComment();
if (!forceDoc && !hasClassFields())
privAccessComment->setWriteOutText(false);
else
privAccessComment->setWriteOutText(true);
// now, 2 sub-sub sections in accessor block
// add/update accessor methods for attributes
HierarchicalCodeBlock * pubStaticAccessors = pubAccessorBlock->getHierarchicalCodeBlock("pubStaticAccessorMethods", "", 1);
HierarchicalCodeBlock * pubRegularAccessors = pubAccessorBlock->getHierarchicalCodeBlock("pubRegularAccessorMethods", "", 1);
pubStaticAccessors->getComment()->setWriteOutText(false); // never write block comment
pubRegularAccessors->getComment()->setWriteOutText(false); // never write block comment
HierarchicalCodeBlock * protStaticAccessors = protAccessorBlock->getHierarchicalCodeBlock("protStaticAccessorMethods", "", 1);
HierarchicalCodeBlock * protRegularAccessors = protAccessorBlock->getHierarchicalCodeBlock("protRegularAccessorMethods", "", 1);
protStaticAccessors->getComment()->setWriteOutText(false); // never write block comment
protRegularAccessors->getComment()->setWriteOutText(false); // never write block comment
HierarchicalCodeBlock * privStaticAccessors = privAccessorBlock->getHierarchicalCodeBlock("privStaticAccessorMethods", "", 1);
HierarchicalCodeBlock * privRegularAccessors = privAccessorBlock->getHierarchicalCodeBlock("privRegularAccessorMethods", "", 1);
privStaticAccessors->getComment()->setWriteOutText(false); // never write block comment
privRegularAccessors->getComment()->setWriteOutText(false); // never write block comment
// now add in accessors as appropriate
// public stuff
pubStaticAccessors->addCodeClassFieldMethods(staticPublicAttribClassFields);
pubRegularAccessors->addCodeClassFieldMethods(publicAttribClassFields);
pubRegularAccessors->addCodeClassFieldMethods(publicPlainAssocClassFields);
pubRegularAccessors->addCodeClassFieldMethods(publicAggregationClassFields);
pubRegularAccessors->addCodeClassFieldMethods(publicCompositionClassFields);
// protected stuff
protStaticAccessors->addCodeClassFieldMethods(staticProtectedAttribClassFields);
protRegularAccessors->addCodeClassFieldMethods(protectedAttribClassFields);
protRegularAccessors->addCodeClassFieldMethods(protPlainAssocClassFields);
protRegularAccessors->addCodeClassFieldMethods(protAggregationClassFields);
protRegularAccessors->addCodeClassFieldMethods(protCompositionClassFields);
// private stuff
privStaticAccessors->addCodeClassFieldMethods(staticPrivateAttribClassFields);
privRegularAccessors->addCodeClassFieldMethods(privateAttribClassFields);
privRegularAccessors->addCodeClassFieldMethods(privPlainAssocClassFields);
privRegularAccessors->addCodeClassFieldMethods(privAggregationClassFields);
privRegularAccessors->addCodeClassFieldMethods(privCompositionClassFields);
// METHODS subsection : Operation methods (which aren't constructors)
//
// setup/get/create the operations codeblock
// public
pubOperationsBlock = pubMethodsBlock->getHierarchicalCodeBlock("operationMethods", "Operations", 1);
// set conditions for showing section comment
CodeComment * pubOcomment = pubOperationsBlock->getComment();
if (!forceDoc && !hasOperationMethods )
pubOcomment->setWriteOutText(false);
else
pubOcomment->setWriteOutText(true);
//protected
protOperationsBlock = protMethodsBlock->getHierarchicalCodeBlock("operationMethods", "Operations", 1);
// set conditions for showing section comment
CodeComment * protOcomment = protOperationsBlock->getComment();
if (!forceDoc && !hasOperationMethods )
protOcomment->setWriteOutText(false);
else
protOcomment->setWriteOutText(true);
//private
privOperationsBlock = privMethodsBlock->getHierarchicalCodeBlock("operationMethods", "Operations", 1);
// set conditions for showing section comment
CodeComment * privOcomment = privOperationsBlock->getComment();
if (!forceDoc && !hasOperationMethods )
privOcomment->setWriteOutText(false);
else
privOcomment->setWriteOutText(true);
}
#include "rubyclassifiercodedocument.moc"