/**
 * Adds an AssociationWidget to the association list and creates the
 * corresponding UMLAssociation in the current UMLDoc.
 * If the association can't be added, is deleted.
 *
 * @param assoc The AssociationWidget to add.
 * @return      True on success
 */
bool ToolBarStateAssociation::addAssociationInViewAndDoc(AssociationWidget* assoc)
{
    // append in view
    if (m_pUMLScene->addAssociation(assoc, false)) {
        // if view went ok, then append in document
        UMLAssociation *umla = assoc->association();
        if (umla) {
            // association with model representation in UMLDoc
            Uml::ModelType::Enum m = Model_Utils::convert_DT_MT(m_pUMLScene->type());
            UMLDoc *umldoc = UMLApp::app()->document();
            umla->setUMLPackage(umldoc->rootFolder(m));
            umldoc->addAssociation(umla);
        }
        return true;
    } else {
        uError() << "cannot addAssocInViewAndDoc(), deleting";
        delete assoc;
        return false;
    }
}
/**
 * Parse a file into the PetalNode internal tree representation
 * and then create Umbrello objects by traversing the tree.
 *
 * @return  In case of error: NULL
 *          In case of success with non NULL parentPkg: pointer to UMLPackage created for controlled unit
 *          In case of success with NULL parentPkg: pointer to root folder of Logical View
 */
