model_utils.cpp 62.2 KB
Newer Older
Oliver Kellogg's avatar
Oliver Kellogg committed
1 2 3 4 5 6
/***************************************************************************
 *   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.                                   *
 *                                                                         *
7
 *   copyright (C) 2004-2014                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
Oliver Kellogg's avatar
Oliver Kellogg committed
9 10 11 12 13 14
 ***************************************************************************/

// own header
#include "model_utils.h"

// app includes
15
#include "debug_utils.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
16
#include "umlobject.h"
17
#include "umlpackagelist.h"
18
#include "uniqueconstraint.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
19
#include "package.h"
20
#include "folder.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
21
#include "classifier.h"
22 23
#include "enum.h"
#include "entity.h"
24
#include "template.h"
25 26
#include "operation.h"
#include "attribute.h"
27 28
#include "association.h"
#include "umlrole.h"
29 30
#include "umldoc.h"
#include "uml.h"
31 32
#include "umllistview.h"
#include "umllistviewitem.h"
33
#include "umlscene.h"
34
#include "umlview.h"
35
#include "codegenerator.h"
Oliver Kellogg's avatar
Oliver Kellogg committed
36

37 38 39 40
// kde includes
#include <klocale.h>

// qt includes
41 42
#include <QRegExp>
#include <QStringList>
43

