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

618 lines
19 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-2006 *
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
***************************************************************************/
/* This code generated by:
* Author : thomas
* Date : Fri Jun 20 2003
*/
// own header
#include "codeclassfield.h"
// qt/kde includes
#include <tqregexp.h>
#include <kdebug.h>
// app includes
#include "association.h"
#include "classifiercodedocument.h"
#include "codegenerator.h"
#include "attribute.h"
#include "umlobject.h"
#include "umlrole.h"
#include "uml.h"
#include "codegenerators/codegenfactory.h"
// Constructors/Destructors
//
CodeClassField::CodeClassField ( ClassifierCodeDocument * doc , UMLRole * role)
: CodeParameter ( doc , (UMLObject*) role)
{
setParentUMLObject(role);
initFields(true);
}
CodeClassField::CodeClassField ( ClassifierCodeDocument * doc , UMLAttribute * attrib)
: CodeParameter ( doc , (UMLObject*) attrib )
{
setParentUMLObject(attrib);
initFields(true);
}
CodeClassField::~CodeClassField ( ) {
// remove methods from parent document
CodeAccessorMethodList list = m_methodVector;
for(CodeAccessorMethod * m = list.first(); m ; m=list.next())
{
getParentDocument()->removeTextBlock(m);
m->forceRelease();
}
list.clear();
// clear the decl block from parent text block list too
if(m_declCodeBlock)
{
getParentDocument()->removeTextBlock(m_declCodeBlock);
m_declCodeBlock->forceRelease();
delete m_declCodeBlock;
}
}
//
// Methods
//
// Accessor methods
//
void CodeClassField::setParentUMLObject (UMLObject * obj) {
UMLRole *role = dynamic_cast<UMLRole*>(obj);
if(role) {
UMLAssociation * parentAssoc = role->getParentAssociation();
Uml::Association_Type atype = parentAssoc->getAssocType();
m_parentIsAttribute = false;
if ( atype == Uml::at_Association || atype == Uml::at_Association_Self)
m_classFieldType = PlainAssociation; // Plain == Self + untyped associations
else if (atype == Uml::at_Aggregation)
m_classFieldType = Aggregation;
else if (atype == Uml::at_Composition)
m_classFieldType = Composition;
} else {
m_classFieldType = Attribute;
m_parentIsAttribute = true;
}
}
// Public attribute accessor methods
//
TQString CodeClassField::getTypeName ( ) {
if (parentIsAttribute())
{
UMLAttribute * at = (UMLAttribute*) getParentObject();
return at->getTypeName();
} else {
UMLRole * role = (UMLRole*) getParentObject();
if(fieldIsSingleValue()) {
return getUMLObjectName(role->getObject());
} else {
return role->getName();
}
}
}
// get the type of object that will be added/removed from lists
// of objects (as per specification of associations)
TQString CodeClassField::getListObjectType() {
TQString type = TQString ("");
if (!parentIsAttribute())
{
UMLRole * role = dynamic_cast<UMLRole*>(getParentObject());
type = getUMLObjectName(role->getObject());
}
return type;
}
/**
* Get the value of m_isAbstract
* @return the value of m_isAbstract
*/
bool CodeClassField::parentIsAttribute ( ) {
return m_parentIsAttribute;
// return (m_classFieldType == Attribute) ? true : false;
}
/**
* Get the type of classfield this is.
*/
CodeClassField::ClassFieldType CodeClassField::getClassFieldType() {
return m_classFieldType;
}
/**
* Get the value of m_dialog
* @return the value of m_dialog
*/
/*
CodeClassFieldDialog * CodeClassField::getDialog ( ) {
return m_dialog;
}
*/
// methods like this _shouldn't_ be needed IF we properly did things thruought the code.
TQString CodeClassField::getUMLObjectName(UMLObject *obj)
{
return (obj!=0)?obj->getName():TQString("NULL");
}
/**
* Add a Method object to the m_methodVector List
*/
bool CodeClassField::addMethod ( CodeAccessorMethod * add_object ) {
CodeAccessorMethod::AccessorType type = add_object->getType();
if(findMethodByType(type))
return false;
/*
// this wont work as the key for TQMap needs to inherit from TQObject
if(m_methodMap->contains(type))
return false; // return false, we already have some object with this tag in the list
else
m_methodMap->insert(type, add_object);
*/
m_methodVector.append(add_object);
return true;
}
/**
* Remove a Method object from m_methodVector List
*/
bool CodeClassField::removeMethod ( CodeAccessorMethod * remove_object ) {
// m_methodMap->erase(remove_object->getType());
m_methodVector.removeRef(remove_object);
getParentDocument()->removeTextBlock(remove_object);
return true;
}
/**
* Get the list of Method objects held by m_methodVector
* @return TQPtrList<CodeMethodBlock *> list of Method objects held by
* m_methodVector
*/
CodeAccessorMethodList CodeClassField::getMethodList() {
return m_methodVector;
}
/** determine if we will *allow* methods to be viewable.
* this flag is often used to toggle autogeneration of accessor
* methods in the code class field.
*/
bool CodeClassField::getWriteOutMethods ()
{
return m_writeOutMethods;
}
void CodeClassField::setWriteOutMethods ( bool val )
{
m_writeOutMethods = val;
updateContent();
}
/**
* return the declaration statement for this class field object.
* will be empty until this (abstract) class is inherited in elsewhere.
*/
CodeClassFieldDeclarationBlock * CodeClassField::getDeclarationCodeBlock( )
{
return m_declCodeBlock;
}
// Other methods
//
/**
* load params from the appropriate XMI element node.
*/
void CodeClassField::loadFromXMI ( TQDomElement & root ) {
setAttributesFromNode(root);
}
/** set attributes of the node that represents this class
* in the XMI document.
*/
void CodeClassField::setAttributesOnNode ( TQDomDocument & doc, TQDomElement & cfElem)
{
// super class
CodeParameter::setAttributesOnNode(doc,cfElem);
// now set local attributes/fields
cfElem.setAttribute("field_type",m_classFieldType);
cfElem.setAttribute("listClassName",m_listClassName);
cfElem.setAttribute("writeOutMethods",getWriteOutMethods()?"true":"false");
// record tag on declaration codeblock
// which we will store in its own separate child node block
m_declCodeBlock->saveToXMI(doc, cfElem);
// now record the tags on our accessormethods
CodeAccessorMethod *method;
for (CodeAccessorMethodListIt it(m_methodVector); (method = it.current()) != NULL; ++it)
{
method->saveToXMI(doc,cfElem);
}
}
/** set the class attributes of this object from
* the passed element node.
*/
void CodeClassField::setAttributesFromNode ( TQDomElement & root) {
// always disconnect
getParentObject()->disconnect(this);
// superclass call.. may reset the parent object
CodeParameter::setAttributesFromNode(root);
// make AFTER super-class call. This will reconnect to the parent
// and re-check we have all needed child accessor methods and decl blocks
initFields( );
setWriteOutMethods(root.attribute("writeOutMethods","true") == "true" ? true : false);
m_listClassName = root.attribute("listClassName","");
m_classFieldType = (ClassFieldType) root.attribute("field_type","0").toInt();
// load accessor methods now
// by looking for our particular child element
TQDomNode node = root.firstChild();
TQDomElement element = node.toElement();
while( !element.isNull() ) {
TQString tag = element.tagName();
if( tag == "ccfdeclarationcodeblock" ) {
m_declCodeBlock->loadFromXMI(element);
} else
if( tag == "codeaccessormethod" ) {
int type = element.attribute("accessType","0").toInt();
int role_id = element.attribute("role_id","-1").toInt();
CodeAccessorMethod * method = findMethodByType((CodeAccessorMethod::AccessorType) type, role_id);
if(method)
method->loadFromXMI(element);
else
kError()<<"Cant load code accessor method for type:"<<type<<" which doesn't exist in this codeclassfield. Is XMI out-dated or corrupt?"<<endl;
} else
if( tag == "header" ) {
// this is treated in parent.. skip over here
} else
kWarning()<<"ERROR: bad savefile? code classfield loadFromXMI got child element with unknown tag:"<<tag<<" ignoring node."<<endl;
node = element.nextSibling();
element = node.toElement();
}
}
/**
* Save the XMI representation of this object
*/
void CodeClassField::saveToXMI ( TQDomDocument & doc, TQDomElement & root ) {
TQDomElement docElement = doc.createElement( "codeclassfield" );
setAttributesOnNode(doc, docElement);
root.appendChild( docElement );
}
int CodeClassField::minimumListOccurances( ) {
if (!parentIsAttribute())
{
UMLRole * role = dynamic_cast<UMLRole*>(getParentObject());
TQString multi = role->getMultiplicity();
// ush. IF we had a multiplicty object, this would be much easier.
if(!multi.isEmpty())
{
TQString lowerBoundString = multi.remove(TQRegExp("\\.\\.\\d+$"));
if(!lowerBoundString.isEmpty() &&lowerBoundString.contains(TQRegExp("^\\d+$")))
return lowerBoundString.toInt();
}
}
return 0;
}
int CodeClassField::maximumListOccurances( ) {
if (!parentIsAttribute())
{
UMLRole * role = dynamic_cast<UMLRole*>(getParentObject());
TQString multi = role->getMultiplicity();
// ush. IF we had a multiplicty object, this would be much easier.
if(!multi.isEmpty())
{
TQString upperBoundString = multi.section(TQRegExp("(\\.\\.)"),1);
if(!upperBoundString.isEmpty() && upperBoundString.contains(TQRegExp("^\\d+$")))
return upperBoundString.toInt();
else
return -1; // unbounded
} else
return -1; // unbounded
}
return 1;
}
TQString CodeClassField::cleanName ( const TQString &name ) {
return getParentDocument()->cleanName(name);
}
TQString CodeClassField::fixInitialStringDeclValue(const TQString& val, const TQString &type)
{
TQString value = val;
// check for strings only<F2>String value = val;
if (!value.isEmpty() && type == "String") {
if (!value.startsWith("\""))
value.prepend("\"");
if (!value.endsWith("\""))
value.append("\"");
}
return value;
}
void CodeClassField::synchronize ()
{
updateContent();
CodeAccessorMethod *method;
for (CodeAccessorMethodListIt it(m_methodVector); (method = it.current()) != NULL; ++it)
method->syncToParent();
if(m_declCodeBlock)
m_declCodeBlock->syncToParent();
}
CodeAccessorMethod * CodeClassField::findMethodByType ( CodeAccessorMethod::AccessorType type, int role_id)
{
//if we already know to which file this class was written/should be written, just return it.
/*
// argh. this wont work because "accessorType' doesn't inherit from TQObject.
if(m_methodMap->contains(type))
return ((*m_methodMap)[type]);
CodeAccessorMethod * obj = NULL;
*/
if(role_id > 1 || role_id < 0)
{
for (CodeAccessorMethod * m = m_methodVector.first(); m ; m= m_methodVector.next())
if( m->getType() == type)
return m;
} else {
// ugh. forced into this underperforming algorithm because of bad association
// design.
for (CodeAccessorMethod * m = m_methodVector.first(); m ; m= m_methodVector.next())
{
UMLRole * role = dynamic_cast<UMLRole*>(m->getParentObject());
if(!role)
kError()<<" FindMethodByType() cant create role for method type:"<<m->getType()<<endl;
if( role && m->getType() == type && role->getRole() == role_id)
return m;
}
}
return (CodeAccessorMethod *) NULL;
}
void CodeClassField::initAccessorMethods()
{
// everything gets potential get/set method
//if(!m_methodMap->contains(CodeAccessorMethod::GET))
if(!findMethodByType(CodeAccessorMethod::GET))
{
CodeAccessorMethod * method = CodeGenFactory::newCodeAccessorMethod (getParentDocument(), this, CodeAccessorMethod::GET);
if(method)
{
method->setType(CodeAccessorMethod::GET);
addMethod(method);
}
}
if(!findMethodByType(CodeAccessorMethod::SET))
{
CodeAccessorMethod * method = CodeGenFactory::newCodeAccessorMethod (getParentDocument(), this, CodeAccessorMethod::SET);
if(method) {
method->setType(CodeAccessorMethod::SET);
addMethod(method);
}
}
// add in the add,remove and list methods for things which are role based.
// (and only used if the role specifies a 'list' type object
if (!parentIsAttribute()) {
if(!findMethodByType(CodeAccessorMethod::ADD))
{
CodeAccessorMethod * method = CodeGenFactory::newCodeAccessorMethod (getParentDocument(), this, CodeAccessorMethod::ADD);
if(method) {
method->setType(CodeAccessorMethod::ADD);
addMethod(method);
}
}
if(!findMethodByType(CodeAccessorMethod::REMOVE))
{
CodeAccessorMethod * method = CodeGenFactory::newCodeAccessorMethod (getParentDocument(), this, CodeAccessorMethod::REMOVE);
if(method) {
method->setType(CodeAccessorMethod::REMOVE);
addMethod(method);
}
}
if(!findMethodByType(CodeAccessorMethod::LIST))
{
CodeAccessorMethod * method = CodeGenFactory::newCodeAccessorMethod (getParentDocument(), this, CodeAccessorMethod::LIST);
if(method) {
method->setType(CodeAccessorMethod::LIST);
addMethod(method);
}
}
}
}
void CodeClassField::updateContent()
{
// Set properties for writing out the various methods derived from UMLRoles.
// I suppose this could be supported under individual accessor method synctoparent
// calls, but its going to happen again and again for many languages. Why not a catch
// all here? -b.t.
if (parentIsAttribute())
{
for ( CodeAccessorMethod *method = m_methodVector.first(); method;
method = m_methodVector.next() )
method->setWriteOutText( m_writeOutMethods );
return;
}
UMLRole * role = dynamic_cast<UMLRole*>(getParentObject());
Uml::Changeability_Type changeType = role->getChangeability();
bool isSingleValue = fieldIsSingleValue();
bool isEmptyRole = role->getName().isEmpty() ? true : false;
for (CodeAccessorMethod * method = m_methodVector.first(); method; method=m_methodVector.next())
{
CodeAccessorMethod::AccessorType type = method->getType();
// for role-based accessors, we DON'T write ourselves out when
// the name of the role is not defined OR when the global flag
// to not show ANY methods is set.
if(!m_writeOutMethods || isEmptyRole)
{
method->setWriteOutText(false);
continue;
}
// not to change if no tag (don't know what it is, OR its not an AutoGenerated method
if(method->getContentType() != CodeBlock::AutoGenerated)
continue;
// first off, some accessor methods wont appear if its a singleValue
// role and vice-versa
if(isSingleValue)
{
switch(type) {
case CodeAccessorMethod::SET:
// SET method true ONLY IF changeability is NOT Frozen
if (changeType != Uml::chg_Frozen)
method->setWriteOutText(true);
else
method->setWriteOutText(false);
break;
case CodeAccessorMethod::GET:
method->setWriteOutText(true);
break;
case CodeAccessorMethod::ADD:
case CodeAccessorMethod::REMOVE:
case CodeAccessorMethod::LIST:
default: // list/add/remove always false
method->setWriteOutText(false);
break;
}
}
else
{
switch(type) {
// get/set always false
case CodeAccessorMethod::GET:
case CodeAccessorMethod::SET:
method->setWriteOutText(false);
break;
case CodeAccessorMethod::ADD:
// ADD method true ONLY IF changeability is NOT Frozen
if (changeType != Uml::chg_Frozen)
method->setWriteOutText(true);
else
method->setWriteOutText(false);
break;
case CodeAccessorMethod::REMOVE:
// Remove methods ONLY IF changeability is Changeable
if (changeType == Uml::chg_Changeable)
method->setWriteOutText(true);
else
method->setWriteOutText(false);
break;
case CodeAccessorMethod::LIST:
default:
method->setWriteOutText(true);
break;
}
}
}
}
// determine whether the parent object in this classfield indicates that it is
// a single variable or a List (Vector). One day this will be done correctly with special
// multiplicity object that we don't have to figure out what it means via regex.
bool CodeClassField::fieldIsSingleValue ( )
{
// For the time being, all attributes ARE single values (yes,
// I know this isnt always true, but we have to start somewhere.)
if(parentIsAttribute())
return true;
UMLRole * role = dynamic_cast<UMLRole*>(getParentObject());
if(!role)
return true; // its really an attribute
TQString multi = role->getMultiplicity();
if(multi.isEmpty() || multi.contains(TQRegExp("^(0|1)$"))
|| multi.contains(TQRegExp("^0\\.\\.1$")))
return true;
return false;
}
void CodeClassField::initFields(bool inConstructor) {
m_writeOutMethods = false;
m_listClassName = TQString ("");
m_declCodeBlock = NULL;
m_methodVector.setAutoDelete(false);
// m_methodMap = new TQMap<CodeAccessorMethod::AccessorType, CodeAccessorMethod *>;
if (!inConstructor)
finishInitialization();
}
void CodeClassField::finishInitialization() {
m_declCodeBlock = CodeGenFactory::newDeclarationCodeBlock(getParentDocument(), this);
initAccessorMethods();
updateContent();
connect(getParentObject(),TQ_SIGNAL(modified()),this,TQ_SIGNAL(modified())); // child objects will trigger off this signal
}
#include "codeclassfield.moc"