UMLPackage* loadFromMDL(QFile& file, UMLPackage *parentPkg /* = 0 */) 
{
    if (parentPkg == NULL) {
        QString fName = file.fileName();
        int lastSlash = fName.lastIndexOf(QLatin1Char('/'));
        if (lastSlash > 0) {
            dirPrefix = fName.left(lastSlash + 1);
        }
    }
    QTextStream stream(&file);
    stream.setCodec("ISO 8859-1");
    QString line;
    PetalNode *root = NULL;
    uint nClosures_sav = nClosures;
    uint linum_sav = linum;
    nClosures = 0;
    linum = 0;
    while (!(line = stream.readLine()).isNull()) {
        linum++;
        if (line.contains(QRegExp(QLatin1String("^\\s*\\(object Petal")))) {
            bool finish = false;
            // Nested loop determines character set to use
            while (!(line = stream.readLine()).isNull()) {
                linum++; // CHECK: do we need petal version info?
                if (line.contains(QLatin1Char(')'))) {
                    finish = true;
                    line = line.replace(QLatin1String(QLatin1String(")")), QString());
                }
                QStringList a = line.trimmed().split(QRegExp(QLatin1String("\\s+")));
                if (a.size() == 2 && a[0] == QLatin1String("charSet")) {
                    const QString& charSet = a[1];
                    if (!charSet.contains(QRegExp(QLatin1String("^\\d+$")))) {
                        uWarning() << "Unimplemented charSet " << charSet;
                        if (finish)
                            break;
                        continue;
                    }
                    const int charSetNum = charSet.toInt();
                    switch (charSetNum) {
                        case 0:         // ASCII
                            ;
                        case 1:    // Default
                            SETCODEC("System");
                        case 2:    // Symbol
                            ; // @todo     SETCODEC("what");
                        case 77:   // Mac
                            SETCODEC("macintosh");
                        case 128:  // ShiftJIS (Japanese)
                            SETCODEC("Shift_JIS");
                        case 129:  // Hangul (Korean)
                            SETCODEC("EUC-KR");
                        case 130:  // Johab (Korean)
                            SETCODEC("EUC-KR");
                        case 134:  // GB2312 (Chinese)
                            SETCODEC("GB18030");  // "Don't use GB2312 here" (Ralf H.)
                        case 136:  // ChineseBig5
                            SETCODEC("Big5");
                        case 161:  // Greek
                            SETCODEC("windows-1253");
                        case 162:  // Turkish
                            SETCODEC("windows-1254");
                        case 163:  // Vietnamese
                            SETCODEC("windows-1258");
                        case 177:  // Hebrew
                            SETCODEC("windows-1255");
                        case 178:  // Arabic
                            SETCODEC("windows-1256");
                        case 186:  // Baltic
                            SETCODEC("windows-1257");
                        case 204:  // Russian
                            SETCODEC("windows-1251");
                        case 222:  // Thai
                            SETCODEC("TIS-620");
                        case 238:  // EastEurope
                            SETCODEC("windows-1250");
                        case 255:  // OEM (extended ASCII)
                            SETCODEC("windows-1252");
                        default:
                            uWarning() << "Unimplemented charSet number" << charSetNum;
                    }
                }
                if (finish)
                     break;
            }
            if (line.isNull())
                break;
        } else {
            QRegExp objectRx(QLatin1String("^\\s*\\(object "));
            if (line.contains(objectRx)) {
                nClosures = 0;
                QStringList initialArgs = scan(line);
                initialArgs.pop_front();  // remove opening parenthesis
                root = readAttributes(initialArgs, stream);
                break;
            }
        }
    }
    file.close();
    nClosures = nClosures_sav;
    linum = linum_sav;
    if (root == NULL)
        return NULL;

    if (parentPkg) {
        UMLPackage *child = petalTree2Uml(root, parentPkg);
        delete root;
        return child;
    }

    if (root->name() != QLatin1String("Design")) {
        uError() << "expecting root name Design";
        delete root;
        return NULL;
    }
    Import_Utils::assignUniqueIdOnCreation(false);
    UMLDoc *umldoc = UMLApp::app()->document();

    //*************************** import Logical View *********************************
    umldoc->setCurrentRoot(Uml::ModelType::Logical);
    UMLPackage *logicalView = umldoc->rootFolder(Uml::ModelType::Logical);
    importView(root, logicalView,
               QLatin1String("root_category"), QLatin1String("logical_models"),
               QLatin1String("Class_Category"), QLatin1String("logical_presentations"));

    //*************************** import Use Case View ********************************
    umldoc->setCurrentRoot(Uml::ModelType::UseCase);
    UMLPackage *useCaseView = umldoc->rootFolder(Uml::ModelType::UseCase);
    importView(root, useCaseView,
               QLatin1String("root_usecase_package"), QLatin1String("logical_models"),
               QLatin1String("Class_Category"), QLatin1String("logical_presentations"));

    //*************************** import Component View *******************************
    umldoc->setCurrentRoot(Uml::ModelType::Component);
    UMLPackage *componentView = umldoc->rootFolder(Uml::ModelType::Component);
    importView(root, componentView,
               QLatin1String("root_subsystem"), QLatin1String("physical_models"),
               QLatin1String("SubSystem"), QLatin1String("physical_presentations"));

    //*************************** import Deployment View ******************************
    umldoc->setCurrentRoot(Uml::ModelType::Deployment);
    UMLPackage *deploymentView = umldoc->rootFolder(Uml::ModelType::Deployment);
    importView(root, deploymentView, QLatin1String("process_structure"),
                                     QLatin1String("ProcsNDevs"), QLatin1String("Processes"));

    //***************************       wrap up        ********************************
    delete root;
    umldoc->setCurrentRoot(Uml::ModelType::Logical);
    Import_Utils::assignUniqueIdOnCreation(true);
    umldoc->resolveTypes();
    return logicalView;
}
Beispiel #3
0
/**
 * Implement abstract operation from NativeImportBase.
 */