44
namespace Model_Utils {
Oliver Kellogg's avatar
Oliver Kellogg committed
45

46 47 48
/**
 * Determines whether the given widget type is cloneable.
 *
49
 * @param type  The input WidgetType.
50 51
 * @return      True if the given type is cloneable.
 */
52
bool isCloneable(WidgetBase::WidgetType type)
53
{
Oliver Kellogg's avatar
Oliver Kellogg committed
54
    switch (type) {
55 56 57 58 59 60 61 62
    case WidgetBase::wt_Actor:
    case WidgetBase::wt_UseCase:
    case WidgetBase::wt_Class:
    case WidgetBase::wt_Interface:
    case WidgetBase::wt_Enum:
    case WidgetBase::wt_Datatype:
    case WidgetBase::wt_Package:
    case WidgetBase::wt_Component:
Oliver Kellogg's avatar
Oliver Kellogg committed
63
    case WidgetBase::wt_Port:
64 65
    case WidgetBase::wt_Node:
    case WidgetBase::wt_Artifact:
Oliver Kellogg's avatar
Oliver Kellogg committed
66 67 68 69
        return true;
    default:
        return false;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
70 71
}

72 73 74 75 76 77 78 79 80
/**
 * Seek the given id in the given list of objects.
 * Each list element may itself contain other objects
 * and the search is done recursively.
 *
 * @param id       The unique ID to seek.
 * @param inList   The UMLObjectList in which to search.
 * @return Pointer to the UMLObject that matches the ID (NULL if none matches).
 */
Andi Fischer's avatar
Andi Fischer committed
81
UMLObject* findObjectInList(Uml::ID::Type id, const UMLObjectList& inList)
82
{
83
    for (UMLObjectListIt oit(inList); oit.hasNext();) {
84
        UMLObject *obj = oit.next();
85
        if (obj->id() == id)
Oliver Kellogg's avatar
Oliver Kellogg committed
86 87
            return obj;
        UMLObject *o;
88
        UMLObject::ObjectType t = obj->baseType();
Oliver Kellogg's avatar
Oliver Kellogg committed
89
        switch (t) {
90 91 92
        case UMLObject::ot_Folder:
        case UMLObject::ot_Package:
        case UMLObject::ot_Component:
93
            o = static_cast<UMLPackage*>(obj)->findObjectById(id);
Oliver Kellogg's avatar
Oliver Kellogg committed
94 95 96
            if (o)
                return o;
            break;
97 98 99 100
        case UMLObject::ot_Interface:
        case UMLObject::ot_Class:
        case UMLObject::ot_Enum:
        case UMLObject::ot_Entity:
101
            o = static_cast<UMLClassifier*>(obj)->findChildObjectById(id);
Oliver Kellogg's avatar
Oliver Kellogg committed
102
            if (o == NULL &&
103
                    (t == UMLObject::ot_Interface || t == UMLObject::ot_Class))
Oliver Kellogg's avatar
Oliver Kellogg committed
104 105 106 107
                o = ((UMLPackage*)obj)->findObjectById(id);
            if (o)
                return o;
            break;
108
        case UMLObject::ot_Association:
Oliver Kellogg's avatar
Oliver Kellogg committed
109
            {
110
                UMLAssociation *assoc = static_cast<UMLAssociation*>(obj);
111
                UMLRole *rA = assoc->getUMLRole(Uml::RoleType::A);
112
                if (rA->id() == id)
Oliver Kellogg's avatar
Oliver Kellogg committed
113
                    return rA;
114
                UMLRole *rB = assoc->getUMLRole(Uml::RoleType::B);
115
                if (rB->id() == id)
Oliver Kellogg's avatar
Oliver Kellogg committed
116 117 118 119 120 121 122 123
                    return rB;
            }
            break;
        default:
            break;
        }
    }
    return NULL;
Oliver Kellogg's avatar
Oliver Kellogg committed
124 125
}

126 127 128 129
/**
 * Find the UML object of the given type and name in the passed-in list.
 *
 * @param inList        List in which to seek the object.
130
 * @param inName        Name of the object to find.
131
 * @param type          ObjectType of the object to find (optional.)
132 133 134 135 136 137 138 139
 *                      When the given type is ot_UMLObject the type is
 *                      disregarded, i.e. the given name is the only
 *                      search criterion.
 * @param currentObj    Object relative to which to search (optional.)
 *                      If given then the enclosing scope(s) of this
 *                      object are searched before the global scope.
 * @return      Pointer to the UMLObject found, or NULL if not found.
 */
140 141
UMLObject* findUMLObject(const UMLObjectList& inList,
                         const QString& inName,
142
                         UMLObject::ObjectType type /* = ot_UMLObject */,
143 144
                         UMLObject *currentObj /* = NULL */)
{
145
    const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive();
146
    QString name = inName;
Oliver Kellogg's avatar
Oliver Kellogg committed
147
    const bool atGlobalScope = name.startsWith(QLatin1String("::"));
Oliver Kellogg's avatar
Oliver Kellogg committed
148 149 150 151
    if (atGlobalScope) {
        name = name.mid(2);
        currentObj = NULL;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
152
    QStringList components;
153 154 155 156
#ifdef TRY_BUGFIX_120682
    // If we have a pointer or a reference in cpp we need to remove
    // the asterisks and ampersands in order to find the appropriate object
    if (UMLApp::app()->getActiveLanguage() == Uml::pl_Cpp) {
Oliver Kellogg's avatar
Oliver Kellogg committed
157 158 159 160
        if (name.endsWith(QLatin1Char('*')))
            name.remove(QLatin1Char('*'));
        else if (name.contains(QLatin1Char('&')))
            name.remove(QLatin1Char('&'));
161 162
    }
#endif
Oliver Kellogg's avatar
Oliver Kellogg committed
163 164 165 166
    if (name.contains(QLatin1String("::")))
        components = name.split(QLatin1String("::"));
    else if (name.contains(QLatin1Char('.')))
        components = name.split(QLatin1Char('.'));
Oliver Kellogg's avatar
Oliver Kellogg committed
167 168 169 170
    QString nameWithoutFirstPrefix;
    if (components.size() > 1) {
        name = components.front();
        components.pop_front();
Oliver Kellogg's avatar
Oliver Kellogg committed
171
        nameWithoutFirstPrefix = components.join(QLatin1String("::"));
Oliver Kellogg's avatar
Oliver Kellogg committed
172 173 174 175
    }
    if (currentObj) {
        UMLPackage *pkg = NULL;
        if (dynamic_cast<UMLClassifierListItem*>(currentObj)) {
176
            currentObj = static_cast<UMLObject*>(currentObj->parent());
Oliver Kellogg's avatar
Oliver Kellogg committed
177
        }
178
        pkg = dynamic_cast<UMLPackage*>(currentObj);
Oliver Kellogg's avatar
Oliver Kellogg committed
179
        if (pkg == NULL || pkg->baseType() == UMLObject::ot_Association)
180
            pkg = currentObj->umlPackage();
Oliver Kellogg's avatar
Oliver Kellogg committed
181 182
        // Remember packages that we've seen - for avoiding cycles.
        UMLPackageList seenPkgs;
183
        for (; pkg; pkg = currentObj->umlPackage()) {
184 185 186 187
            if (nameWithoutFirstPrefix.isEmpty()
                && (type == UMLObject::ot_UMLObject ||
                    type == UMLObject::ot_Folder ||
                    type == UMLObject::ot_Package || type == pkg->baseType())) {
188
                if (caseSensitive) {
189
                    if (pkg->name() == name)
190
                        return pkg;
191
                } else if (pkg->name().toLower() == name.toLower()) {
192 193 194
                    return pkg;
                }
            }
195
            if (seenPkgs.indexOf(pkg) != -1) {
196
                uError() << "findUMLObject(" << name << "): "
197
                    << "breaking out of cycle involving "
198
                    << pkg->name();
Oliver Kellogg's avatar
Oliver Kellogg committed
199 200 201
                break;
            }
            seenPkgs.append(pkg);
202 203 204 205 206 207 208 209 210 211 212 213

            // exclude non package type
            // dynamic_cast<UMLPackage*>(pg) fails for unknown reason
            // see https://bugs.kde.org/show_bug.cgi?id=341709
            UMLObject::ObjectType foundType = pkg->baseType();
            if (foundType != UMLObject::ot_Package &&
                foundType != UMLObject::ot_Folder &&
                foundType != UMLObject::ot_Class &&
                foundType != UMLObject::ot_Interface &&
                foundType != UMLObject::ot_Component) {
                continue;
            }
Oliver Kellogg's avatar
Oliver Kellogg committed
214
            UMLObjectList objectsInCurrentScope = pkg->containedObjects();
215
            for (UMLObjectListIt oit(objectsInCurrentScope); oit.hasNext();) {
216
                UMLObject *obj = oit.next();
217
                if (caseSensitive) {
218
                    if (obj->name() != name)
219
                        continue;
220
                } else if (obj->name().toLower() != name.toLower()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
221
                    continue;
222
                }
223
                UMLObject::ObjectType foundType = obj->baseType();
Oliver Kellogg's avatar
Oliver Kellogg committed
224
                if (nameWithoutFirstPrefix.isEmpty()) {
225
                    if (type != UMLObject::ot_UMLObject && type != foundType) {
226
                        uDebug() << "type mismatch for "
227
                            << name << " (seeking type: "
228 229
                            << UMLObject::toString(type) << ", found type: "
                            << UMLObject::toString(foundType) << ")";
230 231 232
                        // Class, Interface, and Datatype are all Classifiers
                        // and are considered equivalent.
                        // The caller must be prepared to handle possible mismatches.
233 234 235 236 237 238
                        if ((type == UMLObject::ot_Class ||
                             type == UMLObject::ot_Interface ||
                             type == UMLObject::ot_Datatype) &&
                            (foundType == UMLObject::ot_Class ||
                             foundType == UMLObject::ot_Interface ||
                             foundType == UMLObject::ot_Datatype)) {
239 240
                            return obj;
                        }
Oliver Kellogg's avatar
Oliver Kellogg committed
241 242 243 244
                        continue;
                    }
                    return obj;
                }
245 246 247 248 249
                if (foundType != UMLObject::ot_Package &&
                    foundType != UMLObject::ot_Folder &&
                    foundType != UMLObject::ot_Class &&
                    foundType != UMLObject::ot_Interface &&
                    foundType != UMLObject::ot_Component) {
Oliver Kellogg's avatar
Oliver Kellogg committed
250 251
                    uDebug() << "found " << UMLObject::toString(foundType) << name
                             << " is not a package (?)";
Oliver Kellogg's avatar
Oliver Kellogg committed
252 253 254
                    continue;
                }
                UMLPackage *pkg = static_cast<UMLPackage*>(obj);
255 256
                return findUMLObject(pkg->containedObjects(),
                                      nameWithoutFirstPrefix, type);
Oliver Kellogg's avatar
Oliver Kellogg committed
257 258 259 260
            }
            currentObj = pkg;
        }
    }
261
    for (UMLObjectListIt oit(inList); oit.hasNext();) {
262
        UMLObject *obj = oit.next();
263
        if (caseSensitive) {
264
            if (obj->name() != name)
265
                continue;
266
        } else if (obj->name().toLower() != name.toLower()) {
Oliver Kellogg's avatar
Oliver Kellogg committed
267
            continue;
268
        }
269
        UMLObject::ObjectType foundType = obj->baseType();
Oliver Kellogg's avatar
Oliver Kellogg committed
270
        if (nameWithoutFirstPrefix.isEmpty()) {
271
            if (type != UMLObject::ot_UMLObject && type != foundType) {
272
                uDebug() << "type mismatch for "
273
                    << name << " (seeking type: "
274 275
                    << UMLObject::toString(type) << ", found type: "
                    << UMLObject::toString(foundType) << ")";
Oliver Kellogg's avatar
Oliver Kellogg committed
276 277 278 279
                continue;
            }
            return obj;
        }
280 281 282 283 284
        if (foundType != UMLObject::ot_Package &&
            foundType != UMLObject::ot_Folder &&
            foundType != UMLObject::ot_Class &&
            foundType != UMLObject::ot_Interface &&
            foundType != UMLObject::ot_Component) {
Oliver Kellogg's avatar
Oliver Kellogg committed
285 286
            uDebug() << "found " << name << "(" << UMLObject::toString(foundType) << ")"
                     << " is not a package (?)";
Oliver Kellogg's avatar
Oliver Kellogg committed
287 288 289
            continue;
        }
        UMLPackage *pkg = static_cast<UMLPackage*>(obj);
290 291
        return findUMLObject(pkg->containedObjects(),
                              nameWithoutFirstPrefix, type);
Oliver Kellogg's avatar
Oliver Kellogg committed
292 293
    }
    return NULL;
294 295
}

296 297
/**
 * Find the UML object of the given type and name in the passed-in list.
298
 * This method searches for the raw name.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
 *
 * @param inList        List in which to seek the object.
 * @param name          Name of the object to find.
 * @param type          ObjectType of the object to find (optional.)
 *                      When the given type is ot_UMLObject the type is
 *                      disregarded, i.e. the given name is the only
 *                      search criterion.
 * @param currentObj    Object relative to which to search (optional.)
 *                      If given then the enclosing scope(s) of this
 *                      object are searched before the global scope.
 * @return      Pointer to the UMLObject found, or NULL if not found.
 */
UMLObject* findUMLObjectRaw(const UMLObjectList& inList,
                            const QString& name,
                            UMLObject::ObjectType type /* = ot_UMLObject */,
                            UMLObject *currentObj /*= 0*/)
{
316
    Q_UNUSED(currentObj);
317
    for (UMLObjectListIt oit(inList); oit.hasNext();) {
318 319 320 321 322 323 324
        UMLObject *obj = oit.next();
        if (obj->name() == name && type == obj->baseType())
            return obj;
    }
    return NULL;
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
/**
 * Get the root folder of the given UMLObject.
 */
UMLPackage* rootPackage(UMLObject* obj)
{
    if (obj == NULL)
        return NULL;
    UMLPackage* root = obj->umlPackage();
    if (root == NULL) {
        root = dynamic_cast<UMLPackage*>(obj);
    } else {
        while (root->umlPackage() != NULL) {
            root = root->umlPackage();
        }
    }
    return root;
}

343 344 345 346 347 348
/**
 * Add the given list of views to the tree view.
 * @param viewList   the list of views to add
 */
void treeViewAddViews(const UMLViewList& viewList)
{
349
    UMLListView* tree = UMLApp::app()->listView();
350
    foreach (UMLView* v,  viewList) {
Ralf Habacker's avatar
Ralf Habacker committed
351
        if (tree->findItem(v->umlScene()->ID()) != NULL) {
352 353 354 355 356 357 358 359 360 361 362
            continue;
        }
        tree->createDiagramItem(v);
    }
}

/**
 * Change an icon of an object in the tree view.
 * @param object   the object in the treeViewAddViews
 * @param to       the new icon type for the given object
 */
Andi Fischer's avatar
Andi Fischer committed
363
void treeViewChangeIcon(UMLObject* object, Icon_Utils::IconType to)
364
{
365
    UMLListView* tree = UMLApp::app()->listView();
366 367 368 369 370 371 372 373 374
    tree->changeIconOf(object, to);
}

/**
 * Set the given object to the current item in the tree view.
 * @param object   the object which will be the current item
 */
void treeViewSetCurrentItem(UMLObject* object)
{
375
    UMLListView* tree = UMLApp::app()->listView();
376 377 378 379 380 381 382 383 384 385 386
    UMLListViewItem* item = tree->findUMLObject(object);
    tree->setCurrentItem(item);
}

/**
 * Move an object to a new container in the tree view.
 * @param container   the new container for the object
 * @param object      the to be moved object
 */
void treeViewMoveObjectTo(UMLObject* container, UMLObject* object)
{
387
    UMLListView *listView = UMLApp::app()->listView();
388
    UMLListViewItem *newParent = listView->findUMLObject(container);
389
    listView->moveObject(object->id(),
390 391 392 393 394 395 396 397 398 399
                   Model_Utils::convert_OT_LVT(object),
                   newParent);
}

/**
 * Return the current UMLObject from the tree view.
 * @return   the UML object of the current item
 */
UMLObject* treeViewGetCurrentObject()
{
400
    UMLListView *listView = UMLApp::app()->listView();
401
    UMLListViewItem *current = static_cast<UMLListViewItem*>(listView->currentItem());
402
    return current->umlObject();
403 404 405 406
}

/**
 * Return the UMLPackage if the current item
407 408 409
 * in the tree view is a package. Return the
 * closest package in the tree view or NULL otherwise
 *
410 411 412 413
 * @return   the package or NULL
 */
UMLPackage* treeViewGetPackageFromCurrent()
{
414
    UMLListView *listView = UMLApp::app()->listView();
415
    UMLListViewItem *parentItem = (UMLListViewItem*)listView->currentItem();
416
    while (parentItem) {
417
        UMLListViewItem::ListViewType lvt = parentItem->type();
418
        if (Model_Utils::typeIsContainer(lvt)) {
419
            UMLObject *o = parentItem->umlObject();
420 421
            return static_cast<UMLPackage*>(o);
        }
422 423 424 425

        // selected item is not a container, try to find the
        // container higher up in the tree view
        parentItem = static_cast<UMLListViewItem*>(parentItem->parent());
426
    }
427

428 429 430 431 432 433 434 435
    return NULL;
}

/**
 * Build the diagram name from the tree view.
 * @param id   the id of the diaram
 * @return     the constructed diagram name
 */
Andi Fischer's avatar
Andi Fischer committed
436
QString treeViewBuildDiagramName(Uml::ID::Type id)
437
{
438
    UMLListView *listView = UMLApp::app()->listView();
439 440 441 442 443 444 445 446 447 448
    UMLListViewItem* listViewItem = listView->findItem(id);

    if (listViewItem) {
        // skip the name of the first item because it's the View
        listViewItem = static_cast<UMLListViewItem*>(listViewItem->parent());
        
        // Relies on the tree structure of the UMLListView. There are a base "Views" folder
        // and five children, one for each view type (Logical, use case, components, deployment
        // and entity relationship)
        QString name;
449
        while (listView->rootView(listViewItem->type()) == NULL) {
Oliver Kellogg's avatar
Oliver Kellogg committed
450
            name.insert(0, listViewItem->text(0) + QLatin1Char('/'));
451 452 453 454 455 456 457 458 459 460 461 462
            listViewItem = static_cast<UMLListViewItem*>(listViewItem->parent());
            if (listViewItem == NULL)
                break;
        }
        return name;
    }
    else {
        uWarning() << "diagram not found - returning empty name!";
        return QString();
    }
}

463 464 465 466 467 468 469 470 471 472
/**
 * Returns a name for the new object, appended with a number
 * if the default name is taken e.g. new_actor, new_actor_1
 * etc.
 * @param type      The object type.
 * @param parentPkg The package in which to compare the name.
 * @param prefix    The prefix to use (optional)
 *                  If no prefix is given then a type related
 *                  prefix will be chosen internally.
 */
473
QString uniqObjectName(UMLObject::ObjectType type, UMLPackage *parentPkg, QString prefix)
474
{
475 476
    QString currentName = prefix;
    if (currentName.isEmpty()) {
477
        if(type == UMLObject::ot_Class)
478
            currentName = i18n("new_class");
479
        else if(type == UMLObject::ot_Actor)
480
            currentName = i18n("new_actor");
481
        else if(type == UMLObject::ot_UseCase)
482
            currentName = i18n("new_usecase");
483
        else if(type == UMLObject::ot_Package)
484
            currentName = i18n("new_package");
485
        else if(type == UMLObject::ot_Component)
486
            currentName = i18n("new_component");
Oliver Kellogg's avatar
Oliver Kellogg committed
487 488
        else if(type == UMLObject::ot_Port)
            currentName = i18n("new_port");
489
        else if(type == UMLObject::ot_Node)
490
            currentName = i18n("new_node");
491
        else if(type == UMLObject::ot_Artifact)
492
            currentName = i18n("new_artifact");
493
        else if(type == UMLObject::ot_Interface)
494
            currentName = i18n("new_interface");
495
        else if(type == UMLObject::ot_Datatype)
496
            currentName = i18n("new_datatype");
497
        else if(type == UMLObject::ot_Enum)
498
            currentName = i18n("new_enum");
499
        else if(type == UMLObject::ot_Entity)
500
            currentName = i18n("new_entity");
501
        else if(type == UMLObject::ot_Folder)
502
            currentName = i18n("new_folder");
503
        else if(type == UMLObject::ot_Association)
504
            currentName = i18n("new_association");
505
        else if(type == UMLObject::ot_Category)
506
            currentName = i18n("new_category");
507 508
        else {
            currentName = i18n("new_object");
509
            uWarning() << "unknown object type in umldoc::uniqObjectName()";
510 511
        }
    }
512
    UMLDoc *doc = UMLApp::app()->document();
513
    QString name = currentName;
514
    for (int number = 1; !doc->isUnique(name, parentPkg); ++number)  {
Oliver Kellogg's avatar
Oliver Kellogg committed
515
        name = currentName + QLatin1Char('_') + QString::number(number);
516 517 518 519
    }
    return name;
}

520
/**
521 522 523 524
 * Return the xmi.id (XMI-1) or xmi:id (XMI-2) of a QDomElement.
 */
QString getXmiId(QDomElement element)
{
Oliver Kellogg's avatar
Oliver Kellogg committed
525
    QString idStr = element.attribute(QLatin1String("xmi.id"));
526
    if (idStr.isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
527
        idStr = element.attribute(QLatin1String("xmi:id"));
528 529 530 531 532
    return idStr;
}

/**
 * Return true if the given tag is one of the common XMI
533 534 535 536
 * attributes, such as:
 * "name" | "visibility" | "isRoot" | "isLeaf" | "isAbstract" |
 * "isActive" | "ownerScope"
 */
537
bool isCommonXMIAttribute(const QString &tag)
538
{
Oliver Kellogg's avatar
Oliver Kellogg committed
539 540 541 542 543 544 545 546 547 548 549 550 551 552
    bool retval = (UMLDoc::tagEq(tag, QLatin1String("name")) ||
                   UMLDoc::tagEq(tag, QLatin1String("visibility")) ||
                   UMLDoc::tagEq(tag, QLatin1String("isRoot")) ||
                   UMLDoc::tagEq(tag, QLatin1String("isLeaf")) ||
                   UMLDoc::tagEq(tag, QLatin1String("isAbstract")) ||
                   UMLDoc::tagEq(tag, QLatin1String("isSpecification")) ||
                   UMLDoc::tagEq(tag, QLatin1String("isActive")) ||
                   UMLDoc::tagEq(tag, QLatin1String("namespace")) ||
                   UMLDoc::tagEq(tag, QLatin1String("ownerScope")) ||
                   UMLDoc::tagEq(tag, QLatin1String("ModelElement.stereotype")) ||
                   UMLDoc::tagEq(tag, QLatin1String("GeneralizableElement.generalization")) ||
                   UMLDoc::tagEq(tag, QLatin1String("specialization")) ||   //NYI
                   UMLDoc::tagEq(tag, QLatin1String("clientDependency")) || //NYI
                   UMLDoc::tagEq(tag, QLatin1String("supplierDependency"))  //NYI
553
                 );
Oliver Kellogg's avatar
Oliver Kellogg committed
554
    return retval;
555
}
Oliver Kellogg's avatar
Oliver Kellogg committed
556

557 558 559 560 561
/**
 * Return true if the given type is common among the majority
 * of programming languages, such as "bool" or "boolean".
 * TODO: Make this depend on the active programming language.
 */
562 563
bool isCommonDataType(QString type)
{
564
    CodeGenerator *gen = UMLApp::app()->generator();
565 566
    if (gen == NULL)
        return false;
567
    const bool caseSensitive = UMLApp::app()->activeLanguageIsCaseSensitive();
568 569 570
    const QStringList dataTypes = gen->defaultDatatypes();
    QStringList::ConstIterator end(dataTypes.end());
    for (QStringList::ConstIterator it = dataTypes.begin(); it != end; ++it) {
571 572 573
        if (caseSensitive) {
            if (type == *it)
                return true;
574
        } else if (type.toLower() == (*it).toLower()) {
575
            return true;
576
        }
577 578 579 580
    }
    return false;
}

581 582 583
/**
 * Return true if the given object type is a classifier list item type.
 */
584
bool isClassifierListitem(UMLObject::ObjectType type)
585
{
586 587 588 589 590 591 592 593
    if (type == UMLObject::ot_Attribute ||
        type == UMLObject::ot_Operation ||
        type == UMLObject::ot_Template ||
        type == UMLObject::ot_EntityAttribute ||
        type == UMLObject::ot_EnumLiteral ||
        type == UMLObject::ot_UniqueConstraint ||
        type == UMLObject::ot_ForeignKeyConstraint  ||
        type == UMLObject::ot_CheckConstraint) {
594 595 596 597 598 599
        return true;
    } else {
        return false;
    }
}

600 601
/**
 * Try to guess the correct container folder type of an UMLObject.
602
 * Object types that can't be guessed are mapped to Uml::ModelType::Logical.
603 604 605
 * NOTE: This function exists mainly for handling pre-1.5.5 files
 *       and should not be used for new code.
 */
Andi Fischer's avatar
Andi Fischer committed
606
Uml::ModelType::Enum guessContainer(UMLObject *o)
607
{
608
    UMLObject::ObjectType ot = o->baseType();
Oliver Kellogg's avatar
Oliver Kellogg committed
609
    if (ot == UMLObject::ot_Package && o->stereotype() == QLatin1String("subsystem"))
610
        return Uml::ModelType::Component;
Andi Fischer's avatar
Andi Fischer committed
611
    Uml::ModelType::Enum mt = Uml::ModelType::N_MODELTYPES;
612
    switch (ot) {
Oliver Kellogg's avatar
Oliver Kellogg committed
613
        case UMLObject::ot_Package:   // trouble: package can also appear in Component view
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
        case UMLObject::ot_Interface:
        case UMLObject::ot_Datatype:
        case UMLObject::ot_Enum:
        case UMLObject::ot_Class:
        case UMLObject::ot_Attribute:
        case UMLObject::ot_Operation:
        case UMLObject::ot_EnumLiteral:
        case UMLObject::ot_Template:
            mt = Uml::ModelType::Logical;
            break;
        case UMLObject::ot_Actor:
        case UMLObject::ot_UseCase:
            mt = Uml::ModelType::UseCase;
            break;
        case UMLObject::ot_Component:
Oliver Kellogg's avatar
Oliver Kellogg committed
629
        case UMLObject::ot_Port:
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
        case UMLObject::ot_Artifact:  // trouble: artifact can also appear at Deployment
            mt = Uml::ModelType::Component;
            break;
        case UMLObject::ot_Node:
            mt = Uml::ModelType::Deployment;
            break;
        case UMLObject::ot_Entity:
        case UMLObject::ot_EntityAttribute:
        case UMLObject::ot_UniqueConstraint:
        case UMLObject::ot_ForeignKeyConstraint:
        case UMLObject::ot_CheckConstraint:
        case UMLObject::ot_Category:
            mt = Uml::ModelType::EntityRelationship;
            break;
        case UMLObject::ot_Association:
645 646
            {
                UMLAssociation *assoc = static_cast<UMLAssociation*>(o);
647
                UMLDoc *umldoc = UMLApp::app()->document();
648 649
                for (int r = Uml::RoleType::A; r <= Uml::RoleType::B; ++r) {
                    UMLObject *roleObj = assoc->getObject(Uml::RoleType::fromInt(r));
650 651
                    if (roleObj == NULL) {
                        // Ouch! we have been called while types are not yet resolved
652
                        return Uml::ModelType::N_MODELTYPES;
653
                    }
654
                    UMLPackage *pkg = roleObj->umlPackage();
655
                    if (pkg) {
656 657
                        while (pkg->umlPackage()) {  // wind back to root
                            pkg = pkg->umlPackage();
658
                        }
Andi Fischer's avatar
Andi Fischer committed
659
                        const Uml::ModelType::Enum m = umldoc->rootFolderType(pkg);
660
                        if (m != Uml::ModelType::N_MODELTYPES)
661
                            return m;
662 663
                    }
                    mt = guessContainer(roleObj);
664
                    if (mt != Uml::ModelType::Logical)
665 666 667 668 669 670 671 672 673 674
                        break;
                }
            }
            break;
        default:
            break;
    }
    return mt;
}

675
/**
676
 * Parse a direction string into the Uml::ParameterDirection::Enum.
677 678 679
 *
 * @param input  The string to parse: "in", "out", or "inout"
 *               optionally followed by whitespace.
680
 * @param result The corresponding Uml::ParameterDirection::Enum.
681 682 683
 * @return       Length of the string matched, excluding the optional
 *               whitespace.
 */
684
int stringToDirection(QString input, Uml::ParameterDirection::Enum & result)
685
{
Oliver Kellogg's avatar
Oliver Kellogg committed
686
    QRegExp dirx(QLatin1String("^(in|out|inout)"));
Christian Ehrlicher's avatar
Christian Ehrlicher committed
687
    int pos = dirx.indexIn(input);
Oliver Kellogg's avatar
Oliver Kellogg committed
688 689
    if (pos == -1)
        return 0;
690
    const QString dirStr = dirx.capturedTexts().first();
691
    int dirLen = dirStr.length();
Oliver Kellogg's avatar
Oliver Kellogg committed
692
    if (input.length() > dirLen && !input[dirLen].isSpace())
693
        return 0;       // no match after all.
Oliver Kellogg's avatar
Oliver Kellogg committed
694
    if (dirStr == QLatin1String("out"))
695
        result = Uml::ParameterDirection::Out;
Oliver Kellogg's avatar
Oliver Kellogg committed
696
    else if (dirStr == QLatin1String("inout"))
697
        result = Uml::ParameterDirection::InOut;
Oliver Kellogg's avatar
Oliver Kellogg committed
698
    else
699
        result = Uml::ParameterDirection::In;
Oliver Kellogg's avatar
Oliver Kellogg committed
700
    return dirLen;
Oliver Kellogg's avatar
Oliver Kellogg committed
701 702
}

703 704 705 706 707 708 709 710 711 712
/**
 * Parses a template parameter given in UML syntax.
 *
 * @param t             Input text of the template parameter.
 *                      Example:  parname : partype
 *                      or just:  parname          (for class type)
 * @param nmTp          NameAndType returned by this method.
 * @param owningScope   Pointer to the owning scope of the template param.
 * @return      Error status of the parse, PS_OK for success.
 */
713 714
Parse_Status parseTemplate(QString t, NameAndType& nmTp, UMLClassifier *owningScope)
{
715
    UMLDoc *pDoc = UMLApp::app()->document();
Oliver Kellogg's avatar
Oliver Kellogg committed
716

Laurent Montel's avatar
Laurent Montel committed
717
    t = t.trimmed();
Oliver Kellogg's avatar
Oliver Kellogg committed
718 719
    if (t.isEmpty())
        return PS_Empty;
Oliver Kellogg's avatar
Oliver Kellogg committed
720

Oliver Kellogg's avatar
Oliver Kellogg committed
721
    QStringList nameAndType = t.split(QRegExp(QLatin1String("\\s*:\\s*")));
Oliver Kellogg's avatar
Oliver Kellogg committed
722 723
    if (nameAndType.count() == 2) {
        UMLObject *pType = NULL;
Oliver Kellogg's avatar
Oliver Kellogg committed
724
        if (nameAndType[1] != QLatin1String("class")) {
725
            pType = pDoc->findUMLObject(nameAndType[1], UMLObject::ot_UMLObject, owningScope);
Oliver Kellogg's avatar
Oliver Kellogg committed
726 727 728 729 730 731 732 733
            if (pType == NULL)
                return PS_Unknown_ArgType;
        }
        nmTp = NameAndType(nameAndType[0], pType);
    } else {
        nmTp = NameAndType(t, NULL);
    }
    return PS_OK;
Oliver Kellogg's avatar
Oliver Kellogg committed
734 735
}

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
/**
 * Parses an attribute given in UML syntax.
 *
 * @param a             Input text of the attribute in UML syntax.
 *                      Example:  argname : argtype
 * @param nmTp          NameAndType returned by this method.
 * @param owningScope   Pointer to the owning scope of the attribute.
 * @param vis           Optional pointer to visibility (return value.)
 *                      The visibility may be given at the beginning of the
 *                      attribute text in mnemonic form as follows:
 *                      "+"  stands for public
 *                      "#"  stands for protected
 *                      "-"  stands for private
 *                      "~"  stands for implementation level visibility
 *
 * @return      Error status of the parse, PS_OK for success.
 */
753
Parse_Status parseAttribute(QString a, NameAndType& nmTp, UMLClassifier *owningScope,
Andi Fischer's avatar
Andi Fischer committed
754
                            Uml::Visibility::Enum *vis /* = 0 */)
755
{
756
    UMLDoc *pDoc = UMLApp::app()->document();
757

758
    a = a.simplified();
Oliver Kellogg's avatar
Oliver Kellogg committed
759 760
    if (a.isEmpty())
        return PS_Empty;
761

Oliver Kellogg's avatar
Oliver Kellogg committed
762
    int colonPos = a.indexOf(QLatin1Char(':'));
763 764 765 766
    if (colonPos < 0) {
        nmTp = NameAndType(a, NULL);
        return PS_OK;
    }
Laurent Montel's avatar
Laurent Montel committed
767
    QString name = a.left(colonPos).trimmed();
768
    if (vis) {
Oliver Kellogg's avatar
Oliver Kellogg committed
769
        QRegExp mnemonicVis(QLatin1String("^([\\+\\#\\-\\~] *)"));
Christian Ehrlicher's avatar
Christian Ehrlicher committed
770
        int pos = mnemonicVis.indexIn(name);
771 772 773 774 775
        if (pos == -1) {
            *vis = Uml::Visibility::Private;  // default value
        } else {
            QString caption = mnemonicVis.cap(1);
            QString strVis = caption.left(1);
Oliver Kellogg's avatar
Oliver Kellogg committed
776
            if (strVis == QLatin1String("+"))
777
                *vis = Uml::Visibility::Public;
Oliver Kellogg's avatar
Oliver Kellogg committed
778
            else if (strVis == QLatin1String("#"))
779
                *vis = Uml::Visibility::Protected;
Oliver Kellogg's avatar
Oliver Kellogg committed
780
            else if (strVis == QLatin1String("-"))
781 782 783 784 785 786
                *vis = Uml::Visibility::Private;
            else
                *vis = Uml::Visibility::Implementation;
        }
        name.remove(mnemonicVis);
    }
787
    Uml::ParameterDirection::Enum pd = Uml::ParameterDirection::In;
Oliver Kellogg's avatar
Oliver Kellogg committed
788
    if (name.startsWith(QLatin1String(QLatin1String("in ")))) {
789
        pd = Uml::ParameterDirection::In;
790
        name = name.mid(3);
Oliver Kellogg's avatar
Oliver Kellogg committed
791
    } else if (name.startsWith(QLatin1String(QLatin1String("inout ")))) {
792
        pd = Uml::ParameterDirection::InOut;
793
        name = name.mid(6);
Oliver Kellogg's avatar
Oliver Kellogg committed
794
    } else if (name.startsWith(QLatin1String(QLatin1String("out ")))) {
795
        pd = Uml::ParameterDirection::Out;
796 797
        name = name.mid(4);
    }
Laurent Montel's avatar
Laurent Montel committed
798
    a = a.mid(colonPos + 1).trimmed();
799 800 801 802
    if (a.isEmpty()) {
        nmTp = NameAndType(name, NULL, pd);
        return PS_OK;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
803
    QStringList typeAndInitialValue = a.split(QRegExp(QLatin1String("\\s*=\\s*")));
804
    const QString &type = typeAndInitialValue[0];
805
    UMLObject *pType = pDoc->findUMLObject(type, UMLObject::ot_UMLObject, owningScope);
806 807 808 809
    if (pType == NULL) {
        nmTp = NameAndType(name, NULL, pd);
        return PS_Unknown_ArgType;
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
810
    QString initialValue;
811 812
    if (typeAndInitialValue.count() == 2) {
        initialValue = typeAndInitialValue[1];
Oliver Kellogg's avatar
Oliver Kellogg committed
813
    }
814
    nmTp = NameAndType(name, pType, pd, initialValue);
Oliver Kellogg's avatar
Oliver Kellogg committed
815
    return PS_OK;
816 817
}

818 819 820 821 822 823 824 825 826 827
/**
 * Parses an operation given in UML syntax.
 *
 * @param m             Input text of the operation in UML syntax.
 *                      Example of a two-argument operation returning "void":
 *                      methodname (arg1name : arg1type, arg2name : arg2type) : void
 * @param desc          OpDescriptor returned by this method.
 * @param owningScope   Pointer to the owning scope of the operation.
 * @return      Error status of the parse, PS_OK for success.
 */
828 829
Parse_Status parseOperation(QString m, OpDescriptor& desc, UMLClassifier *owningScope)
{
830
    UMLDoc *pDoc = UMLApp::app()->document();
831

832
    m = m.simplified();
Oliver Kellogg's avatar
Oliver Kellogg committed
833 834
    if (m.isEmpty())
        return PS_Empty;
Oliver Kellogg's avatar
Oliver Kellogg committed
835
    if (m.contains(QRegExp(QLatin1String("operator *()")))) {
Oliver Kellogg's avatar
Oliver Kellogg committed
836
        // C++ special case: two sets of parentheses
Oliver Kellogg's avatar
Oliver Kellogg committed
837 838
        desc.m_name = QLatin1String("operator()");
        m.remove(QRegExp(QLatin1String("operator *()")));
Oliver Kellogg's avatar
Oliver Kellogg committed
839 840 841 842 843 844
    } else {
        /**
         * The search pattern includes everything up to the opening parenthesis
         * because UML also permits non programming-language oriented designs
         * using narrative names, for example "check water temperature".
         */
Oliver Kellogg's avatar
Oliver Kellogg committed
845
        QRegExp beginningUpToOpenParenth(QLatin1String("^([^\\(]+)"));
Christian Ehrlicher's avatar
Christian Ehrlicher committed