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

743 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 : Thu Jun 19 2003
*/
// own header
#include "classifiercodedocument.h"
// qt/kde includes
#include <kdebug.h>
#include <tqregexp.h>
// local includes
#include "association.h"
#include "attribute.h"
#include "operation.h"
#include "classifierlistitem.h"
#include "classifier.h"
#include "codegenerator.h"
#include "uml.h"
#include "umldoc.h"
#include "umlrole.h"
#include "umlattributelist.h"
#include "umloperationlist.h"
#include "codegenerators/codegenfactory.h"
// Constructors/Destructors
//
ClassifierCodeDocument::ClassifierCodeDocument ( UMLClassifier * parent )
{
init (parent);
}
ClassifierCodeDocument::~ClassifierCodeDocument ( )
{
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
delete cf;
}
m_classfieldVector.clear();
}
//
// Methods
//
// Accessor methods
//
/** get a list of codeclassifier objects held by this classifiercodedocument that meet the passed criteria.
*/
CodeClassFieldList ClassifierCodeDocument::getSpecificClassFields (CodeClassField::ClassFieldType cfType)
{
CodeClassFieldList list;
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if (cf->getClassFieldType() == cfType)
list.append(cf);
}
return list;
}
/** get a list of codeclassifier objects held by this classifiercodedocument that meet the passed criteria.
*/
CodeClassFieldList ClassifierCodeDocument::getSpecificClassFields (CodeClassField::ClassFieldType cfType, bool isStatic)
{
CodeClassFieldList list;
list.setAutoDelete(false);
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if (cf->getClassFieldType() == cfType && cf->getStatic() == isStatic)
list.append(cf);
}
return list;
}
/** get a list of codeclassifier objects held by this classifiercodedocument that meet the passed criteria.
*/
CodeClassFieldList ClassifierCodeDocument::getSpecificClassFields (CodeClassField::ClassFieldType cfType, Uml::Visibility visibility)
{
CodeClassFieldList list;
list.setAutoDelete(false);
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if (cf->getClassFieldType() == cfType && cf->getVisibility() == visibility)
list.append(cf);
}
return list;
}
/** get a list of codeclassifier objects held by this classifiercodedocument that meet the passed criteria.
*/
CodeClassFieldList ClassifierCodeDocument::getSpecificClassFields (CodeClassField::ClassFieldType cfType, bool isStatic, Uml::Visibility visibility)
{
CodeClassFieldList list;
list.setAutoDelete(false);
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if (cf->getClassFieldType() == cfType && cf->getVisibility() == visibility && cf->getStatic() == isStatic )
list.append(cf);
}
return list;
}
// do we have accessor methods for lists of objects?
// (as opposed to lists of primitive types like 'int' or 'float', etc)
bool ClassifierCodeDocument::hasObjectVectorClassFields() {
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if(cf->getClassFieldType() != CodeClassField::Attribute)
{
UMLRole * role = dynamic_cast<UMLRole*>(cf->getParentObject());
TQString multi = role->getMultiplicity();
if (
multi.contains(TQRegExp("[23456789\\*]")) ||
multi.contains(TQRegExp("1\\d"))
)
return true;
}
}
return false;
}
bool ClassifierCodeDocument::hasClassFields() {
if(m_classfieldVector.count() > 0 )
return true;
return false;
}
/**
* Tell if one or more codeclassfields are derived from associations.
*/
bool ClassifierCodeDocument::hasAssociationClassFields() {
CodeClassFieldList list = getSpecificClassFields(CodeClassField::Attribute);
return (m_classfieldVector.count() - list.count()) > 0 ? true : false;
}
/**
* Tell if one or more codeclassfields are derived from attributes.
*/
bool ClassifierCodeDocument::hasAttributeClassFields() {
CodeClassFieldList list = getSpecificClassFields(CodeClassField::Attribute);
return list.count() > 0 ? true : false;
}
/**
* Add a CodeClassField object to the m_classfieldVector List
* @return boolean true if successful in adding
*/
// We DON'T add methods of the code classfield here because we need to allow
// the codegenerator writer the liberty to organize their document as they desire.
bool ClassifierCodeDocument::addCodeClassField ( CodeClassField * add_object ) {
UMLObject * umlobj = add_object->getParentObject();
if(!(m_classFieldMap.contains(umlobj)))
{
m_classfieldVector.append(add_object);
m_classFieldMap.insert(umlobj,add_object);
return true;
}
return false;
}
// this is a slot..should only be called from a signal
void ClassifierCodeDocument::addAttributeClassField (UMLClassifierListItem *obj, bool syncToParentIfAdded) {
UMLAttribute *at = (UMLAttribute*)obj;
CodeClassField * cf = CodeGenFactory::newCodeClassField(this, at);
if(cf)
if (addCodeClassField(cf) && syncToParentIfAdded)
updateContent();
}
/**
* Remove a CodeClassField object from m_classfieldVector List
*/
bool ClassifierCodeDocument::removeCodeClassField ( CodeClassField * remove_object ) {
UMLObject * umlobj = remove_object->getParentObject();
if(m_classFieldMap.contains(umlobj))
{
if (m_classfieldVector.removeRef(remove_object))
{
// remove from our classfield map
m_classFieldMap.remove(umlobj);
delete remove_object;
return true;
}
}
return false;
}
void ClassifierCodeDocument::removeAttributeClassField(UMLClassifierListItem *obj)
{
CodeClassField * remove_object = m_classFieldMap[obj];
if(remove_object)
removeCodeClassField(remove_object);
}
void ClassifierCodeDocument::removeAssociationClassField (UMLAssociation *assoc )
{
// the object could be either (or both!) role a or b. We should check
// both parts of the association.
CodeClassField * remove_object = m_classFieldMap[assoc->getUMLRole(Uml::A)];
if(remove_object)
removeCodeClassField(remove_object);
// check role b
remove_object = m_classFieldMap[assoc->getUMLRole(Uml::B)];
if(remove_object)
removeCodeClassField(remove_object);
}
/**
* Get the list of CodeClassField objects held by m_classfieldVector
* @return CodeClassFieldList list of CodeClassField objects held by
* m_classfieldVector
*/
CodeClassFieldList * ClassifierCodeDocument::getCodeClassFieldList ( ) {
return &m_classfieldVector;
}
/**
* Get the value of m_parentclassifier
* @return the value of m_parentclassifier
*/
UMLClassifier * ClassifierCodeDocument::getParentClassifier ( ) {
return m_parentclassifier;
}
/**
* @return TQPtrList<CodeOperation>
*/
TQPtrList<CodeOperation> ClassifierCodeDocument::getCodeOperations ( ) {
TQPtrList<CodeOperation> list;
list.setAutoDelete(false);
TextBlockList * tlist = getTextBlockList();
for (TextBlock *tb = tlist->first(); tb; tb=tlist->next())
{
CodeOperation * cop = dynamic_cast<CodeOperation*>(tb);
if(cop)
list.append(cop);
}
return list;
}
/**
* @param o The Operation to add
*/
void ClassifierCodeDocument::addOperation (UMLClassifierListItem * o) {
UMLOperation *op = dynamic_cast<UMLOperation*>(o);
if (op == NULL) {
kError() << "ClassifierCodeDocument::addOperation: arg is not a UMLOperation"
<< endl;
}
TQString tag = CodeOperation::findTag(op);
CodeOperation * codeOp = dynamic_cast<CodeOperation*>(findTextBlockByTag(tag, true));
bool createdNew = false;
// create the block, if it doesn't already exist
if(!codeOp)
{
codeOp = CodeGenFactory::newCodeOperation(this, op);
createdNew = true;
}
// now try to add it. This may fail because it (or a block with
// the same tag) is already in the document somewhere. IF we
// created this new, then we need to delete our object.
if(!addCodeOperation(codeOp)) // wont add if already present
if(createdNew)
delete codeOp;
}
/**
* @param op
*/
void ClassifierCodeDocument::removeOperation (UMLClassifierListItem * op ) {
TQString tag = CodeOperation::findTag((UMLOperation*)op);
TextBlock *tb = findTextBlockByTag(tag, true);
if(tb)
{
if(removeTextBlock(tb)) // wont add if already present
delete tb; // delete unused operations
else
kError()<<"Cant remove CodeOperation from ClassCodeDocument!"<<endl;
}
else
kError()<<"Cant Find codeOperation for deleted operation!"<<endl;
}
// Other methods
//
void ClassifierCodeDocument::addCodeClassFieldMethods(CodeClassFieldList &list )
{
for (CodeClassFieldListIt ccflit(list); ccflit.current(); ++ccflit)
{
CodeClassField * field = ccflit.current();
CodeAccessorMethodList list = field->getMethodList();
CodeAccessorMethod * method;
for (CodeAccessorMethodListIt it(list); (method = it.current()) != NULL; ++it)
{
/*
TQString tag = method->getTag();
if(tag.isEmpty())
{
tag = getUniqueTag();
method->setTag(tag);
}
*/
addTextBlock(method); // wont add if already exists in document, will add a tag if missing;
}
}
}
// add declaration blocks for the passed classfields
void ClassifierCodeDocument::declareClassFields (CodeClassFieldList & list ,
CodeGenObjectWithTextBlocks * parent )
{
for (CodeClassFieldListIt ccflit(list); ccflit.current(); ++ccflit)
{
CodeClassField * field = ccflit.current();
CodeClassFieldDeclarationBlock * declBlock = field->getDeclarationCodeBlock();
/*
// if it has a tag, check
if(!declBlock->getTag().isEmpty())
{
// In C++, because we may shift the declaration to a different parent
// block for a change in scope, we need to track down any pre-existing
// location, and remove FIRST before adding to new parent
CodeGenObjectWithTextBlocks * oldParent = findParentObjectForTaggedTextBlock (declBlock->getTag());
if(oldParent) {
if(oldParent != parent)
oldParent->removeTextBlock(declBlock);
}
}
*/
parent->addTextBlock(declBlock); // wont add it IF its already present. Will give it a tag if missing
}
}
bool ClassifierCodeDocument::parentIsClass() {
return (m_parentclassifier->getBaseType() == Uml::ot_Class);
}
bool ClassifierCodeDocument::parentIsInterface() {
return (m_parentclassifier->getBaseType() == Uml::ot_Interface);
}
/**
* Init from a UMLClassifier object.
* @param classifier
* @param package
*/
void ClassifierCodeDocument::init (UMLClassifier * c )
{
m_parentclassifier = c;
m_classfieldVector.setAutoDelete(false);
updateHeader();
syncNamesToParent();
// initCodeClassFields(); // cant call here?..newCodeClassField is pure virtual
// slots
if (parentIsClass()) {
connect(c,TQ_SIGNAL(attributeAdded(UMLClassifierListItem*)),this,TQ_SLOT(addAttributeClassField(UMLClassifierListItem*)));
connect(c,TQ_SIGNAL(attributeRemoved(UMLClassifierListItem*)),this,TQ_SLOT(removeAttributeClassField(UMLClassifierListItem*)));
}
connect(c,TQ_SIGNAL(sigAssociationEndAdded(UMLAssociation*)),this,TQ_SLOT(addAssociationClassField(UMLAssociation*)));
connect(c,TQ_SIGNAL(sigAssociationEndRemoved(UMLAssociation*)),this,TQ_SLOT(removeAssociationClassField(UMLAssociation*)));
connect(c,TQ_SIGNAL(operationAdded(UMLClassifierListItem*)),this,TQ_SLOT(addOperation(UMLClassifierListItem*)));
connect(c,TQ_SIGNAL(operationRemoved(UMLClassifierListItem*)),this,TQ_SLOT(removeOperation(UMLClassifierListItem*)));
connect(c,TQ_SIGNAL(modified()),this,TQ_SLOT(syncToParent()));
}
// IF the classifier object is modified, this will get called.
// @todo we cannot make this virtual as long as the
// ClassifierCodeDocument constructor calls it because that gives
// a call-before-construction error.
// Example of the problem: CPPSourceCodeDocument reimplementing syncNamesToParent()
// CPPCodeGenerator::initFromParentDocument()
// CodeDocument * codeDoc = new CPPSourceCodeDocument(c);
// CPPSourceCodeDocument::CPPSourceCodeDocument(UMLClassifier * concept)
// : ClassifierCodeDocument(concept)
// ClassifierCodeDocument::ClassifierCodeDocument(concept)
// init(concept);
// syncNamesToParent();
// dispatches to CPPSourceCodeDocument::syncNamesToParent()
// but that object is not yet constructed.
//
void ClassifierCodeDocument::syncNamesToParent( ) {
TQString fileName = CodeGenerator::cleanName(getParentClassifier()->getName());
if (!UMLApp::app()->activeLanguageIsCaseSensitive()) {
// @todo let the user decide about mixed case file names (codegen setup menu)
fileName = fileName.lower();
}
setFileName(fileName);
setPackage(m_parentclassifier->getUMLPackage());
}
void ClassifierCodeDocument::synchronize( ) {
updateHeader(); // doing this insures time/date stamp is at the time of this call
syncNamesToParent();
updateContent();
syncClassFields();
updateOperations();
}
void ClassifierCodeDocument::syncClassFields( )
{
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
cf->synchronize();
}
}
void ClassifierCodeDocument::updateOperations( ) {
UMLOperationList opList(getParentClassifier()->getOpList());
for (UMLOperation *op = opList.first(); op; op = opList.next())
{
TQString tag = CodeOperation::findTag(op);
CodeOperation * codeOp = dynamic_cast<CodeOperation*>(findTextBlockByTag(tag, true));
bool createdNew = false;
if(!codeOp)
{
codeOp = CodeGenFactory::newCodeOperation(this, op);
createdNew = true;
}
// now try to add it. This may fail because it (or a block with
// the same tag) is already in the document somewhere. IF we
// created this new, then we need to delete our object.
if(!addCodeOperation(codeOp)) // wont add if already present
if(createdNew)
delete codeOp;
// synchronize all non-new operations
if(!createdNew)
codeOp->syncToParent();
}
}
void ClassifierCodeDocument::syncToParent( ) {
synchronize();
}
/**
* add codeclassfields to this classifiercodedocument. IF a codeclassfield
* already exists, it is not added.
*/
void ClassifierCodeDocument::initCodeClassFields ( ) {
UMLClassifier * c = getParentClassifier();
// first, do the code classifields that arise from attributes
if (parentIsClass()) {
UMLAttributeList alist = c->getAttributeList();
for(UMLAttribute * at = alist.first(); at; at = alist.next())
{
CodeClassField * field = CodeGenFactory::newCodeClassField(this, at);
addCodeClassField(field);
}
}
// now, do the code classifields that arise from associations
UMLAssociationList ap = c->getSpecificAssocs(Uml::at_Association);
UMLAssociationList ag = c->getAggregations();
UMLAssociationList ac = c->getCompositions();
UMLAssociationList selfAssoc = c->getSpecificAssocs(Uml::at_Association_Self);
updateAssociationClassFields(ap);
updateAssociationClassFields(ag);
updateAssociationClassFields(ac);
updateAssociationClassFields(selfAssoc);
}
void ClassifierCodeDocument::updateAssociationClassFields ( UMLAssociationList &assocList )
{
CodeClassFieldList list;
for(UMLAssociation * a=assocList.first(); a; a=assocList.next())
addAssociationClassField(a, false); // syncToParent later
}
void ClassifierCodeDocument::addAssociationClassField (UMLAssociation * a, bool syncToParentIfAdded)
{
Uml::IDType cid = getParentClassifier()->getID(); // so we know who 'we' are
bool printRoleA = false, printRoleB = false, shouldSync = false;
// it may seem counter intuitive, but you want to insert the role of the
// *other* class into *this* class.
if (a->getObjectId(Uml::A) == cid)
printRoleB = true;
if (a->getObjectId(Uml::B) == cid)
printRoleA = true;
// grab RoleB decl
if (printRoleB)
{
UMLRole * role = a->getUMLRole(Uml::B);
if(!m_classFieldMap.contains((UMLObject*)role))
{
CodeClassField * classfield = CodeGenFactory::newCodeClassField(this, role);
if( addCodeClassField(classfield))
shouldSync = true;
}
}
// print RoleA decl
if (printRoleA)
{
UMLRole * role = a->getUMLRole(Uml::A);
if(!m_classFieldMap.contains((UMLObject*)role))
{
CodeClassField * classfield = CodeGenFactory::newCodeClassField(this, role);
if( addCodeClassField(classfield))
shouldSync = true;
}
}
if (shouldSync && syncToParentIfAdded)
syncToParent(); // needed for a slot add
}
/** set the class attributes of this object from
* the passed element node.
*/
void ClassifierCodeDocument::setAttributesFromNode ( TQDomElement & elem )
{
// NOTE: we DON'T set the parent here as we ONLY get to this point
// IF the parent codegenerator could find a matching parent classifier
// that already has a code document.
// We FIRST set code class field stuff..check re-linnking with
// accessor methods by looking for our particular child element
TQDomNode node = elem.firstChild();
TQDomElement childElem = node.toElement();
while( !childElem.isNull() ) {
TQString tag = childElem.tagName();
if( tag == "classfields" ) {
// load classfields
loadClassFieldsFromXMI(childElem);
break;
}
node = childElem.nextSibling();
childElem= node.toElement();
}
// call super-class after. THis will populate the text blocks (incl
// the code accessor methods above) as is appropriate
CodeDocument::setAttributesFromNode(elem);
}
// look at all classfields currently in document.. match up
// by parent object ID and Role ID (needed for self-association CF's)
CodeClassField *
ClassifierCodeDocument::findCodeClassFieldFromParentID (Uml::IDType id,
int role_id)
{
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
if(role_id == -1) { // attribute-based
if (STR2ID(cf->getID()) == id)
return cf;
} else { // association(role)-based
const Uml::Role_Type r = (Uml::Role_Type)role_id;
UMLRole * role = dynamic_cast<UMLRole *>(cf->getParentObject());
if(role && STR2ID(cf->getID()) == id && role->getRole() == r)
return cf;
}
}
// shouldn't happen..
kError() << "Failed to find codeclassfield for parent uml id:"
<< ID2STR(id) << " (role id:" << role_id
<< ") Do you have a corrupt classifier code document?"
<< endl;
return (CodeClassField*) NULL; // not found
}
void ClassifierCodeDocument::loadClassFieldsFromXMI( TQDomElement & elem) {
TQDomNode node = elem.firstChild();
TQDomElement childElem = node.toElement();
while( !childElem.isNull() ) {
TQString nodeName = childElem.tagName();
if( nodeName == "codeclassfield")
{
TQString id = childElem.attribute("parent_id","-1");
int role_id = childElem.attribute("role_id","-1").toInt();
CodeClassField * cf = findCodeClassFieldFromParentID(STR2ID(id), role_id);
if(cf)
{
// Because we just may change the parent object here,
// we need to yank it from the map of umlobjects
m_classFieldMap.remove(cf->getParentObject());
// configure from XMI
cf->loadFromXMI(childElem);
// now add back in
m_classFieldMap.insert(cf->getParentObject(),cf);
} else
kError()<<" LoadFromXMI: can't load classfield parent_id:"<<id<<" do you have a corrupt savefile?"<<endl;
}
node = childElem.nextSibling();
childElem= node.toElement();
}
}
/**
* Save the XMI representation of this object
*/
void ClassifierCodeDocument::saveToXMI ( TQDomDocument & doc, TQDomElement & root ) {
#if 0
// avoid the creation of primitive data type
TQString strType;
if (getParentClassifier()->getBaseType() == Uml::ot_Datatype) {
strType = getParentClassifier()->getName();
// lets get the default code generator to check if it is a primitive data type
// there's a reason to create files for int/boolean and so ?
if (getParentGenerator()->isReservedKeyword(strType))
return;
}
#endif
TQDomElement docElement = doc.createElement( "classifiercodedocument" );
setAttributesOnNode(doc, docElement);
root.appendChild( docElement );
}
/**
* load params from the appropriate XMI element node.
*/
void ClassifierCodeDocument::loadFromXMI ( TQDomElement & root ) {
// set attributes/fields
setAttributesFromNode(root);
// now sync our doc, needed?
// synchronize();
}
/** set attributes of the node that represents this class
* in the XMI document.
*/
void ClassifierCodeDocument::setAttributesOnNode ( TQDomDocument & doc, TQDomElement & docElement)
{
// do super-class first
CodeDocument::setAttributesOnNode(doc, docElement);
// cache local attributes/fields
docElement.setAttribute("parent_class", ID2STR(getParentClassifier()->getID()));
// (code) class fields
// which we will store in its own separate child node block
TQDomElement fieldsElement = doc.createElement( "classfields" );
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * field = ccflit.current();
field->saveToXMI(doc, fieldsElement);
}
docElement.appendChild( fieldsElement);
}
TextBlock * ClassifierCodeDocument::findCodeClassFieldTextBlockByTag (const TQString &tag)
{
for (CodeClassFieldListIt ccflit(m_classfieldVector); ccflit.current(); ++ccflit)
{
CodeClassField * cf = ccflit.current();
CodeClassFieldDeclarationBlock * decl = cf->getDeclarationCodeBlock();
if(decl && decl->getTag() == tag)
return decl;
// well, if not in the decl block, then in the methods perhaps?
CodeAccessorMethodList mlist = cf->getMethodList();
CodeAccessorMethod *m;
for (CodeAccessorMethodListIt it(mlist); (m = it.current()) != NULL; ++it)
if(m->getTag() == tag)
return m;
}
// if we get here, we failed.
return (TextBlock*) NULL;
}
#include "classifiercodedocument.moc"