bool AdaImport::parseStmt()
{
    const int srcLength = m_source.count();
    QString keyword = m_source[m_srcIndex];
    UMLDoc *umldoc = UMLApp::app()->document();
    //uDebug() << '"' << keyword << '"';
    if (keyword == QLatin1String("with")) {
        if (m_inGenericFormalPart) {
            // mapping of generic formal subprograms or packages is not yet implemented
            return false;
        }
        while (++m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String(";")) {
            QStringList components = m_source[m_srcIndex].toLower().split(QLatin1Char('.'));
            const QString& prefix = components.first();
            if (prefix == QLatin1String("system")
                || prefix == QLatin1String("ada")
                || prefix == QLatin1String("gnat")
                || prefix == QLatin1String("interfaces")
                || prefix == QLatin1String("text_io")
                || prefix == QLatin1String("unchecked_conversion")
                || prefix == QLatin1String("unchecked_deallocation")) {
                if (advance() != QLatin1String(","))
                    break;
                continue;
            }
            parseStems(components);
            if (advance() != QLatin1String(","))
                break;
        }
        return true;
    }
    if (keyword == QLatin1String("generic")) {
        m_inGenericFormalPart = true;
        return true;
    }
    if (keyword == QLatin1String("package")) {
        const QString& name = advance();
        QStringList parentPkgs = name.toLower().split(QLatin1Char('.'));
        parentPkgs.pop_back();  // exclude the current package
        parseStems(parentPkgs);
        UMLObject *ns = NULL;
        if (advance() == QLatin1String("is")) {
            ns = Import_Utils::createUMLObject(UMLObject::ot_Package, name,
                                               currentScope(), m_comment);
            if (m_source[m_srcIndex + 1] == QLatin1String("new")) {
                m_srcIndex++;
                QString pkgName = advance();
                UMLObject *gp = Import_Utils::createUMLObject(UMLObject::ot_Package, pkgName,
                                                              currentScope());
                gp->setStereotype(QLatin1String("generic"));
                // Add binding from instantiator to instantiatee
                UMLAssociation *assoc = new UMLAssociation(Uml::AssociationType::Dependency, ns, gp);
                assoc->setUMLPackage(umldoc->rootFolder(Uml::ModelType::Logical));
                assoc->setStereotype(QLatin1String("bind"));
                // Work around missing display of stereotype in AssociationWidget:
                assoc->setName(assoc->stereotype(true));
                umldoc->addAssociation(assoc);
                skipStmt();
            } else {
                pushScope(static_cast<UMLPackage*>(ns));
            }
        } else if (m_source[m_srcIndex] == QLatin1String("renames")) {
            m_renaming[name] = advance();
        } else {
            uError() << "unexpected: " << m_source[m_srcIndex];
            skipStmt(QLatin1String("is"));
        }
        if (m_inGenericFormalPart) {
            if (ns)
                ns->setStereotype(QLatin1String("generic"));
            m_inGenericFormalPart = false;
        }
        return true;
    }
    if (m_inGenericFormalPart)
        return false;  // skip generic formal parameter (not yet implemented)
    if (keyword == QLatin1String("subtype")) {
        QString name = advance();
        advance();  // "is"
        QString base = expand(advance());
        base.remove(QLatin1String("Standard."), Qt::CaseInsensitive);
        UMLObject *type = umldoc->findUMLObject(base, UMLObject::ot_UMLObject, currentScope());
        if (type == NULL) {
            type = Import_Utils::createUMLObject(UMLObject::ot_Datatype, base, currentScope());
        }
        UMLObject *subtype = Import_Utils::createUMLObject(type->baseType(), name,
                                                           currentScope(), m_comment);
        UMLAssociation *assoc = new UMLAssociation(Uml::AssociationType::Dependency, subtype, type);
        assoc->setUMLPackage(umldoc->rootFolder(Uml::ModelType::Logical));
        assoc->setStereotype(QLatin1String("subtype"));
        // Work around missing display of stereotype in AssociationWidget:
        assoc->setName(assoc->stereotype(true));
        umldoc->addAssociation(assoc);
        skipStmt();
        return true;
    }
    if (keyword == QLatin1String("type")) {
        QString name = advance();
        QString next = advance();
        if (next == QLatin1String("(")) {
            uDebug() << name << ": discriminant handling is not yet implemented";
            // @todo Find out how to map discriminated record to UML.
            //       For now, we just create a pro forma empty record.
            Import_Utils::createUMLObject(UMLObject::ot_Class, name, currentScope(),
                                          m_comment, QLatin1String("record"));
            skipStmt(QLatin1String("end"));
            if ((next = advance()) == QLatin1String("case"))
                m_srcIndex += 2;  // skip "case" ";"
            skipStmt();
            return true;
        }
        if (next == QLatin1String(";")) {
            // forward declaration
            Import_Utils::createUMLObject(UMLObject::ot_Class, name, currentScope(),
                                          m_comment);
            return true;
        }
        if (next != QLatin1String("is")) {
            uError() << "expecting \"is\"";
            return false;
        }
        next = advance();
        if (next == QLatin1String("(")) {
            // enum type
            UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Enum,
                            name, currentScope(), m_comment);
            UMLEnum *enumType = static_cast<UMLEnum*>(ns);
            while ((next = advance()) != QLatin1String(")")) {
                Import_Utils::addEnumLiteral(enumType, next, m_comment);
                m_comment.clear();
                if (advance() != QLatin1String(","))
                    break;
            }
            skipStmt();
            return true;
        }
        bool isTaggedType = false;
        if (next == QLatin1String("abstract")) {
            m_isAbstract = true;
            next = advance();
        }
        if (next == QLatin1String("tagged")) {
            isTaggedType = true;
            next = advance();
        }
        if (next == QLatin1String("limited") ||
            next == QLatin1String("task") ||
            next == QLatin1String("protected") ||
            next == QLatin1String("synchronized")) {
            next = advance();  // we can't (yet?) represent that
        }
        if (next == QLatin1String("private") ||
            next == QLatin1String("interface") ||
            next == QLatin1String("record") ||
            (next == QLatin1String("null") &&
             m_source[m_srcIndex+1] == QLatin1String("record"))) {
            UMLObject::ObjectType t = (next == QLatin1String("interface") ? UMLObject::ot_Interface
                                                                          : UMLObject::ot_Class);
            UMLObject *ns = Import_Utils::createUMLObject(t, name, currentScope(), m_comment);
            if (t == UMLObject::ot_Interface) {
                while ((next = advance()) == QLatin1String("and")) {
                    UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
                    QString base = expand(advance());
                    UMLObject *p = Import_Utils::createUMLObject(UMLObject::ot_Interface, base, currentScope());
                    UMLClassifier *parent = static_cast<UMLClassifier*>(p);
                    Import_Utils::createGeneralization(klass, parent);
                }
            } else {
                ns->setAbstract(m_isAbstract);
            }
            m_isAbstract = false;
            if (isTaggedType) {
                if (! m_classesDefinedInThisScope.contains(ns))
                    m_classesDefinedInThisScope.append(ns);
            } else {
                ns->setStereotype(QLatin1String("record"));
            }
            if (next == QLatin1String("record"))
                m_klass = static_cast<UMLClassifier*>(ns);
            else
                skipStmt();
            return true;
        }
        if (next == QLatin1String("new")) {
            QString base = expand(advance());
            QStringList baseInterfaces;
            while ((next = advance()) == QLatin1String("and")) {
                baseInterfaces.append(expand(advance()));
            }
            const bool isExtension = (next == QLatin1String("with"));
            UMLObject::ObjectType t;
            if (isExtension || m_isAbstract) {
                t = UMLObject::ot_Class;
            } else {
                base.remove(QLatin1String("Standard."), Qt::CaseInsensitive);
                UMLObject *known = umldoc->findUMLObject(base, UMLObject::ot_UMLObject, currentScope());
                t = (known ? known->baseType() : UMLObject::ot_Datatype);
            }
            UMLObject *ns = Import_Utils::createUMLObject(t, base, NULL);
            UMLClassifier *parent = static_cast<UMLClassifier*>(ns);
            ns = Import_Utils::createUMLObject(t, name, currentScope(), m_comment);
            if (isExtension) {
                next = advance();
                if (next == QLatin1String("null") || next == QLatin1String("record")) {
                    UMLClassifier *klass = static_cast<UMLClassifier*>(ns);
                    Import_Utils::createGeneralization(klass, parent);
                    if (next == QLatin1String("record")) {
                        // Set the m_klass for attributes.
                        m_klass = klass;
                    }
                    if (baseInterfaces.count()) {
                        t = UMLObject::ot_Interface;
                        QStringList::Iterator end(baseInterfaces.end());
                        for (QStringList::Iterator bi(baseInterfaces.begin()); bi != end; ++bi) {
                             ns = Import_Utils::createUMLObject(t, *bi, currentScope());
                             parent = static_cast<UMLClassifier*>(ns);
                             Import_Utils::createGeneralization(klass, parent);
                        }
                    }
                }
            }
            skipStmt();
            return true;
        }
        // Datatypes: TO BE DONE
        return false;
    }
    if (keyword == QLatin1String("private")) {
        m_currentAccess = Uml::Visibility::Private;
        return true;
    }
    if (keyword == QLatin1String("end")) {
        if (m_klass) {
            if (advance() != QLatin1String("record")) {
                uError() << "end: expecting \"record\" at "
                          << m_source[m_srcIndex];
            }
            m_klass = NULL;
        } else if (scopeIndex()) {
            if (advance() != QLatin1String(";")) {
                QString scopeName = currentScope()->fullyQualifiedName();
                if (scopeName.toLower() != m_source[m_srcIndex].toLower())
                    uError() << "end: expecting " << scopeName << ", found "
                              << m_source[m_srcIndex];
            }
            popScope();
            m_currentAccess = Uml::Visibility::Public;   // @todo make a stack for this
        } else {
            uError() << "importAda: too many \"end\"";
        }
        skipStmt();
        return true;
    }
    // subprogram
    if (keyword == QLatin1String("not"))
        keyword = advance();
    if (keyword == QLatin1String("overriding"))
        keyword = advance();
    if (keyword == QLatin1String("function") || keyword == QLatin1String("procedure")) {
        const QString& name = advance();
        QString returnType;
        if (advance() != QLatin1String("(")) {
            // Unlike an Ada package, a UML package does not support
            // subprograms.
            // In order to map those, we would need to create a UML
            // class with stereotype <<utility>> for the Ada package.
            uDebug() << "ignoring parameterless " << keyword << " " << name;
            skipStmt();
            return true;
        }
        UMLClassifier *klass = NULL;
        UMLOperation *op = NULL;
        const uint MAX_PARNAMES = 16;
        while (m_srcIndex < srcLength && m_source[m_srcIndex] != QLatin1String(")")) {
            QString parName[MAX_PARNAMES];
            uint parNameCount = 0;
            do {
                if (parNameCount >= MAX_PARNAMES) {
                    uError() << "MAX_PARNAMES is exceeded at " << name;
                    break;
                }
                parName[parNameCount++] = advance();
            } while (advance() == QLatin1String(","));
            if (m_source[m_srcIndex] != QLatin1String(":")) {
                uError() << "importAda: expecting ':'";
                skipStmt();
                break;
            }
            const QString &direction = advance();
            QString typeName;
            Uml::ParameterDirection::Enum dir = Uml::ParameterDirection::In;
            if (direction == QLatin1String("access")) {
                // Oops, we have to improvise here because there
                // is no such thing as "access" in UML.
                // So we use the next best thing, "inout".
                // Better ideas, anyone?
                dir = Uml::ParameterDirection::InOut;
                typeName = advance();
            } else if (direction == QLatin1String("in")) {
                if (m_source[m_srcIndex + 1] == QLatin1String("out")) {
                    dir = Uml::ParameterDirection::InOut;
                    m_srcIndex++;
                }
                typeName = advance();
            } else if (direction == QLatin1String("out")) {
                dir = Uml::ParameterDirection::Out;
                typeName = advance();
            } else {
                typeName = direction;  // In Ada, the default direction is "in"
            }
            typeName.remove(QLatin1String("Standard."), Qt::CaseInsensitive);
            typeName = expand(typeName);
            if (op == NULL) {
                // In Ada, the first parameter indicates the class.
                UMLObject *type = Import_Utils::createUMLObject(UMLObject::ot_Class, typeName, currentScope());
                UMLObject::ObjectType t = type->baseType();
                if ((t != UMLObject::ot_Interface &&
                     (t != UMLObject::ot_Class || type->stereotype() == QLatin1String("record"))) ||
                    !m_classesDefinedInThisScope.contains(type)) {
                    // Not an instance bound method - we cannot represent it.
                    skipStmt(QLatin1String(")"));
                    break;
                }
                klass = static_cast<UMLClassifier*>(type);
                op = Import_Utils::makeOperation(klass, name);
                // The controlling parameter is suppressed.
                parNameCount--;
                if (parNameCount) {
                    for (uint i = 0; i < parNameCount; ++i) {
                        parName[i] = parName[i + 1];
                    }
                }
            }
            for (uint i = 0; i < parNameCount; ++i) {
                UMLAttribute *att = Import_Utils::addMethodParameter(op, typeName, parName[i]);
                att->setParmKind(dir);
            }
            if (advance() != QLatin1String(";"))
                break;
        }
        if (keyword == QLatin1String("function")) {
            if (advance() != QLatin1String("return")) {
                if (klass)
                    uError() << "importAda: expecting \"return\" at function "
                        << name;
                return false;
            }
            returnType = expand(advance());
            returnType.remove(QLatin1String("Standard."), Qt::CaseInsensitive);
        }
        bool isAbstract = false;
        if (advance() == QLatin1String("is") && advance() == QLatin1String("abstract"))
            isAbstract = true;
        if (klass != NULL && op != NULL)
            Import_Utils::insertMethod(klass, op, m_currentAccess, returnType,
                                       false, isAbstract, false, false, m_comment);
        skipStmt();
        return true;
    }
    if (keyword == QLatin1String("task") || keyword == QLatin1String("protected")) {
        // Can task and protected objects/types be mapped to UML?
        QString name = advance();
        if (name == QLatin1String("type")) {
            name = advance();
        }
        QString next = advance();
        if (next == QLatin1String("(")) {
            skipStmt(QLatin1String(")"));  // skip discriminant
            next = advance();
        }
        if (next == QLatin1String("is"))
            skipStmt(QLatin1String("end"));
        skipStmt();
        return true;
    }
    if (keyword == QLatin1String("for")) {    // rep spec
        QString typeName = advance();
        QString next = advance();
        if (next == QLatin1String("'")) {
            advance();  // skip qualifier
            next = advance();
        }
        if (next == QLatin1String("use")) {
            if (advance() == QLatin1String("record"))
                skipStmt(QLatin1String("end"));
        } else {
            uError() << "importAda: expecting \"use\" at rep spec of "
                      << typeName;
        }
        skipStmt();
        return true;
    }
    // At this point we're only interested in attribute declarations.
    if (m_klass == NULL || keyword == QLatin1String("null")) {
        skipStmt();
        return true;
    }
    const QString& name = keyword;
    if (advance() != QLatin1String(":")) {
        uError() << "adaImport: expecting \":\" at " << name << " "
                  << m_source[m_srcIndex];
        skipStmt();
        return true;
    }
    QString nextToken = advance();
    if (nextToken == QLatin1String("aliased"))
        nextToken = advance();
    QString typeName = expand(nextToken);
    QString initialValue;
    if (advance() == QLatin1String(":=")) {
        initialValue = advance();
        QString token;
        while ((token = advance()) != QLatin1String(";")) {
            initialValue.append(QLatin1Char(' ') + token);
        }
    }
    UMLObject *o = Import_Utils::insertAttribute(m_klass, m_currentAccess, name,
                                                 typeName, m_comment);
    if (o) {
        UMLAttribute *attr = static_cast<UMLAttribute*>(o);
        attr->setInitialValue(initialValue);
    }
    skipStmt();
    return true;
}
Beispiel #4
0
/**
 * Creates the <UML:Generalization> or <UML:Association> XMI element
 * including its role objects.
 */
