/*************************************************************************** * * * 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) 2003-2006 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "association.h" // qt/kde includes #include #include #include // app includes #include "classifier.h" #include "folder.h" #include "uml.h" #include "umldoc.h" #include "umlrole.h" #include "uniqueid.h" #include "model_utils.h" using namespace Uml; // static members const Uml::Association_Type UMLAssociation::atypeFirst = Uml::at_Generalization; const Uml::Association_Type UMLAssociation::atypeLast = Uml::at_Relationship; const unsigned UMLAssociation::nAssocTypes = (unsigned)atypeLast - (unsigned)atypeFirst + 1; // constructor UMLAssociation::UMLAssociation( Uml::Association_Type type, UMLObject * roleA, UMLObject * roleB ) : UMLObject("") { init(type, roleA, roleB); m_pRole[Uml::A]->setID( UniqueID::gen() ); m_pRole[Uml::B]->setID( UniqueID::gen() ); } UMLAssociation::UMLAssociation( Uml::Association_Type type /* = Uml::at_Unknown */) : UMLObject("", Uml::id_Reserved) { init(type, NULL, NULL); } // destructor UMLAssociation::~UMLAssociation( ) { if (m_pRole[A] == NULL) { kError() << "UMLAssociation destructor: m_pRole[A] is NULL already" << endl; } else { delete m_pRole[A]; m_pRole[A] = NULL; } if (m_pRole[B] == NULL) { kError() << "UMLAssociation destructor: m_pRole[B] is NULL already" << endl; } else { delete m_pRole[B]; m_pRole[B] = NULL; } } bool UMLAssociation::operator==(UMLAssociation &rhs) { if (this == &rhs) { return true; } return ( UMLObject::operator== ( rhs ) && m_AssocType == rhs.m_AssocType && m_Name == rhs.m_Name && m_pRole[A] == rhs.m_pRole[A] && m_pRole[B] == rhs.m_pRole[B] ); } const TQString UMLAssociation::assocTypeStr[UMLAssociation::nAssocTypes] = { /* The elements must be listed in the same order as in the Uml::Association_Type. */ i18n("Generalization"), // at_Generalization i18n("Aggregation"), // at_Aggregation i18n("Dependency"), // at_Dependency i18n("Association"), // at_Association i18n("Self Association"), // at_Association_Self i18n("Collaboration Message"), // at_Coll_Message i18n("Sequence Message"), // at_Seq_Message i18n("Collaboration Self Message"), // at_Coll_Message_Self i18n("Sequence Self Message"), // at_Seq_Message_Self i18n("Containment"), // at_Containment i18n("Composition"), // at_Composition i18n("Realization"), // at_Realization i18n("Uni Association"), // at_UniAssociation i18n("Anchor"), // at_Anchor i18n("State Transition"), // at_State i18n("Activity"), // at_Activity }; Uml::Association_Type UMLAssociation::getAssocType() const { return m_AssocType; } TQString UMLAssociation::toString ( ) const { TQString string; if(m_pRole[A]) { string += m_pRole[A]->getObject()->getName(); string += ':'; string += m_pRole[A]->getName(); } string += ':' + typeAsString(m_AssocType) + ':'; if(m_pRole[B]) { string += m_pRole[B]->getObject( )->getName(); string += ':'; string += m_pRole[B]->getName(); } return string; } TQString UMLAssociation::typeAsString (Uml::Association_Type atype) { if (atype < atypeFirst || atype > atypeLast) return ""; return assocTypeStr[(unsigned)atype - (unsigned)atypeFirst]; } bool UMLAssociation::assocTypeHasUMLRepresentation(Uml::Association_Type atype) { return (atype == Uml::at_Generalization || atype == Uml::at_Realization || atype == Uml::at_Association || atype == Uml::at_Association_Self || atype == Uml::at_UniAssociation || atype == Uml::at_Aggregation || atype == Uml::at_Relationship || atype == Uml::at_Composition || atype == Uml::at_Dependency); } bool UMLAssociation::resolveRef() { bool successA = getUMLRole(A)->resolveRef(); bool successB = getUMLRole(B)->resolveRef(); if (successA && successB) { UMLObject *objA = getUMLRole(A)->getObject(); UMLObject *objB = getUMLRole(B)->getObject(); // Check if need to change the assoc type to Realization if (m_AssocType == Uml::at_Generalization && (objA && objA->getBaseType() == Uml::ot_Interface || objB && objB->getBaseType() == Uml::ot_Interface)) m_AssocType = Uml::at_Realization; m_pUMLPackage->addAssocToConcepts(this); return true; } return false; } void UMLAssociation::saveToXMI( TQDomDocument & qDoc, TQDomElement & qElement ) { if (m_AssocType == Uml::at_Generalization) { TQDomElement assocElement = UMLObject::save("UML:Generalization", qDoc); assocElement.setAttribute( "discriminator", "" ); assocElement.setAttribute( "child", ID2STR(getObjectId(A)) ); assocElement.setAttribute( "parent", ID2STR(getObjectId(B)) ); qElement.appendChild( assocElement ); return; } if (m_AssocType == Uml::at_Realization) { TQDomElement assocElement = UMLObject::save("UML:Abstraction", qDoc); assocElement.setAttribute( "client", ID2STR(getObjectId(A)) ); assocElement.setAttribute( "supplier", ID2STR(getObjectId(B)) ); qElement.appendChild( assocElement ); return; } if (m_AssocType == Uml::at_Dependency) { TQDomElement assocElement = UMLObject::save("UML:Dependency", qDoc); assocElement.setAttribute( "client", ID2STR(getObjectId(A)) ); assocElement.setAttribute( "supplier", ID2STR(getObjectId(B)) ); qElement.appendChild( assocElement ); return; } TQDomElement associationElement = UMLObject::save("UML:Association", qDoc); TQDomElement connElement = qDoc.createElement("UML:Association.connection"); getUMLRole(A)->saveToXMI (qDoc, connElement); getUMLRole(B)->saveToXMI (qDoc, connElement); associationElement.appendChild (connElement); qElement.appendChild( associationElement ); } bool UMLAssociation::load( TQDomElement & element ) { if (getID() == Uml::id_None) return false; // old style XMI file. No real info in this association. UMLDoc * doc = UMLApp::app()->getDocument(); UMLObject * obj[2] = { NULL, NULL }; if (m_AssocType == Uml::at_Generalization || m_AssocType == Uml::at_Realization || m_AssocType == Uml::at_Dependency) { for (unsigned r = Uml::A; r <= Uml::B; r++) { const TQString fetch = (m_AssocType == Uml::at_Generalization ? r == Uml::A ? "child" : "parent" : r == Uml::A ? "client" : "supplier"); TQString roleIdStr = element.attribute(fetch, ""); if (roleIdStr.isEmpty()) { // Might be given as a child node instead - see below. continue; } // set umlobject of role if possible (else defer resolution) obj[r] = doc->findObjectById(STR2ID(roleIdStr)); Uml::Role_Type role = (Uml::Role_Type)r; if (obj[r] == NULL) { m_pRole[role]->setSecondaryId(roleIdStr); // defer to resolveRef() } else { m_pRole[role]->setObject(obj[r]); if (m_pUMLPackage == NULL) { Uml::Model_Type mt = Model_Utils::convert_OT_MT(obj[r]->getBaseType()); m_pUMLPackage = doc->getRootFolder(mt); kDebug() << "UMLAssociation::load(assoctype " << m_AssocType << "): setting model type " << mt << endl; } } } if (obj[A] == NULL || obj[B] == NULL) { for (TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isComment()) continue; TQDomElement tempElement = node.toElement(); TQString tag = tempElement.tagName(); if (Model_Utils::isCommonXMIAttribute(tag)) continue; // Permitted tag names: // roleA: "child" "subtype" "client" // roleB: "parent" "supertype" "supplier" TQString idStr = tempElement.attribute( "xmi.id", "" ); if (idStr.isEmpty()) idStr = tempElement.attribute( "xmi.idref", "" ); if (idStr.isEmpty()) { TQDomNode inner = node.firstChild(); TQDomElement tmpElem = inner.toElement(); idStr = tmpElem.attribute( "xmi.id", "" ); if (idStr.isEmpty()) idStr = tmpElem.attribute( "xmi.idref", "" ); } if (idStr.isEmpty()) { kError() << "UMLAssociation::load (type " << m_AssocType << ", id " << ID2STR(getID()) << "): " << "xmi id not given for " << tag << endl; continue; } // Since we know for sure that we're dealing with a non // umbrello file, use deferred resolution unconditionally. if (tagEq(tag, "child") || tagEq(tag, "subtype") || tagEq(tag, "client")) { getUMLRole(A)->setSecondaryId(idStr); } else { getUMLRole(B)->setSecondaryId(idStr); } } } // its a realization if either endpoint is an interface if (m_AssocType == Uml::at_Generalization && (obj[A] && obj[A]->getBaseType() == Uml::ot_Interface || obj[B] && obj[B]->getBaseType() == Uml::ot_Interface)) m_AssocType = Uml::at_Realization; return true; } for (TQDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling()) { // uml13.dtd compliant format (new style) if (node.isComment()) continue; TQDomElement tempElement = node.toElement(); TQString tag = tempElement.tagName(); if (Model_Utils::isCommonXMIAttribute(tag)) continue; if (!tagEq(tag, "Association.connection") && !tagEq(tag, "Namespace.ownedElement") && !tagEq(tag, "Namespace.contents")) { kWarning() << "UMLAssociation::load: " << "unknown child node " << tag << endl; continue; } // Load role A. node = tempElement.firstChild(); while (node.isComment()) node = node.nextSibling(); tempElement = node.toElement(); if (tempElement.isNull()) { kWarning() << "UML:Association : element (A) is Null" << endl; return false; } tag = tempElement.tagName(); if (!tagEq(tag, "AssociationEndRole") && !tagEq(tag, "AssociationEnd")) { kWarning() << "UMLAssociation::load: " << "unknown child (A) tag " << tag << endl; return false; } if (! getUMLRole(A)->loadFromXMI(tempElement)) return false; // Load role B. node = node.nextSibling(); while (node.isComment()) node = node.nextSibling(); tempElement = node.toElement(); if (tempElement.isNull()) { kWarning() << "UML:Association : element (B) is Null" << endl; return false; } tag = tempElement.tagName(); if (!tagEq(tag, "AssociationEndRole") && !tagEq(tag, "AssociationEnd")) { kWarning() << "UMLAssociation::load: " << "unknown child (B) tag " << tag << endl; return false; } if (! getUMLRole(B)->loadFromXMI(tempElement)) return false; if (m_pUMLPackage == NULL) { Uml::Model_Type mt = Model_Utils::convert_OT_MT(getObject(B)->getBaseType()); m_pUMLPackage = doc->getRootFolder(mt); kDebug() << "UMLAssociation::load: setting model type " << mt << endl; } // setting the association type: // // In the old days, we could just record this on the association, // and be done with it. But thats not how the UML13.dtd does things. // As a result, we are checking roleA for information about the // parent association (!) which by this point in the parse, should // be set. However, the information that the roles are allowed to have // is not complete, so we need to finish the analysis here. // find self-associations if (m_AssocType == Uml::at_Association && getObjectId(A) == getObjectId(B)) m_AssocType = Uml::at_Association_Self; // fall-back default type if (m_AssocType == Uml::at_Unknown) { m_AssocType = Uml::at_Association; } return true; } // From here on it's old-style stuff. TQString assocTypeStr = element.attribute( "assoctype", "-1" ); Uml::Association_Type assocType = Uml::at_Unknown; if (assocTypeStr[0] >= 'a' && assocTypeStr[0] <= 'z') { // In an earlier version, the natural assoctype names were saved. const TQString assocTypeString[nAssocTypes] = { "generalization", // at_Generalization "aggregation", // at_Aggregation "dependency", // at_Dependency "association", // at_Association "associationself", // at_Association_Self "collmessage", // at_Coll_Message "seqmessage", // at_Seq_Message "collmessageself", // at_Coll_Message_Self "seqmessageself", // at_Seq_Message_Self "implementation", // at_Implementation "composition", // at_Composition "realization", // at_Realization "uniassociation", // at_UniAssociation "anchor", // at_Anchor "state", // at_State "activity", // at_Activity "relationship" // at_Relationship }; unsigned index; for (index = 0; index < nAssocTypes; index++) if (assocTypeStr == assocTypeString[index]) break; if (index < nAssocTypes) assocType = (Uml::Association_Type)index; } else { int assocTypeNum = assocTypeStr.toInt(); if (assocTypeNum < (int)atypeFirst || assocTypeNum > (int)atypeLast) { kWarning() << "bad assoctype of UML:Association " << ID2STR(getID()) << endl; return false; } assocType = (Uml::Association_Type)assocTypeNum; } setAssocType( assocType ); Uml::IDType roleAObjID = STR2ID(element.attribute( "rolea", "-1" )); Uml::IDType roleBObjID = STR2ID(element.attribute( "roleb", "-1" )); if (assocType == at_Aggregation || assocType == at_Composition) { // Flip roles to compensate for changed diamond logic in LinePath. // For further explanations see AssociationWidget::loadFromXMI. Uml::IDType tmp = roleAObjID; roleAObjID = roleBObjID; roleBObjID = tmp; } UMLObject * objA = doc->findObjectById(roleAObjID); UMLObject * objB = doc->findObjectById(roleBObjID); if(objA) getUMLRole(A)->setObject(objA); else return false; if(objB) getUMLRole(B)->setObject(objB); else return false; setMulti(element.attribute( "multia", "" ), A); setMulti(element.attribute( "multib", "" ), B); setRoleName(element.attribute( "namea", "" ), A); setRoleName(element.attribute( "nameb", "" ), B); setRoleDoc(element.attribute( "doca", "" ), A); setRoleDoc(element.attribute( "docb", "" ), B); // Visibility defaults to Public if it cant set it here.. TQString visibilityA = element.attribute( "visibilitya", "0"); TQString visibilityB = element.attribute( "visibilityb", "0"); if (visibilityA.toInt() > 0) setVisibility((Uml::Visibility::Value)visibilityA.toInt(), A); if (visibilityB.toInt() > 0) setVisibility((Uml::Visibility::Value)visibilityB.toInt(), B); // Changeability defaults to Changeable if it cant set it here.. TQString changeabilityA = element.attribute( "changeabilitya", "0"); TQString changeabilityB = element.attribute( "changeabilityb", "0"); if (changeabilityA.toInt() > 0) setChangeability ( (Uml::Changeability_Type) changeabilityA.toInt(), A); if (changeabilityB.toInt() > 0) setChangeability ( (Uml::Changeability_Type) changeabilityB.toInt(), B); return true; } UMLObject* UMLAssociation::getObject(Uml::Role_Type role) { return m_pRole[role]->getObject(); } Uml::IDType UMLAssociation::getObjectId(Uml::Role_Type role) { UMLRole *roleObj = m_pRole[role]; UMLObject *o = roleObj->getObject(); if (o == NULL) { TQString auxID = roleObj->getSecondaryId(); if (auxID.isEmpty()) { kError() << "UMLAssociation::getObjectId(" << role << "): getObject returns NULL" << endl; return Uml::id_None; } else { kDebug() << "UMLAssociation::getObjectId(" << role << "): using secondary ID " << auxID << endl; return STR2ID(auxID); } } return o->getID(); } /* CURRENTLY UNUSED Uml::IDType UMLAssociation::getRoleId(Role_Type role) const { return m_pRole[role]->getID(); } */ Uml::Changeability_Type UMLAssociation::getChangeability(Uml::Role_Type role) const { return m_pRole[role]->getChangeability(); } Uml::Visibility UMLAssociation::getVisibility(Uml::Role_Type role) const { return m_pRole[role]->getVisibility(); } TQString UMLAssociation::getMulti(Uml::Role_Type role) const { return m_pRole[role]->getMultiplicity(); } TQString UMLAssociation::getRoleName(Uml::Role_Type role) const { return m_pRole[role]->getName(); } TQString UMLAssociation::getRoleDoc(Uml::Role_Type role) const { return m_pRole[role]->getDoc(); } UMLRole * UMLAssociation::getUMLRole(Uml::Role_Type role) { return m_pRole[role]; } void UMLAssociation::setOldLoadMode(bool value /* = true */) { m_bOldLoadMode = value; } bool UMLAssociation::getOldLoadMode() const { return m_bOldLoadMode; } void UMLAssociation::setAssocType(Uml::Association_Type assocType) { m_AssocType = assocType; if(m_AssocType == at_UniAssociation) { // In this case we need to auto-set the multiplicity/rolenames // of the roles #ifdef VERBOSE_DEBUGGING kDebug() << " A new uni-association has been created." << endl; #endif } UMLDoc *umldoc = UMLApp::app()->getDocument(); if (! umldoc->loading()) emit modified(); } void UMLAssociation::setObject(UMLObject *obj, Uml::Role_Type role) { m_pRole[role]->setObject(obj); } void UMLAssociation::setVisibility(Uml::Visibility value, Uml::Role_Type role) { m_pRole[role]->setVisibility(value); } void UMLAssociation::setChangeability(Uml::Changeability_Type value, Uml::Role_Type role) { m_pRole[role]->setChangeability(value); } void UMLAssociation::setMulti(const TQString &value, Uml::Role_Type role) { m_pRole[role]->setMultiplicity(value); } void UMLAssociation::setRoleName(const TQString &value, Uml::Role_Type role) { m_pRole[role]->setName(value); } void UMLAssociation::setRoleDoc(const TQString &doc, Uml::Role_Type role) { m_pRole[role]->setDoc(doc); } TQString UMLAssociation::ChangeabilityToString(Uml::Changeability_Type type) { switch (type) { case Uml::chg_Frozen: return "frozen"; break; case Uml::chg_AddOnly: return "addOnly"; break; case Uml::chg_Changeable: default: return "changeable"; break; } } void UMLAssociation::init(Uml::Association_Type type, UMLObject *roleAObj, UMLObject *roleBObj) { m_AssocType = type; m_BaseType = ot_Association; m_Name = ""; m_bOldLoadMode = false; nrof_parent_widgets = -1; if (!UMLApp::app()->getDocument()->loading()) m_pUMLPackage = UMLApp::app()->getDocument()->currentRoot(); m_pRole[Uml::A] = new UMLRole (this, roleAObj, Uml::A); m_pRole[Uml::B] = new UMLRole (this, roleBObj, Uml::B); } #include "association.moc"