/*************************************************************************** * * * 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 * ***************************************************************************/ /* This code generated by: * Author : thomas * Date : Thu Jun 19 2003 */ // own header #include "classifiercodedocument.h" // qt/kde includes #include #include // 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(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 */ TQPtrList ClassifierCodeDocument::getCodeOperations ( ) { TQPtrList list; list.setAutoDelete(false); TextBlockList * tlist = getTextBlockList(); for (TextBlock *tb = tlist->first(); tb; tb=tlist->next()) { CodeOperation * cop = dynamic_cast(tb); if(cop) list.append(cop); } return list; } /** * @param o The Operation to add */ void ClassifierCodeDocument::addOperation (UMLClassifierListItem * o) { UMLOperation *op = dynamic_cast(o); if (op == NULL) { kError() << "ClassifierCodeDocument::addOperation: arg is not a UMLOperation" << endl; } TQString tag = CodeOperation::findTag(op); CodeOperation * codeOp = dynamic_cast(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!"<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,TQT_SIGNAL(attributeAdded(UMLClassifierListItem*)),this,TQT_SLOT(addAttributeClassField(UMLClassifierListItem*))); connect(c,TQT_SIGNAL(attributeRemoved(UMLClassifierListItem*)),this,TQT_SLOT(removeAttributeClassField(UMLClassifierListItem*))); } connect(c,TQT_SIGNAL(sigAssociationEndAdded(UMLAssociation*)),this,TQT_SLOT(addAssociationClassField(UMLAssociation*))); connect(c,TQT_SIGNAL(sigAssociationEndRemoved(UMLAssociation*)),this,TQT_SLOT(removeAssociationClassField(UMLAssociation*))); connect(c,TQT_SIGNAL(operationAdded(UMLClassifierListItem*)),this,TQT_SLOT(addOperation(UMLClassifierListItem*))); connect(c,TQT_SIGNAL(operationRemoved(UMLClassifierListItem*)),this,TQT_SLOT(removeOperation(UMLClassifierListItem*))); connect(c,TQT_SIGNAL(modified()),this,TQT_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(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(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:"<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"