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

584 lines
24 KiB

/***************************************************************************
* *
* 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) 2004-2007 *
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
***************************************************************************/
/* This code generated by:
* Author : thomas
* Date : Mon Jun 23 2003
*/
/**
We carve the Java document up into sections as follows:
* header
* package declaration
* import statements
* class declaration
* guts of the class (e.g. field decl, accessor methods, operations, dependant classes)
*/
// own header
#include "javaclassifiercodedocument.h"
// qt/kde includes
#include <kdebug.h>
#include <tqregexp.h>
// local includes
#include "javacodegenerator.h"
#include "javacodecomment.h"
#include "javaclassdeclarationblock.h"
#include "javacodeclassfielddeclarationblock.h"
#include "codegen_utils.h"
#include "../classifier.h"
#include "../codegenerationpolicy.h"
#include "../uml.h"
// Constructors/Destructors
//
JavaClassifierCodeDocument::JavaClassifierCodeDocument ( UMLClassifier * concept )
: ClassifierCodeDocument (concept) {
init();
}
JavaClassifierCodeDocument::~JavaClassifierCodeDocument ( ) { }
//
// Methods
//
// Accessor methods
//
// Make it easier on ourselves
JavaCodeGenerationPolicy * JavaClassifierCodeDocument::getJavaPolicy() {
CodeGenPolicyExt *pe = UMLApp::app()->getPolicyExt();
JavaCodeGenerationPolicy * policy = dynamic_cast<JavaCodeGenerationPolicy*>(pe);
return policy;
}
/**
* Get the dialog widget which allows user interaction with the object parameters.
* @return CodeDocumentDialog
*/
/*
CodeDocumentDialog JavaClassifierCodeDocument::getDialog ( ) {
}
*/
bool JavaClassifierCodeDocument::forceDoc () {
return UMLApp::app()->getCommonPolicy()->getCodeVerboseDocumentComments();
}
// We overwritten by Java language implementation to get lowercase path
TQString JavaClassifierCodeDocument::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 JavaClassifierCodeDocument::getJavaClassName (const TQString &name) {
return Codegen_Utils::capitalizeFirstLetter(CodeGenerator::cleanName(name));
}
// Initialize this java classifier code document
void JavaClassifierCodeDocument::init ( ) {
setFileExtension(".java");
//initCodeClassFields(); // this is dubious because it calls down to
// CodeGenFactory::newCodeClassField(this)
// but "this" is still in construction at that time.
classDeclCodeBlock = 0;
operationsBlock = 0;
constructorBlock = 0;
// this will call updateContent() as well as other things that sync our document.
synchronize();
}
/**
* @param op
*/
// in the vannilla version, we just tack all operations on the end
// of the document
bool JavaClassifierCodeDocument::addCodeOperation (CodeOperation * op ) {
if(!op->getParentOperation()->isLifeOperation())
return operationsBlock->addTextBlock(op);
else
return constructorBlock->addTextBlock(op);
}
// Sigh. NOT optimal. The only reason that we need to have this
// is so we can create the JavaClassDeclarationBlock.
// 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 "javaclassdeclarationblock"
void JavaClassifierCodeDocument::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 JavaCodeComment(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 JavaCodeOperation(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 == "javaclassdeclarationblock" )
{
JavaClassDeclarationBlock * block = getClassDecl();
block->loadFromXMI(element);
if(!addTextBlock(block))
{
kError()<<"Unable to add java 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 JavaClassifierCodeDocument::scopeToJavaDecl(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:
case Uml::Visibility::Implementation:
default:
scopeString = "private";
break;
}
return scopeString;
}
JavaClassDeclarationBlock * JavaClassifierCodeDocument::getClassDecl()
{
if(!classDeclCodeBlock)
{
classDeclCodeBlock = new JavaClassDeclarationBlock (this);
classDeclCodeBlock->setTag("ClassDeclBlock");
}
return classDeclCodeBlock;
}
void JavaClassifierCodeDocument::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 JavaClassifierCodeDocument::updateContent( )
{
// Gather info on the various fields and parent objects of this class...
UMLClassifier * c = getParentClassifier();
CodeGenerationPolicy * commonPolicy = UMLApp::app()->getCommonPolicy();
CodeGenPolicyExt * pe = UMLApp::app()->getPolicyExt();
JavaCodeGenerationPolicy * policy = dynamic_cast<JavaCodeGenerationPolicy*>(pe);
// 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(policy->getAutoGenerateAttribAccessors());
else
field->setWriteOutMethods(policy->getAutoGenerateAssocAccessors());
// attribute-based ClassFields
// we do it this way to have the static fields sorted out from regular ones
CodeClassFieldList staticAttribClassFields = getSpecificClassFields (CodeClassField::Attribute, true);
CodeClassFieldList attribClassFields = getSpecificClassFields (CodeClassField::Attribute, false);
// association-based ClassFields
// don't care if they are static or not..all are lumped together
CodeClassFieldList plainAssocClassFields = getSpecificClassFields ( CodeClassField::PlainAssociation );
CodeClassFieldList aggregationClassFields = getSpecificClassFields ( CodeClassField::Aggregation );
CodeClassFieldList compositionClassFields = getSpecificClassFields ( CodeClassField::Composition );
bool isInterface = parentIsInterface();
bool hasOperationMethods = c->getOpList().last() ? true : false;
TQString endLine = commonPolicy->getNewLineEndingChars(); // a shortcut..so we don't have to call this all the time
//
// START GENERATING CODE/TEXT BLOCKS and COMMENTS FOR THE DOCUMENT
//
//
// PACKAGE CODE BLOCK
//
TQString pkgs = getPackage();
pkgs.replace(TQRegExp("::"), ".");
TQString packageText = getPackage().isEmpty() ? "" : "package "+pkgs+';'+endLine;
CodeBlockWithComments * pblock = addOrUpdateTaggedCodeBlockWithComments("packages", packageText, "", 0, false);
if(packageText.isEmpty() && pblock->getContentType() == CodeBlock::AutoGenerated)
pblock->setWriteOutText(false);
else
pblock->setWriteOutText(true);
// IMPORT CODEBLOCK
//
// Q: Why all utils? Isnt just List and Vector the only classes we are using?
// A: doesn't matter at all; its more readable to just include '*' and java compilers
// don't slow down or anything. (TZ)
TQString importStatement = "";
if ( hasObjectVectorClassFields() )
importStatement.append("import java.util.*;");
//only import classes in a different package from this class
UMLPackageList imports;
TQMap<UMLPackage*, TQString> packageMap; // so we don't repeat packages
CodeGenerator::findObjectsRelated(c,imports);
for(UMLPackage *con = imports.first(); con ; con = imports.next())
// NO (default) datatypes in the import statement.. use defined
// ones whould be possible, but no idea how to do that...at least for now.
// Dynamic casting is slow..not an optimal way to do this.
if (!packageMap.contains(con) && con->getBaseType() != Uml::ot_Datatype)
{
packageMap.insert(con, con->getPackage());
// now, we DON'T need to import classes that are already in our own package
// (that is, IF a package is specified). Otherwise, we should have a declaration.
if (con->getPackage() != c->getPackage() ||
(c->getPackage().isEmpty() && con->getPackage().isEmpty()))
{
importStatement.append(endLine+"import ");
if(!con->getPackage().isEmpty())
importStatement.append(con->getPackage()+'.');
importStatement.append(CodeGenerator::cleanName(con->getName())+';');
}
}
// now, add/update the imports codeblock
CodeBlockWithComments * iblock = addOrUpdateTaggedCodeBlockWithComments("imports", importStatement, "", 0, false);
if(importStatement.isEmpty() && iblock->getContentType() == CodeBlock::AutoGenerated)
iblock->setWriteOutText(false);
else
iblock->setWriteOutText(true);
// CLASS DECLARATION BLOCK
//
// get the declaration block. If its not already present, add it too
JavaClassDeclarationBlock * myClassDeclCodeBlock = getClassDecl();
addTextBlock(myClassDeclCodeBlock); // note: wont add if already present
// 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 java classifier document
// header comment
// package code block
// import code block
// class declaration
// section:
// - class field declaration section comment
// - class field declarations (0+ codeblocks)
// 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 java 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).
//
// * CLASS FIELD declaration section
//
// get/create the field declaration code block
HierarchicalCodeBlock * fieldDeclBlock = myClassDeclCodeBlock->getHierarchicalCodeBlock("fieldsDecl", "Fields", 1);
// Update the comment: we only set comment to appear under the following conditions
CodeComment * fcomment = fieldDeclBlock->getComment();
if (isInterface || (!forceDoc() && !hasClassFields()) )
fcomment->setWriteOutText(false);
else
fcomment->setWriteOutText(true);
// now actually declare the fields within the appropriate HCodeBlock
declareClassFields(staticAttribClassFields, fieldDeclBlock);
declareClassFields(attribClassFields, fieldDeclBlock);
declareClassFields(plainAssocClassFields, fieldDeclBlock);
declareClassFields(aggregationClassFields, fieldDeclBlock);
declareClassFields(compositionClassFields, fieldDeclBlock);
//
// METHODS section
//
// get/create the method codeblock
HierarchicalCodeBlock * methodsBlock = myClassDeclCodeBlock->getHierarchicalCodeBlock("methodsBlock", "Methods", 1);
// Update the section comment
CodeComment * methodsComment = methodsBlock->getComment();
// set conditions for showing this comment
if (!forceDoc() && !hasClassFields() && !hasOperationMethods)
methodsComment->setWriteOutText(false);
else
methodsComment->setWriteOutText(true);
// METHODS sub-section : constructor methods
//
// get/create the constructor codeblock
HierarchicalCodeBlock * constBlock = methodsBlock->getHierarchicalCodeBlock("constructorMethods", "Constructors", 1);
constructorBlock = constBlock; // record this codeblock for later, when operations are updated
// special condiions for showing comment: only when autogenerateding empty constructors
// Although, we *should* check for other constructor methods too
CodeComment * constComment = constBlock->getComment();
CodeGenerationPolicy *pol = UMLApp::app()->getCommonPolicy();
if (!forceDoc() && (isInterface || !pol->getAutoGenerateConstructors()))
constComment->setWriteOutText(false);
else
constComment->setWriteOutText(true);
// add/get the empty constructor
TQString JavaClassName = getJavaClassName(c->getName());
TQString emptyConstStatement = "public "+JavaClassName+" ( ) { }";
CodeBlockWithComments * emptyConstBlock =
constBlock->addOrUpdateTaggedCodeBlockWithComments("emptyconstructor", emptyConstStatement, "Empty Constructor", 1, false);
// Now, as an additional condition we only show the empty constructor block
// IF it was desired to be shown
if(parentIsClass() && pol->getAutoGenerateConstructors())
emptyConstBlock->setWriteOutText(true);
else
emptyConstBlock->setWriteOutText(false);
// METHODS subsection : ACCESSOR METHODS
//
// get/create the accessor codeblock
HierarchicalCodeBlock * accessorBlock = methodsBlock->getHierarchicalCodeBlock("accessorMethods", "Accessor Methods", 1);
// set conditions for showing section comment
CodeComment * accessComment = accessorBlock->getComment();
if (!forceDoc() && !hasClassFields())
accessComment->setWriteOutText(false);
else
accessComment->setWriteOutText(true);
// now, 2 sub-sub sections in accessor block
// add/update accessor methods for attributes
HierarchicalCodeBlock * staticAccessors = accessorBlock->getHierarchicalCodeBlock("staticAccessorMethods", "", 1);
staticAccessors->getComment()->setWriteOutText(false); // never write block comment
staticAccessors->addCodeClassFieldMethods(staticAttribClassFields);
staticAccessors->addCodeClassFieldMethods(attribClassFields);
// add/update accessor methods for associations
HierarchicalCodeBlock * regularAccessors = accessorBlock->getHierarchicalCodeBlock("regularAccessorMethods", "", 1);
regularAccessors->getComment()->setWriteOutText(false); // never write block comment
regularAccessors->addCodeClassFieldMethods(plainAssocClassFields);
regularAccessors->addCodeClassFieldMethods(aggregationClassFields);
regularAccessors->addCodeClassFieldMethods(compositionClassFields);
// METHODS subsection : Operation methods (which arent constructors)
//
// get/create the operations codeblock
operationsBlock = methodsBlock->getHierarchicalCodeBlock("operationMethods", "Operations", 1);
// set conditions for showing section comment
CodeComment * ocomment = operationsBlock->getComment();
if (!forceDoc() && !hasOperationMethods )
ocomment->setWriteOutText(false);
else
ocomment->setWriteOutText(true);
}
#include "javaclassifiercodedocument.moc"