bool UMLAssociation::load( QDomElement & element )
{
    if (id() == Uml::id_None)
        return false; // old style XMI file. No real info in this association.

    UMLDoc * doc = UMLApp::app()->document();
    UMLObject * obj[2] = { NULL, NULL };
    if (m_AssocType == Uml::AssociationType::Generalization ||
        m_AssocType == Uml::AssociationType::Realization    ||
        m_AssocType == Uml::AssociationType::Dependency     ||
        m_AssocType == Uml::AssociationType::Child2Category ||
        m_AssocType == Uml::AssociationType::Category2Parent
        ) {
        for (unsigned r = Uml::A; r <= Uml::B; ++r) {
            const QString fetch = (m_AssocType == Uml::AssociationType::Generalization ?
                                   r == Uml::A ? "child" : "parent"
                       : r == Uml::A ? "client" : "supplier");
            QString 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::ModelType mt = Model_Utils::convert_OT_MT(obj[r]->baseType());
                    m_pUMLPackage = doc->rootFolder(mt);
                    uDebug() << "assoctype " << m_AssocType
                        << ": setting model type " << mt;
                }
            }
        }
        if (obj[A] == NULL || obj[B] == NULL) {
            for (QDomNode node = element.firstChild(); !node.isNull();
                    node = node.nextSibling()) {
                if (node.isComment())
                    continue;
                QDomElement tempElement = node.toElement();
                QString tag = tempElement.tagName();
                if (Model_Utils::isCommonXMIAttribute(tag))
                    continue;
                // Permitted tag names:
                //  roleA: "child" "subtype" "client"
                //  roleB: "parent" "supertype" "supplier"
                QString idStr = tempElement.attribute( "xmi.id", "" );
                if (idStr.isEmpty())
                    idStr = tempElement.attribute( "xmi.idref", "" );
                if (idStr.isEmpty()) {
                    QDomNode inner = node.firstChild();
                    QDomElement tmpElem = inner.toElement();
                    idStr = tmpElem.attribute( "xmi.id", "" );
                    if (idStr.isEmpty())
                        idStr = tmpElem.attribute( "xmi.idref", "" );
                }
                if (idStr.isEmpty()) {
                    uError() << "type " << m_AssocType
                        << ", id " << ID2STR(id()) << ": "
                        << "xmi id not given for " << tag;
                    continue;
                }
                // Since we know for sure that we're dealing with a non
                // umbrello file, use deferred resolution unconditionally.
                if (UMLDoc::tagEq(tag, "child") || UMLDoc::tagEq(tag, "subtype") || UMLDoc::tagEq(tag, "client")) {
                    getUMLRole(A)->setSecondaryId(idStr);
                } else {
                    getUMLRole(B)->setSecondaryId(idStr);
                }
            }
        }

        // it is a realization if either endpoint is an interface
        if (isRealization(obj[A], obj[B])) {
            m_AssocType = Uml::AssociationType::Realization;
        }
        return true;
    }

    for (QDomNode node = element.firstChild(); !node.isNull();
            node = node.nextSibling()) {
        // uml13.dtd compliant format (new style)
        if (node.isComment())
            continue;
        QDomElement tempElement = node.toElement();
        QString tag = tempElement.tagName();
        if (Model_Utils::isCommonXMIAttribute(tag))
            continue;
        if (!UMLDoc::tagEq(tag, "Association.connection") &&
                !UMLDoc::tagEq(tag, "Namespace.ownedElement") &&
                !UMLDoc::tagEq(tag, "Namespace.contents")) {
            uWarning() << "unknown child node " << tag;
            continue;
        }
        // Load role A.
        node = tempElement.firstChild();
        while (node.isComment())
            node = node.nextSibling();
        tempElement = node.toElement();
        if (tempElement.isNull()) {
            uWarning() << "UML:Association : element (A) is Null";
            return false;
        }
        tag = tempElement.tagName();
        if (!UMLDoc::tagEq(tag, "AssociationEndRole") &&
                !UMLDoc::tagEq(tag, "AssociationEnd")) {
            uWarning() << "unknown child (A) tag " << tag;
            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()) {
            uWarning() << "UML:Association : element (B) is Null";
            return false;
        }
        tag = tempElement.tagName();
        if (!UMLDoc::tagEq(tag, "AssociationEndRole") &&
                !UMLDoc::tagEq(tag, "AssociationEnd")) {
            uWarning() << "unknown child (B) tag " << tag;
            return false;
        }
        if (! getUMLRole(B)->loadFromXMI(tempElement))
            return false;

        if (m_pUMLPackage == NULL) {
            Uml::ModelType mt = Model_Utils::convert_OT_MT(getObject(B)->baseType());
            m_pUMLPackage = doc->rootFolder(mt);
            uDebug() << "setting model type " << mt;
        }

        // 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::AssociationType::Association && getObjectId(A) == getObjectId(B))
            m_AssocType = Uml::AssociationType::Association_Self;

        // fall-back default type
        if (m_AssocType == Uml::AssociationType::Unknown) {
            m_AssocType = Uml::AssociationType::Association;
        }

        return true;
    }

    // From here on it's old-style stuff.
    QString assocTypeStr = element.attribute( "assoctype", "-1" );
    Uml::AssociationType assocType = Uml::AssociationType::Unknown;
    if (assocTypeStr[0] >= 'a' && assocTypeStr[0] <= 'z') {
        // In an earlier version, the natural assoctype names were saved.
        const QString assocTypeString[] = {
                    "generalization",   // Uml::AssociationType::Generalization
                    "aggregation",      // Uml::AssociationType::Aggregation
                    "dependency",       // Uml::AssociationType::Dependency
                    "association",      // Uml::AssociationType::Association
                    "associationself",  // Uml::AssociationType::Association_Self
                    "collmessage",      // Uml::AssociationType::Coll_Message
                    "seqmessage",       // Uml::AssociationType::Seq_Message
                    "collmessageself",  // Uml::AssociationType::Coll_Message_Self
                    "seqmessageself",   // Uml::AssociationType::Seq_Message_Self
                    "implementation",   // Uml::AssociationType::Implementation
                    "composition",      // Uml::AssociationType::Composition
                    "realization",      // Uml::AssociationType::Realization
                    "uniassociation",   // Uml::AssociationType::UniAssociation
                    "anchor",           // Uml::AssociationType::Anchor
                    "state",            // Uml::AssociationType::State
                    "activity",         // Uml::AssociationType::Activity
                    "exception",        // Uml::AssociationType::Exception
                    "category2parent"   // Uml::AssociationType::Category2Parent
                    "child2category"    // Uml::AssociationType::Child2Category
                    "relationship"      // Uml::AssociationType::Relationship
        };
        const int arraySize = sizeof(assocTypeString)/sizeof(QString);
        uDebug() << "AssociationType string array size = " << arraySize;

        int index;
        for (index = 0; index < arraySize; ++index)
            if (assocTypeStr == assocTypeString[index])
                break;
        if (index < arraySize)
            assocType = Uml::AssociationType::Value(index);
    } else {
        int assocTypeNum = assocTypeStr.toInt();
        if (assocTypeNum < (int)Uml::AssociationType::Generalization ||   // first enum
            assocTypeNum > (int)Uml::AssociationType::Relationship) {     // last enum
            uWarning() << "bad assoctype of UML:AssociationType " << ID2STR(id());
            return false;
        }
        assocType = Uml::AssociationType::Value(assocTypeNum);
    }
    setAssociationType( assocType );

    Uml::IDType roleAObjID = STR2ID(element.attribute( "rolea", "-1" ));
    Uml::IDType roleBObjID = STR2ID(element.attribute( "roleb", "-1" ));
    if (assocType == Uml::AssociationType::Aggregation ||
        assocType == Uml::AssociationType::Composition) {
        // Flip roles to compensate for changed diamond logic in AssociationLine.
        // 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..
    QString visibilityA = element.attribute( "visibilitya", "0");
    QString visibilityB = element.attribute( "visibilityb", "0");
    int vis = visibilityA.toInt();
    if (vis >= 200)  // bkwd compat.
        vis -= 200;
    setVisibility((Uml::Visibility::Value)vis, A);
    vis = visibilityB.toInt();
    if (vis >= 200)  // bkwd compat.
        vis -= 200;
    setVisibility((Uml::Visibility::Value)vis, B);

    // Changeability defaults to Changeable if it cant set it here..
    QString changeabilityA = element.attribute( "changeabilitya", "0");
    QString changeabilityB = element.attribute( "changeabilityb", "0");
    if (changeabilityA.toInt() > 0)
        setChangeability(Uml::Changeability(Uml::Changeability::Value(changeabilityA.toInt())), A);
    if (changeabilityB.toInt() > 0)
        setChangeability(Uml::Changeability(Uml::Changeability::Value(changeabilityB.toInt())), B);

    return true;
}