umbrello API Documentation

association.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *   copyright (C) 2003-2006                                               *
00009  *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "association.h"
00014 // qt/kde includes
00015 #include <kdebug.h>
00016 #include <klocale.h>
00017 #include <qregexp.h>
00018 // app includes
00019 #include "classifier.h"
00020 #include "folder.h"
00021 #include "uml.h"
00022 #include "umldoc.h"
00023 #include "umlrole.h"
00024 #include "uniqueid.h"
00025 #include "model_utils.h"
00026 
00027 using namespace Uml;
00028 
00029 // static members
00030 const Uml::Association_Type UMLAssociation::atypeFirst = Uml::at_Generalization;
00031 const Uml::Association_Type UMLAssociation::atypeLast = Uml::at_Relationship;
00032 const unsigned UMLAssociation::nAssocTypes = (unsigned)atypeLast -
00033         (unsigned)atypeFirst + 1;
00034 
00035 // constructor
00036 UMLAssociation::UMLAssociation( Association_Type type,
00037                                 UMLObject * roleA, UMLObject * roleB )
00038         : UMLObject("")
00039 {
00040     init(type, roleA, roleB);
00041 
00042     m_pRole[Uml::A]->setID( UniqueID::gen() );
00043     m_pRole[Uml::B]->setID( UniqueID::gen() );
00044 }
00045 
00046 UMLAssociation::UMLAssociation( Association_Type type /* = Uml::at_Unknown */)
00047         : UMLObject("", Uml::id_Reserved)
00048 {
00049     init(type, NULL, NULL);
00050 }
00051 
00052 // destructor
00053 UMLAssociation::~UMLAssociation( ) {
00054     if (m_pRole[A] == NULL) {
00055         kError() << "UMLAssociation destructor: m_pRole[A] is NULL already"
00056         << endl;
00057     } else {
00058         delete m_pRole[A];
00059         m_pRole[A] = NULL;
00060     }
00061     if (m_pRole[B] == NULL) {
00062         kError() << "UMLAssociation destructor: m_pRole[B] is NULL already"
00063         << endl;
00064     } else {
00065         delete m_pRole[B];
00066         m_pRole[B] = NULL;
00067     }
00068 }
00069 
00070 bool UMLAssociation::operator==(UMLAssociation &rhs) {
00071     if (this == &rhs) {
00072         return true;
00073     }
00074     return ( UMLObject::operator== ( rhs ) &&
00075              m_AssocType == rhs.m_AssocType &&
00076              m_Name == rhs.m_Name &&
00077              m_pRole[A] == rhs.m_pRole[A] &&
00078              m_pRole[B] == rhs.m_pRole[B] );
00079 }
00080 
00081 const QString UMLAssociation::assocTypeStr[UMLAssociation::nAssocTypes] = {
00082             /* The elements must be listed in the same order as in the
00083                Uml::Association_Type.  */
00084             i18n("Generalization"),             // at_Generalization
00085             i18n("Aggregation"),                // at_Aggregation
00086             i18n("Dependency"),                 // at_Dependency
00087             i18n("Association"),                // at_Association
00088             i18n("Self Association"),           // at_Association_Self
00089             i18n("Collaboration Message"),      // at_Coll_Message
00090             i18n("Sequence Message"),           // at_Seq_Message
00091             i18n("Collaboration Self Message"), // at_Coll_Message_Self
00092             i18n("Sequence Self Message"),      // at_Seq_Message_Self
00093             i18n("Containment"),                // at_Containment
00094             i18n("Composition"),                // at_Composition
00095             i18n("Realization"),                // at_Realization
00096             i18n("Uni Association"),            // at_UniAssociation
00097             i18n("Anchor"),                     // at_Anchor
00098             i18n("State Transition"),           // at_State
00099             i18n("Activity"),                   // at_Activity
00100         };
00101 
00102 Uml::Association_Type UMLAssociation::getAssocType() const {
00103     return m_AssocType;
00104 }
00105 
00106 QString UMLAssociation::toString ( ) const
00107 {
00108     QString string;
00109     if(m_pRole[A])
00110     {
00111         string += m_pRole[A]->getObject()->getName();
00112         string += ':';
00113         string += m_pRole[A]->getName();
00114     }
00115     string += ':' + typeAsString(m_AssocType) + ':';
00116     if(m_pRole[B])
00117     {
00118         string += m_pRole[B]->getObject( )->getName();
00119         string += ':';
00120         string += m_pRole[B]->getName();
00121     }
00122     return string;
00123 }
00124 
00125 QString UMLAssociation::typeAsString (Uml::Association_Type atype)
00126 {
00127     if (atype < atypeFirst || atype > atypeLast)
00128         return "";
00129     return assocTypeStr[(unsigned)atype - (unsigned)atypeFirst];
00130 }
00131 
00132 bool UMLAssociation::assocTypeHasUMLRepresentation(Uml::Association_Type atype)
00133 {
00134     return (atype == Uml::at_Generalization ||
00135             atype == Uml::at_Realization ||
00136             atype == Uml::at_Association ||
00137             atype == Uml::at_Association_Self ||
00138             atype == Uml::at_UniAssociation ||
00139             atype == Uml::at_Aggregation ||
00140             atype == Uml::at_Relationship ||
00141             atype == Uml::at_Composition ||
00142             atype == Uml::at_Dependency);
00143 }
00144 
00145 bool UMLAssociation::resolveRef() {
00146     bool successA = getUMLRole(A)->resolveRef();
00147     bool successB = getUMLRole(B)->resolveRef();
00148     if (successA && successB) {
00149         UMLObject *objA = getUMLRole(A)->getObject();
00150         UMLObject *objB = getUMLRole(B)->getObject();
00151         // Check if need to change the assoc type to Realization
00152         if (m_AssocType == Uml::at_Generalization &&
00153                 (objA && objA->getBaseType() == Uml::ot_Interface ||
00154                  objB && objB->getBaseType() == Uml::ot_Interface))
00155             m_AssocType = Uml::at_Realization;
00156         m_pUMLPackage->addAssocToConcepts(this);
00157         return true;
00158     }
00159     return false;
00160 }
00161 
00162 void UMLAssociation::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) {
00163     if (m_AssocType == Uml::at_Generalization) {
00164         QDomElement assocElement = UMLObject::save("UML:Generalization", qDoc);
00165         assocElement.setAttribute( "discriminator", "" );
00166         assocElement.setAttribute( "child", ID2STR(getObjectId(A)) );
00167         assocElement.setAttribute( "parent", ID2STR(getObjectId(B)) );
00168         qElement.appendChild( assocElement );
00169         return;
00170     }
00171     if (m_AssocType == Uml::at_Realization) {
00172         QDomElement assocElement = UMLObject::save("UML:Abstraction", qDoc);
00173         assocElement.setAttribute( "client", ID2STR(getObjectId(A)) );
00174         assocElement.setAttribute( "supplier", ID2STR(getObjectId(B)) );
00175         qElement.appendChild( assocElement );
00176         return;
00177     }
00178     if (m_AssocType == Uml::at_Dependency) {
00179         QDomElement assocElement = UMLObject::save("UML:Dependency", qDoc);
00180         assocElement.setAttribute( "client", ID2STR(getObjectId(A)) );
00181         assocElement.setAttribute( "supplier", ID2STR(getObjectId(B)) );
00182         qElement.appendChild( assocElement );
00183         return;
00184     }
00185     QDomElement associationElement = UMLObject::save("UML:Association", qDoc);
00186     QDomElement connElement = qDoc.createElement("UML:Association.connection");
00187     getUMLRole(A)->saveToXMI (qDoc, connElement);
00188     getUMLRole(B)->saveToXMI (qDoc, connElement);
00189     associationElement.appendChild (connElement);
00190     qElement.appendChild( associationElement );
00191 }
00192 
00193 bool UMLAssociation::load( QDomElement & element ) {
00194     if (getID() == Uml::id_None)
00195         return false; // old style XMI file. No real info in this association.
00196 
00197     UMLDoc * doc = UMLApp::app()->getDocument();
00198     UMLObject * obj[2] = { NULL, NULL };
00199     if (m_AssocType == Uml::at_Generalization ||
00200         m_AssocType == Uml::at_Realization ||
00201         m_AssocType == Uml::at_Dependency) {
00202         for (unsigned r = Uml::A; r <= Uml::B; r++) {
00203             const QString fetch = (m_AssocType == Uml::at_Generalization ?
00204                                    r == Uml::A ? "child" : "parent"
00205                        : r == Uml::A ? "client" : "supplier");
00206             QString roleIdStr = element.attribute(fetch, "");
00207             if (roleIdStr.isEmpty()) {
00208                 // Might be given as a child node instead - see below.
00209                 continue;
00210             }
00211 
00212             // set umlobject of role if possible (else defer resolution)
00213             obj[r] = doc->findObjectById(STR2ID(roleIdStr));
00214             Uml::Role_Type role = (Uml::Role_Type)r;
00215             if (obj[r] == NULL) {
00216                 m_pRole[role]->setSecondaryId(roleIdStr);  // defer to resolveRef()
00217             } else {
00218                 m_pRole[role]->setObject(obj[r]);
00219                 if (m_pUMLPackage == NULL) {
00220                     Uml::Model_Type mt = Model_Utils::convert_OT_MT(obj[r]->getBaseType());
00221                     m_pUMLPackage = doc->getRootFolder(mt);
00222                     kDebug() << "UMLAssociation::load(assoctype " << m_AssocType
00223                         << "): setting model type " << mt << endl;
00224                 }
00225             }
00226         }
00227         if (obj[A] == NULL || obj[B] == NULL) {
00228             for (QDomNode node = element.firstChild(); !node.isNull();
00229                     node = node.nextSibling()) {
00230                 if (node.isComment())
00231                     continue;
00232                 QDomElement tempElement = node.toElement();
00233                 QString tag = tempElement.tagName();
00234                 if (Model_Utils::isCommonXMIAttribute(tag))
00235                     continue;
00236                 // Permitted tag names:
00237                 //  roleA: "child" "subtype" "client"
00238                 //  roleB: "parent" "supertype" "supplier"
00239                 QString idStr = tempElement.attribute( "xmi.id", "" );
00240                 if (idStr.isEmpty())
00241                     idStr = tempElement.attribute( "xmi.idref", "" );
00242                 if (idStr.isEmpty()) {
00243                     QDomNode inner = node.firstChild();
00244                     QDomElement tmpElem = inner.toElement();
00245                     idStr = tmpElem.attribute( "xmi.id", "" );
00246                     if (idStr.isEmpty())
00247                         idStr = tmpElem.attribute( "xmi.idref", "" );
00248                 }
00249                 if (idStr.isEmpty()) {
00250                     kError() << "UMLAssociation::load (type " << m_AssocType
00251                         << ", id " << ID2STR(getID()) << "): "
00252                         << "xmi id not given for " << tag << endl;
00253                     continue;
00254                 }
00255                 // Since we know for sure that we're dealing with a non
00256                 // umbrello file, use deferred resolution unconditionally.
00257                 if (tagEq(tag, "child") || tagEq(tag, "subtype") || tagEq(tag, "client")) {
00258                     getUMLRole(A)->setSecondaryId(idStr);
00259                 } else {
00260                     getUMLRole(B)->setSecondaryId(idStr);
00261                 }
00262             }
00263         }
00264 
00265         // its a realization if either endpoint is an interface
00266         if (m_AssocType == Uml::at_Generalization &&
00267                 (obj[A] && obj[A]->getBaseType() == Uml::ot_Interface ||
00268                  obj[B] && obj[B]->getBaseType() == Uml::ot_Interface))
00269             m_AssocType = Uml::at_Realization;
00270 
00271         return true;
00272     }
00273 
00274     for (QDomNode node = element.firstChild(); !node.isNull();
00275             node = node.nextSibling()) {
00276         // uml13.dtd compliant format (new style)
00277         if (node.isComment())
00278             continue;
00279         QDomElement tempElement = node.toElement();
00280         QString tag = tempElement.tagName();
00281         if (Model_Utils::isCommonXMIAttribute(tag))
00282             continue;
00283         if (!tagEq(tag, "Association.connection") &&
00284                 !tagEq(tag, "Namespace.ownedElement") &&
00285                 !tagEq(tag, "Namespace.contents")) {
00286             kWarning() << "UMLAssociation::load: "
00287             << "unknown child node " << tag << endl;
00288             continue;
00289         }
00290         // Load role A.
00291         node = tempElement.firstChild();
00292         while (node.isComment())
00293             node = node.nextSibling();
00294         tempElement = node.toElement();
00295         if (tempElement.isNull()) {
00296             kWarning() << "UML:Association : element (A) is Null" << endl;
00297             return false;
00298         }
00299         tag = tempElement.tagName();
00300         if (!tagEq(tag, "AssociationEndRole") &&
00301                 !tagEq(tag, "AssociationEnd")) {
00302             kWarning() << "UMLAssociation::load: "
00303             << "unknown child (A) tag " << tag << endl;
00304             return false;
00305         }
00306         if (! getUMLRole(A)->loadFromXMI(tempElement))
00307             return false;
00308         // Load role B.
00309         node = node.nextSibling();
00310         while (node.isComment())
00311             node = node.nextSibling();
00312         tempElement = node.toElement();
00313         if (tempElement.isNull()) {
00314             kWarning() << "UML:Association : element (B) is Null" << endl;
00315             return false;
00316         }
00317         tag = tempElement.tagName();
00318         if (!tagEq(tag, "AssociationEndRole") &&
00319                 !tagEq(tag, "AssociationEnd")) {
00320             kWarning() << "UMLAssociation::load: "
00321             << "unknown child (B) tag " << tag << endl;
00322             return false;
00323         }
00324         if (! getUMLRole(B)->loadFromXMI(tempElement))
00325             return false;
00326 
00327         if (m_pUMLPackage == NULL) {
00328             Uml::Model_Type mt = Model_Utils::convert_OT_MT(getObject(B)->getBaseType());
00329             m_pUMLPackage = doc->getRootFolder(mt);
00330             kDebug() << "UMLAssociation::load: setting model type " << mt << endl;
00331         }
00332 
00333         // setting the association type:
00334         //
00335         // In the old days, we could just record this on the association,
00336         // and be done with it. But thats not how the UML13.dtd does things.
00337         // As a result, we are checking roleA for information about the
00338         // parent association (!) which by this point in the parse, should
00339         // be set. However, the information that the roles are allowed to have
00340         // is not complete, so we need to finish the analysis here.
00341 
00342         // find self-associations
00343         if (m_AssocType == Uml::at_Association && getObjectId(A) == getObjectId(B))
00344             m_AssocType = Uml::at_Association_Self;
00345 
00346         // fall-back default type
00347         if (m_AssocType == Uml::at_Unknown) {
00348             m_AssocType = Uml::at_Association;
00349         }
00350 
00351         return true;
00352     }
00353 
00354     // From here on it's old-style stuff.
00355     QString assocTypeStr = element.attribute( "assoctype", "-1" );
00356     Uml::Association_Type assocType = Uml::at_Unknown;
00357     if (assocTypeStr[0] >= 'a' && assocTypeStr[0] <= 'z') {
00358         // In an earlier version, the natural assoctype names were saved.
00359         const QString assocTypeString[nAssocTypes] = {
00360                     "generalization",   // at_Generalization
00361                     "aggregation",      // at_Aggregation
00362                     "dependency",       // at_Dependency
00363                     "association",      // at_Association
00364                     "associationself",  // at_Association_Self
00365                     "collmessage",      // at_Coll_Message
00366                     "seqmessage",       // at_Seq_Message
00367                     "collmessageself",  // at_Coll_Message_Self
00368                     "seqmessageself",   // at_Seq_Message_Self
00369                     "implementation",   // at_Implementation
00370                     "composition",      // at_Composition
00371                     "realization",      // at_Realization
00372                     "uniassociation",   // at_UniAssociation
00373                     "anchor",           // at_Anchor
00374                     "state",            // at_State
00375                     "activity",         // at_Activity
00376                     "relationship"      // at_Relationship
00377                 };
00378 
00379         unsigned index;
00380         for (index = 0; index < nAssocTypes; index++)
00381             if (assocTypeStr == assocTypeString[index])
00382                 break;
00383         if (index < nAssocTypes)
00384             assocType = (Uml::Association_Type)index;
00385     } else {
00386         int assocTypeNum = assocTypeStr.toInt();
00387         if (assocTypeNum < (int)atypeFirst || assocTypeNum > (int)atypeLast) {
00388             kWarning() << "bad assoctype of UML:Association "
00389             << ID2STR(getID()) << endl;
00390             return false;
00391         }
00392         assocType = (Uml::Association_Type)assocTypeNum;
00393     }
00394     setAssocType( assocType );
00395 
00396     Uml::IDType roleAObjID = STR2ID(element.attribute( "rolea", "-1" ));
00397     Uml::IDType roleBObjID = STR2ID(element.attribute( "roleb", "-1" ));
00398     if (assocType == at_Aggregation || assocType == at_Composition) {
00399         // Flip roles to compensate for changed diamond logic in LinePath.
00400         // For further explanations see AssociationWidget::loadFromXMI.
00401         Uml::IDType tmp = roleAObjID;
00402         roleAObjID = roleBObjID;
00403         roleBObjID = tmp;
00404     }
00405 
00406     UMLObject * objA = doc->findObjectById(roleAObjID);
00407     UMLObject * objB = doc->findObjectById(roleBObjID);
00408 
00409     if(objA)
00410         getUMLRole(A)->setObject(objA);
00411     else
00412         return false;
00413 
00414     if(objB)
00415         getUMLRole(B)->setObject(objB);
00416     else
00417         return false;
00418 
00419     setMulti(element.attribute( "multia", "" ), A);
00420     setMulti(element.attribute( "multib", "" ), B);
00421 
00422     setRoleName(element.attribute( "namea", "" ), A);
00423     setRoleName(element.attribute( "nameb", "" ), B);
00424 
00425     setRoleDoc(element.attribute( "doca", "" ), A);
00426     setRoleDoc(element.attribute( "docb", "" ), B);
00427 
00428     // Visibility defaults to Public if it cant set it here..
00429     QString visibilityA = element.attribute( "visibilitya", "0");
00430     QString visibilityB = element.attribute( "visibilityb", "0");
00431     if (visibilityA.toInt() > 0)
00432         setVisibility((Uml::Visibility::Value)visibilityA.toInt(), A);
00433     if (visibilityB.toInt() > 0)
00434         setVisibility((Uml::Visibility::Value)visibilityB.toInt(), B);
00435 
00436     // Changeability defaults to Changeable if it cant set it here..
00437     QString changeabilityA = element.attribute( "changeabilitya", "0");
00438     QString changeabilityB = element.attribute( "changeabilityb", "0");
00439     if (changeabilityA.toInt() > 0)
00440         setChangeability ( (Changeability_Type) changeabilityA.toInt(), A);
00441     if (changeabilityB.toInt() > 0)
00442         setChangeability ( (Changeability_Type) changeabilityB.toInt(), B);
00443 
00444     return true;
00445 }
00446 
00447 UMLObject* UMLAssociation::getObject(Role_Type role) {
00448     return m_pRole[role]->getObject();
00449 }
00450 
00451 Uml::IDType UMLAssociation::getObjectId(Role_Type role) {
00452     UMLRole *roleObj = m_pRole[role];
00453     UMLObject *o = roleObj->getObject();
00454     if (o == NULL) {
00455         QString auxID = roleObj->getSecondaryId();
00456         if (auxID.isEmpty()) {
00457             kError() << "UMLAssociation::getObjectId(" << role
00458             << "): getObject returns NULL" << endl;
00459             return Uml::id_None;
00460         } else {
00461             kDebug() << "UMLAssociation::getObjectId(" << role
00462             << "): using secondary ID " << auxID << endl;
00463             return STR2ID(auxID);
00464         }
00465     }
00466     return o->getID();
00467 }
00468 
00469 /* CURRENTLY UNUSED
00470 Uml::IDType UMLAssociation::getRoleId(Role_Type role) const {
00471     return m_pRole[role]->getID();
00472 }
00473  */
00474 
00475 Changeability_Type UMLAssociation::getChangeability(Role_Type role) const {
00476     return m_pRole[role]->getChangeability();
00477 }
00478 
00479 Uml::Visibility UMLAssociation::getVisibility(Role_Type role) const {
00480     return m_pRole[role]->getVisibility();
00481 }
00482 
00483 QString UMLAssociation::getMulti(Role_Type role) const {
00484     return m_pRole[role]->getMultiplicity();
00485 }
00486 
00487 QString UMLAssociation::getRoleName(Role_Type role) const {
00488     return m_pRole[role]->getName();
00489 }
00490 
00491 QString UMLAssociation::getRoleDoc(Role_Type role) const {
00492     return m_pRole[role]->getDoc();
00493 }
00494 
00495 UMLRole * UMLAssociation::getUMLRole(Role_Type role) {
00496     return m_pRole[role];
00497 }
00498 
00499 void UMLAssociation::setOldLoadMode(bool value /* = true */) {
00500     m_bOldLoadMode = value;
00501 }
00502 
00503 bool UMLAssociation::getOldLoadMode() const {
00504     return m_bOldLoadMode;
00505 }
00506 
00507 void UMLAssociation::setAssocType(Uml::Association_Type assocType) {
00508     m_AssocType = assocType;
00509     if(m_AssocType == at_UniAssociation)
00510     {
00511         // In this case we need to auto-set the multiplicity/rolenames
00512         // of the roles
00513 #ifdef VERBOSE_DEBUGGING
00514         kDebug() << " A new uni-association has been created." << endl;
00515 #endif
00516     }
00517     UMLDoc *umldoc = UMLApp::app()->getDocument();
00518     if (! umldoc->loading())
00519         emit modified();
00520 }
00521 
00522 void UMLAssociation::setObject(UMLObject *obj, Role_Type role) {
00523     m_pRole[role]->setObject(obj);
00524 }
00525 
00526 void UMLAssociation::setVisibility(Uml::Visibility value, Role_Type role) {
00527     m_pRole[role]->setVisibility(value);
00528 }
00529 
00530 void UMLAssociation::setChangeability(Changeability_Type value, Role_Type role) {
00531     m_pRole[role]->setChangeability(value);
00532 }
00533 
00534 void UMLAssociation::setMulti(const QString &value, Role_Type role) {
00535     m_pRole[role]->setMultiplicity(value);
00536 }
00537 
00538 void UMLAssociation::setRoleName(const QString &value, Role_Type role) {
00539     m_pRole[role]->setName(value);
00540 }
00541 
00542 void UMLAssociation::setRoleDoc(const QString &doc, Role_Type role) {
00543     m_pRole[role]->setDoc(doc);
00544 }
00545 
00546 QString UMLAssociation::ChangeabilityToString(Uml::Changeability_Type type) {
00547     switch (type) {
00548     case Uml::chg_Frozen:
00549         return "frozen";
00550         break;
00551     case Uml::chg_AddOnly:
00552         return "addOnly";
00553         break;
00554     case Uml::chg_Changeable:
00555     default:
00556         return "changeable";
00557         break;
00558     }
00559 }
00560 
00561 void UMLAssociation::init(Association_Type type, UMLObject *roleAObj, UMLObject *roleBObj) {
00562     m_AssocType = type;
00563     m_BaseType = ot_Association;
00564     m_Name = "";
00565     m_bOldLoadMode = false;
00566     nrof_parent_widgets = -1;
00567     m_pUMLPackage = UMLApp::app()->getDocument()->currentRoot();
00568     m_pRole[Uml::A] = new UMLRole (this, roleAObj, Uml::A);
00569     m_pRole[Uml::B] = new UMLRole (this, roleBObj, Uml::B);
00570 }
00571 
00572 
00573 #include "association.moc"
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:07:54 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003