umlobject.cpp 41.1 KB
Newer Older
Thibault Normand's avatar
Thibault Normand 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.                                   *
 *                                                                         *
Oliver Kellogg's avatar
Oliver Kellogg committed
7
 *   copyright (C) 2002-2014                                               *
Ralf Habacker's avatar
Ralf Habacker committed
8
 *   Umbrello UML Modeller Authors <umbrello-devel@kde.org>                *
Thibault Normand's avatar
Thibault Normand committed
9
10
11
12
 ***************************************************************************/

// own header
#include "umlobject.h"
13

Thibault Normand's avatar
Thibault Normand committed
14
// app includes
15
#include "classpropertiesdialog.h"
16
#include "debug_utils.h"
17
#include "enumliteral.h"
Thibault Normand's avatar
Thibault Normand committed
18
19
20
21
#include "uniqueid.h"
#include "uml.h"
#include "umldoc.h"
#include "umllistview.h"
22
#include "umlobjectprivate.h"
23
#include "models/objectsmodel.h"
Thibault Normand's avatar
Thibault Normand committed
24
25
26
27
28
#include "package.h"
#include "folder.h"
#include "stereotype.h"
#include "object_factory.h"
#include "model_utils.h"
29
#include "import_utils.h"
Thibault Normand's avatar
Thibault Normand committed
30
#include "docwindow.h"
Pierre Pettera's avatar
Pierre Pettera committed
31
32
#include "cmds.h"

33
// kde includes
34
#include <KLocalizedString>
35
36

// qt includes
37
38
#include <QApplication>
#include <QPointer>
39

Pierre Pettera's avatar
Pierre Pettera committed
40
41
using namespace Uml;

42
43
DEBUG_REGISTER_DISABLED(UMLObject)

Ralf Habacker's avatar
Ralf Habacker committed
44
45
46
47
48
/**
 * Creates a UMLObject.
 * @param other object to created from
 */
UMLObject::UMLObject(const UMLObject &other)
49
50
  : QObject(other.umlParent()),
    m_d(new UMLObjectPrivate)
Ralf Habacker's avatar
Ralf Habacker committed
51
52
{
    other.copyInto(this);
53
    UMLApp::app()->document()->objectsModel()->add(this);
Ralf Habacker's avatar
Ralf Habacker committed
54
55
}

56
57
58
59
60
61
62
/**
 * Creates a UMLObject.
 * @param parent   The parent of the object.
 * @param name     The name of the object.
 * @param id       The ID of the object (optional.) If omitted
 *                 then a new ID will be assigned internally.
 */
Andi Fischer's avatar
Andi Fischer committed
63
UMLObject::UMLObject(UMLObject* parent, const QString& name, ID::Type id)
64
65
  : QObject(parent),
    m_nId(id),
66
67
    m_name(name),
    m_d(new UMLObjectPrivate)
Andi Fischer's avatar
Andi Fischer committed
68
{
Thibault Normand's avatar
Thibault Normand committed
69
    init();
Andi Fischer's avatar
Andi Fischer committed
70
    if (id == Uml::ID::None)
Thibault Normand's avatar
Thibault Normand committed
71
        m_nId = UniqueID::gen();
72
    UMLApp::app()->document()->objectsModel()->add(this);
Thibault Normand's avatar
Thibault Normand committed
73
74
}

75
76
77
78
79
80
/**
 * Creates a UMLObject.
 * @param name     The name of the object.
 * @param id       The ID of the object (optional.) If omitted
 *                 then a new ID will be assigned internally.
 */
Andi Fischer's avatar
Andi Fischer committed
81
UMLObject::UMLObject(const QString& name, ID::Type id)
82
  : QObject(0),
83
    m_nId(id),
84
85
    m_name(name),
    m_d(new UMLObjectPrivate)
Andi Fischer's avatar
Andi Fischer committed
86
{
Thibault Normand's avatar
Thibault Normand committed
87
    init();
Andi Fischer's avatar
Andi Fischer committed
88
    if (id == Uml::ID::None)
Thibault Normand's avatar
Thibault Normand committed
89
        m_nId = UniqueID::gen();
90
    UMLApp::app()->document()->objectsModel()->add(this);
Thibault Normand's avatar
Thibault Normand committed
91
92
}

93
94
95
96
/**
 * Creates a UMLObject.
 * @param   parent   The parent of the object.
 */
97
UMLObject::UMLObject(UMLObject * parent)
98
  : QObject(parent),
Andi Fischer's avatar
Andi Fischer committed
99
    m_nId(Uml::ID::None),
100
101
    m_name(QString()),
    m_d(new UMLObjectPrivate)
Andi Fischer's avatar
Andi Fischer committed
102
{
Thibault Normand's avatar
Thibault Normand committed
103
    init();
104
    UMLApp::app()->document()->objectsModel()->add(this);
Thibault Normand's avatar
Thibault Normand committed
105
106
}

107
108
109
/**
 * Standard destructor.
 */
Andi Fischer's avatar
Andi Fischer committed
110
111
UMLObject::~UMLObject()
{
112
113
114
    // unref stereotype
    setUMLStereotype(0);
    if (m_pSecondary && m_pSecondary->baseType() == ot_Stereotype) {
115
        UMLStereotype* stereotype = m_pSecondary->asUMLStereotype();
116
117
118
        if (stereotype)
            stereotype->decrRefCount();
    }
119
    UMLApp::app()->document()->objectsModel()->remove(this);
120
    delete m_d;
Thibault Normand's avatar
Thibault Normand committed
121
122
}

123
124
125
/**
 * Initializes key variables of the class.
 */
Andi Fischer's avatar
Andi Fischer committed
126
127
void UMLObject::init()
{
Oliver Kellogg's avatar
Oliver Kellogg committed
128
    setObjectName(QLatin1String("UMLObject"));
129
    m_BaseType = ot_UMLObject;
Andi Fischer's avatar
Andi Fischer committed
130
    m_visibility = Uml::Visibility::Public;
131
    m_pStereotype = 0;
132
    m_Doc.clear();
Thibault Normand's avatar
Thibault Normand committed
133
134
135
    m_bAbstract = false;
    m_bStatic = false;
    m_bCreationWasSignalled = false;
136
    m_pSecondary = 0;
Thibault Normand's avatar
Thibault Normand committed
137
138
}

139
/**
140
 * Display the properties configuration dialog for the object.
141
 *
142
143
 * @param parent    The parent widget.
 * @return  True for success of this operation.
144
 */
145
bool UMLObject::showPropertiesDialog(QWidget *parent)
Andi Fischer's avatar
Andi Fischer committed
146
{
147
    DocWindow *docwindow = UMLApp::app()->docWindow();
Thibault Normand's avatar
Thibault Normand committed
148
    docwindow->updateDocumentation(false);
149
    QPointer<ClassPropertiesDialog> dlg = new ClassPropertiesDialog(parent, this, false);
Thibault Normand's avatar
Thibault Normand committed
150
151
152
    bool modified = false;
    if (dlg->exec()) {
        docwindow->showDocumentation(this, true);
153
        UMLApp::app()->document()->setModified(true);
Thibault Normand's avatar
Thibault Normand committed
154
155
        modified = true;
    }
156
    dlg->close();
157
    delete dlg;
Thibault Normand's avatar
Thibault Normand committed
158
159
160
    return modified;
}

161
162
163
164
165
166
167
168
169
170
171
172
/**
 * This should be reimplemented by subclasses if they wish to
 * accept certain types of associations. Note that this only
 * tells if this UMLObject can accept the association
 * type. When creating an association another check is made to
 * see if the association is valid. For example a UMLClass
 * (UMLClassifier) can accept generalizations and should
 * return true. If while creating a generalization the
 * superclass is already subclassed from this, the association
 * is not valid and will not be created.  The default accepts
 * nothing (returns false)
 */
173
bool UMLObject::acceptAssociationType(Uml::AssociationType::Enum type)
Andi Fischer's avatar
Andi Fischer committed
174
{
175
    Q_UNUSED(type);
Andi Fischer's avatar
Andi Fischer committed
176
    // A UMLObject accepts nothing. This should be reimplemented by the subclasses
Thibault Normand's avatar
Thibault Normand committed
177
178
179
    return false;
}

180
181
182
/**
 * Assigns a new Id to the object
 */
Andi Fischer's avatar
Andi Fischer committed
183
void UMLObject::setID(ID::Type NewID)
Andi Fischer's avatar
Andi Fischer committed
184
{
Thibault Normand's avatar
Thibault Normand committed
185
    m_nId = NewID;
186
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
187
188
}

189
190
191
/**
 * Set the UMLObject's name
 */
Andi Fischer's avatar
Andi Fischer committed
192
193
void UMLObject::setName(const QString &strName)
{
194
195
196
    if (name() != strName) {
        UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(this, strName));
    }
Pierre Pettera's avatar
Pierre Pettera committed
197
198
}

199
/**
Frederik Schwarzer's avatar
Frederik Schwarzer committed
200
 * Method used by setName: it is called by  cmdSetName, Don't use it!
201
 */
202
void UMLObject::setNameCmd(const QString &strName)
Andi Fischer's avatar
Andi Fischer committed
203
{
204
    m_name = strName;
205
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
206
207
}

208
/**
209
 * Returns a copy of m_name
210
 */
211
QString UMLObject::name() const
Andi Fischer's avatar
Andi Fischer committed
212
{
213
    return m_name;
Thibault Normand's avatar
Thibault Normand committed
214
215
}

216
/**
217
 * Returns the fully qualified name, i.e. all package prefixes and then m_name.
218
219
220
221
222
223
224
225
226
 *
 * @param separator  The separator string to use (optional.)
 *                   If not given then the separator is chosen according
 *                   to the currently selected active programming language
 *                   of import and code generation.
 * @param includeRoot  Whether to prefix the root folder name to the FQN.
 *                     See UMLDoc::getRootFolder(). Default: false.
 * @return  The fully qualified name of this UMLObject.
 */
227
QString UMLObject::fullyQualifiedName(const QString& separator,
Andi Fischer's avatar
Andi Fischer committed
228
229
        bool includeRoot /* = false */) const
{
Thibault Normand's avatar
Thibault Normand committed
230
    QString fqn;
231
232
    UMLPackage *parent = umlPackage();
    if (parent && parent != this) {
Thibault Normand's avatar
Thibault Normand committed
233
234
        bool skipPackage = false;
        if (!includeRoot) {
235
            UMLDoc *umldoc = UMLApp::app()->document();
236
237
            if ((umldoc->rootFolderType(parent) != Uml::ModelType::N_MODELTYPES) ||
                    (parent == umldoc->datatypeFolder()))
Thibault Normand's avatar
Thibault Normand committed
238
239
240
                skipPackage = true;
        }
        if (!skipPackage) {
241
242
243
            QString tempSeparator = separator;
            if (tempSeparator.isEmpty())
                tempSeparator = UMLApp::app()->activeLanguageScopeSeparator();
244
            fqn = parent->fullyQualifiedName(tempSeparator, includeRoot);
245
            fqn.append(tempSeparator);
Thibault Normand's avatar
Thibault Normand committed
246
247
        }
    }
248
    fqn.append(m_name);
Thibault Normand's avatar
Thibault Normand committed
249
250
251
    return fqn;
}

252
253
254
/**
 * Overloaded '==' operator
 */
255
bool UMLObject::operator==(const UMLObject & rhs) const
Andi Fischer's avatar
Andi Fischer committed
256
257
{
    if (this == &rhs)
Thibault Normand's avatar
Thibault Normand committed
258
259
260
261
262
263
264
265
        return true;

    //don't compare IDs, these are program specific and
    //don't mean the objects are the same
    //***** CHECK: Who put in this comment? What was the reason?
    //***** Currently some operator== in umbrello compare the IDs
    //***** while others don't.

266
    if (m_name != rhs.m_name)
Thibault Normand's avatar
Thibault Normand committed
267
268
269
270
        return false;

    // Packages create different namespaces, therefore they should be
    // part of the equality test.
271
    if (umlParent() != rhs.umlParent())
Thibault Normand's avatar
Thibault Normand committed
272
273
274
275
276
277
278
        return false;

    // Making the type part of an object's identity has its problems:
    // Not all programming languages support declarations of the same
    // name but different type.
    // In such cases, the code generator is responsible for generating
    // the appropriate error message.
Andi Fischer's avatar
Andi Fischer committed
279
    if (m_BaseType != rhs.m_BaseType)
Thibault Normand's avatar
Thibault Normand committed
280
281
282
283
284
        return false;

    // The documentation should not be part of the equality test.
    // If two objects are the same but differ only in their documentation,
    // what does that mean?
285
    //if(m_Doc != rhs.m_Doc)
Thibault Normand's avatar
Thibault Normand committed
286
287
    //  return false;

288
    // The visibility should not be part of the equality test.
Thibault Normand's avatar
Thibault Normand committed
289
    // What does it mean if two objects are the same but differ in their
290
    // visibility? - I'm not aware of any programming language that would
Thibault Normand's avatar
Thibault Normand committed
291
    // support that.
292
    //if(m_visibility != rhs.m_visibility)
Thibault Normand's avatar
Thibault Normand committed
293
294
295
    //  return false;

    // See comments above
296
    //if(m_pStereotype != rhs.m_pStereotype)
Thibault Normand's avatar
Thibault Normand committed
297
298
299
    //  return false;

    // See comments above
300
    //if(m_bAbstract != rhs.m_bAbstract)
Thibault Normand's avatar
Thibault Normand committed
301
302
303
    //  return false;

    // See comments above
304
    //if(m_bStatic != rhs.m_bStatic)
Thibault Normand's avatar
Thibault Normand committed
305
306
307
308
309
    //  return false;

    return true;
}

310
311
312
313
/**
 * Copy the internal presentation of this object into the new
 * object.
 */
314
void UMLObject::copyInto(UMLObject *lhs) const
Thibault Normand's avatar
Thibault Normand committed
315
316
{
    // Data members with copy constructor
317
318
    lhs->m_Doc = m_Doc;
    lhs->m_pStereotype = m_pStereotype;
319
320
    if (lhs->m_pStereotype)
        lhs->m_pStereotype->incrRefCount();
321
322
323
    lhs->m_bAbstract = m_bAbstract;
    lhs->m_bStatic = m_bStatic;
    lhs->m_BaseType = m_BaseType;
Andi Fischer's avatar
Andi Fischer committed
324
    lhs->m_visibility = m_visibility;
325
    lhs->setUMLParent(umlParent());
Thibault Normand's avatar
Thibault Normand committed
326
327

    // We don't want the same name existing twice.
328
    lhs->m_name = Model_Utils::uniqObjectName(m_BaseType, umlPackage(), m_name);
Thibault Normand's avatar
Thibault Normand committed
329
330

    // Create a new ID.
331
    lhs->m_nId = UniqueID::gen();
Thibault Normand's avatar
Thibault Normand committed
332
333

    // Hope that the parent from QObject is okay.
334
    if (lhs->umlParent() != umlParent())
335
        uDebug() << "copyInto has a wrong parent";
Thibault Normand's avatar
Thibault Normand committed
336
337
}

Ralf Habacker's avatar
Ralf Habacker committed
338
339
340
341
342
343
344
UMLObject *UMLObject::clone() const
{
    UMLObject *clone = new UMLObject;
    UMLObject::copyInto(clone);
    return clone;
}

345
346
347
/**
 * Returns the abstract state of the object.
 */
348
bool UMLObject::isAbstract() const
Andi Fischer's avatar
Andi Fischer committed
349
{
Thibault Normand's avatar
Thibault Normand committed
350
351
352
    return m_bAbstract;
}

353
354
355
/**
 * Sets the paste state of the object.
 */
Andi Fischer's avatar
Andi Fischer committed
356
357
void UMLObject::setAbstract(bool bAbstract)
{
Thibault Normand's avatar
Thibault Normand committed
358
    m_bAbstract = bAbstract;
359
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
360
361
}

362
363
364
365
/**
 * Returns true if this UMLObject has classifier scope,
 * otherwise false (the default).
 */
366
bool UMLObject::isStatic() const
Thibault Normand's avatar
Thibault Normand committed
367
368
369
{
    return m_bStatic;
}
Andi Fischer's avatar
Andi Fischer committed
370

371
372
373
/**
 * Sets the value for m_bStatic.
 */
Thibault Normand's avatar
Thibault Normand committed
374
375
376
void UMLObject::setStatic(bool bStatic)
{
    m_bStatic = bStatic;
377
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
378
379
}

380
381
382
383
384
/**
 * Forces the emission of the modified signal.  Useful when
 * updating several attributes at a time: you can block the
 * signals, update all atts, and then force the signal.
 */
Thibault Normand's avatar
Thibault Normand committed
385
386
void UMLObject::emitModified()
{
387
    UMLDoc *umldoc = UMLApp::app()->document();
388
    if (!umldoc->loading() && !umldoc->closing())
Thibault Normand's avatar
Thibault Normand committed
389
        emit modified();
Thibault Normand's avatar
Thibault Normand committed
390
391
}

392
393
394
395
396
/**
 * Returns the type of the object.
 *
 * @return  Returns the type of the object.
 */
397
UMLObject::ObjectType UMLObject::baseType() const
Andi Fischer's avatar
Andi Fischer committed
398
{
Thibault Normand's avatar
Thibault Normand committed
399
400
401
    return m_BaseType;
}

402
403
404
405
406
407
408
409
/**
 * @return The type used for rtti as string.
 */
QLatin1String UMLObject::baseTypeStr() const
{
    return QLatin1String(ENUM_NAME(UMLObject, ObjectType, m_BaseType));
}

410
411
412
/**
 * Set the type of the object.
 *
413
 * @param ot The ObjectType to set.
414
 */
415
void UMLObject::setBaseType(ObjectType ot)
Andi Fischer's avatar
Andi Fischer committed
416
{
Thibault Normand's avatar
Thibault Normand committed
417
418
419
    m_BaseType = ot;
}

420
421
422
423
424
/**
 * Returns the ID of the object.
 *
 * @return  Returns the ID of the object.
 */
Andi Fischer's avatar
Andi Fischer committed
425
ID::Type UMLObject::id() const
Andi Fischer's avatar
Andi Fischer committed
426
{
Thibault Normand's avatar
Thibault Normand committed
427
428
429
    return m_nId;
}

430
431
432
433
434
/**
 * Returns the documentation for the object.
 *
 * @return  Returns the documentation for the object.
 */
435
QString UMLObject::doc() const
Andi Fischer's avatar
Andi Fischer committed
436
{
Thibault Normand's avatar
Thibault Normand committed
437
438
439
    return m_Doc;
}

440
441
442
443
444
445
446
447
448
449
/**
 * Returns state of documentation for the object.
 *
 * @return false if documentation is empty
 */
bool UMLObject::hasDoc() const
{
    return !m_Doc.isEmpty();
}

450
451
452
453
454
455
456
457
458
459
460
/**
 * Sets the documentation for the object.
 *
 * @param d The documentation for the object.
 */
void UMLObject::setDoc(const QString &d)
{
    m_Doc = d;
    //emit modified();  No, this is done centrally at DocWindow::updateDocumentation()
}

461
462
463
464
465
/**
 * Returns the visibility of the object.
 *
 * @return  Returns the visibility of the object.
 */
Andi Fischer's avatar
Andi Fischer committed
466
Visibility::Enum UMLObject::visibility() const
Andi Fischer's avatar
Andi Fischer committed
467
{
Andi Fischer's avatar
Andi Fischer committed
468
    return m_visibility;
Thibault Normand's avatar
Thibault Normand committed
469
470
}

471
472
473
/**
 * Sets the visibility of the object.
 *
474
 * @param visibility  The visibility of the object.
475
 */
Andi Fischer's avatar
Andi Fischer committed
476
void UMLObject::setVisibility(Visibility::Enum visibility)
Andi Fischer's avatar
Andi Fischer committed
477
{
478
479
480
    if (m_visibility != visibility) {
        UMLApp::app()->executeCommand(new CmdSetVisibility(this, visibility));
    }
Pierre Pettera's avatar
Pierre Pettera committed
481
482
}

483
/**
Frederik Schwarzer's avatar
Frederik Schwarzer committed
484
 * Method used by setVisibility: it is called by  cmdSetVisibility, Don't use it!
485
 */
Andi Fischer's avatar
Andi Fischer committed
486
void UMLObject::setVisibilityCmd(Visibility::Enum visibility)
Andi Fischer's avatar
Andi Fischer committed
487
{
Andi Fischer's avatar
Andi Fischer committed
488
    m_visibility = visibility;
489
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
490
491
}

492
493
494
495
496
497
498
/**
 * Sets the class' UMLStereotype. Adjusts the reference counts
 * at the previously set stereotype and at the new stereotype.
 * If the previously set UMLStereotype's reference count drops
 * to zero then the UMLStereotype is removed at the UMLDoc and
 * it is then physically deleted.
 *
499
 * @param stereo Sets the classes UMLStereotype.
500
 */
Andi Fischer's avatar
Andi Fischer committed
501
502
void UMLObject::setUMLStereotype(UMLStereotype *stereo)
{
Thibault Normand's avatar
Thibault Normand committed
503
504
505
506
507
508
509
510
    if (stereo == m_pStereotype)
        return;
    if (stereo) {
        stereo->incrRefCount();
    }
    if (m_pStereotype) {
        m_pStereotype->decrRefCount();
        if (m_pStereotype->refCount() == 0) {
511
            UMLDoc *pDoc = UMLApp::app()->document();
Thibault Normand's avatar
Thibault Normand committed
512
513
514
515
516
517
            pDoc->removeStereotype(m_pStereotype);
            delete m_pStereotype;
        }
    }
    m_pStereotype = stereo;
    // TODO: don't emit modified() if predefined folder
518
    emitModified();
Thibault Normand's avatar
Thibault Normand committed
519
520
}

521
522
523
524
/**
 * Sets the classes stereotype name.
 * Internally uses setUMLStereotype().
 *
525
 * @param name     Sets the classes stereotype name.
526
 */
527
void UMLObject::setStereotype(const QString &name)
Andi Fischer's avatar
Andi Fischer committed
528
{
529
530
531
    if (name != stereotype()) {
        UMLApp::app()->executeCommand(new CmdSetStereotype(this, name));
    }
532
533
534
535
536
}

void UMLObject::setStereotypeCmd(const QString& name)
{
    if (name.isEmpty()) {
537
        setUMLStereotype(0);
Thibault Normand's avatar
Thibault Normand committed
538
539
        return;
    }
540
    UMLDoc *pDoc = UMLApp::app()->document();
541
    UMLStereotype *s = pDoc->findOrCreateStereotype(name);
Thibault Normand's avatar
Thibault Normand committed
542
543
544
    setUMLStereotype(s);
}

545
546
547
548
549
/**
 * Returns the classes UMLStereotype object.
 *
 * @return   Returns the classes UMLStereotype object.
 */
550
UMLStereotype * UMLObject::umlStereotype()
Andi Fischer's avatar
Andi Fischer committed
551
{
Thibault Normand's avatar
Thibault Normand committed
552
553
554
    return m_pStereotype;
}

555
556
557
/**
 * Returns the stereotype.
 */
558
QString UMLObject::stereotype(bool includeAdornments /* = false */) const
Andi Fischer's avatar
Andi Fischer committed
559
{
560
    if (m_pStereotype == 0)
561
        return QString();
562
    return m_pStereotype->name(includeAdornments);
Thibault Normand's avatar
Thibault Normand committed
563
564
}

565
566
567
568
569
570
571
572
573
574
575
576
/**
 * Return the package(s) in which this UMLObject is contained
 * as a text.
 *
 * @param separator Separator string for joining together the
 *                  individual package prefixes (optional.)
 *                  If no separator is given then the separator
 *                  of the currently selected language is used.
 * @param includeRoot  Whether to prefix the root folder name.
 *                     Default: false.
 * @return  The UMLObject's enclosing package(s) as a text.
 */
577
QString UMLObject::package(const QString& separator, bool includeRoot)
Andi Fischer's avatar
Andi Fischer committed
578
{
579
580
581
    QString tempSeparator = separator;
    if (tempSeparator.isEmpty())
        tempSeparator = UMLApp::app()->activeLanguageScopeSeparator();
582
    QString fqn = fullyQualifiedName(tempSeparator, includeRoot);
583
    if (!fqn.contains(tempSeparator))
584
        return QString();
585
    QString scope = fqn.left(fqn.length() - tempSeparator.length() - m_name.length());
Thibault Normand's avatar
Thibault Normand committed
586
587
588
    return scope;
}

589
590
591
592
593
594
595
596
/**
 * Return a list of the packages in which this class is embedded.
 * The outermost package is first in the list.
 *
 * @param includeRoot  Whether to prefix the root folder name.
 *                     Default: false.
 * @return  UMLPackageList of the containing packages.
 */
597
UMLPackageList UMLObject::packages(bool includeRoot) const
Andi Fischer's avatar
Andi Fischer committed
598
{
Thibault Normand's avatar
Thibault Normand committed
599
    UMLPackageList pkgList;
600
    UMLPackage* pkg = umlPackage();
601
    while (pkg != 0) {
Thibault Normand's avatar
Thibault Normand committed
602
        pkgList.prepend(pkg);
603
        pkg = pkg->umlPackage();
Thibault Normand's avatar
Thibault Normand committed
604
605
606
607
608
609
    }
    if (!includeRoot)
        pkgList.removeFirst();
    return pkgList;
}

610
611
612
613
614
615
616
617
618
619
620
621
/**
 * Sets the UMLPackage in which this class is located.
 *
 * @param pPkg   Pointer to the class' UMLPackage.
 */
bool UMLObject::setUMLPackage(UMLPackage *pPkg)
{
    if (pPkg == this) {
        uDebug() << "setting parent to myself is not allowed";
        return false;
    }

622
    if (pPkg == 0) {
623
624
625
626
627
        // Allow setting to NULL for stereotypes
        setParent(pPkg);
        return true;
    }

628
    if (pPkg->umlPackage() == this) {
629
630
631
632
633
634
635
636
637
        uDebug() << "setting parent to an object of which I'm already the parent is not allowed";
        return false;
    }

    setParent(pPkg);
    emitModified();
    return true;
}

638
639
640
/**
 * Returns the UMLPackage that this class is located in.
 *
641
642
 * This method is a shortcut for calling umlParent()->asUMLPackage().
 *
643
644
 * @return  Pointer to the UMLPackage of this class.
 */
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
UMLPackage* UMLObject::umlPackage() const
{
    return dynamic_cast<UMLPackage *>(parent());
}

/**
 * Set UML model parent.
 *
 * @param parent object to set as parent
 *
 * @TODO prevent setting parent to myself
 */
void UMLObject::setUMLParent(UMLObject *parent)
{
    setParent(parent);
}

/**
 * Return UML model parent.
 *
 * Model classes of type UMLClassifierListItem and below
 * uses QObject::parent to hold the model parent
 *
 * @return parent of uml object
 */
UMLObject *UMLObject::umlParent() const
Andi Fischer's avatar
Andi Fischer committed
671
{
672
    uCheckPointerAndReturnIfZero(this);
673
    return dynamic_cast<UMLObject *>(parent());
Thibault Normand's avatar
Thibault Normand committed
674
675
}

676
677
678
/**
 * Return secondary ID. Required by resolveRef().
 */
679
QString UMLObject::secondaryId() const
Andi Fischer's avatar
Andi Fischer committed
680
{
Thibault Normand's avatar
Thibault Normand committed
681
682
683
    return m_SecondaryId;
}

684
685
686
687
688
/**
 * Set the secondary ID.
 * Currently only required by petalTree2Uml(); all other setting of the
 * m_SecondaryID is internal to the UMLObject class hierarchy.
 */
Andi Fischer's avatar
Andi Fischer committed
689
690
void UMLObject::setSecondaryId(const QString& id)
{
Thibault Normand's avatar
Thibault Normand committed
691
692
693
    m_SecondaryId = id;
}

694
695
696
697
/**
 * Return secondary ID fallback.
 * Required by resolveRef() for imported model files.
 */
698
QString UMLObject::secondaryFallback() const
Andi Fischer's avatar
Andi Fischer committed
699
{
Thibault Normand's avatar
Thibault Normand committed
700
701
702
    return m_SecondaryFallback;
}

703
704
705
706
/**
 * Set the secondary ID fallback.
 * Currently only used by petalTree2Uml().
 */
Andi Fischer's avatar
Andi Fischer committed
707
708
void UMLObject::setSecondaryFallback(const QString& id)
{
Thibault Normand's avatar
Thibault Normand committed
709
710
711
    m_SecondaryFallback = id;
}

712
713
714
715
/**
 * Calls UMLDoc::signalUMLObjectCreated() if m_BaseType affords
 * doing so.
 */
Andi Fischer's avatar
Andi Fischer committed
716
717
void UMLObject::maybeSignalObjectCreated()
{
Thibault Normand's avatar
Thibault Normand committed
718
    if (!m_bCreationWasSignalled &&
719
720
721
            m_BaseType != ot_Stereotype &&
            m_BaseType != ot_Association &&
            m_BaseType != ot_Role) {
Thibault Normand's avatar
Thibault Normand committed
722
        m_bCreationWasSignalled = true;
723
        UMLDoc* umldoc = UMLApp::app()->document();
Thibault Normand's avatar
Thibault Normand committed
724
725
726
727
        umldoc->signalUMLObjectCreated(this);
    }
}

728
729
730
731
732
733
734
735
736
737
738
/**
 * Resolve referenced objects (if any.)
 * Needs to be called after all UML objects are loaded from file.
 * This needs to be done after all model objects are loaded because
 * some of the xmi.id's might be forward references, i.e. they may
 * identify model objects which were not yet loaded at the point of
 * reference.
 * The default implementation attempts resolution of the m_SecondaryId.
 *
 * @return   True for success.
 */
Andi Fischer's avatar
Andi Fischer committed
739
740
bool UMLObject::resolveRef()
{
Thibault Normand's avatar
Thibault Normand committed
741
742
743
744
745
    if (m_pSecondary || (m_SecondaryId.isEmpty() && m_SecondaryFallback.isEmpty())) {
        maybeSignalObjectCreated();
        return true;
    }
#ifdef VERBOSE_DEBUGGING
746
    uDebug() << m_name << ": m_SecondaryId is " << m_SecondaryId;
Thibault Normand's avatar
Thibault Normand committed
747
#endif
748
    UMLDoc *pDoc = UMLApp::app()->document();
Thibault Normand's avatar
Thibault Normand committed
749
750
751
    // In the new, XMI standard compliant save format,
    // the type is the xmi.id of a UMLClassifier.
    if (! m_SecondaryId.isEmpty()) {
752
        m_pSecondary = pDoc->findObjectById(Uml::ID::fromString(m_SecondaryId));
753
        if (m_pSecondary != 0) {
754
            if (m_pSecondary->baseType() == ot_Stereotype) {
755
756
                if (m_pStereotype)
                    m_pStereotype->decrRefCount();
757
                m_pStereotype = m_pSecondary->asUMLStereotype();
Thibault Normand's avatar
Thibault Normand committed
758
                m_pStereotype->incrRefCount();
759
                m_pSecondary = 0;
Thibault Normand's avatar
Thibault Normand committed
760
            }
761
            m_SecondaryId = QString();
Thibault Normand's avatar
Thibault Normand committed
762
763
764
            maybeSignalObjectCreated();
            return true;
        }
765
        if (m_SecondaryFallback.isEmpty()) {
766
            uDebug() << "object with xmi.id=" << m_SecondaryId << " not found, setting to undef";
767
            UMLFolder *datatypes = pDoc->datatypeFolder();
Oliver Kellogg's avatar
Oliver Kellogg committed
768
            m_pSecondary = Object_Factory::createUMLObject(ot_Datatype, QLatin1String("undef"), datatypes, false);
769
            return true;
Thibault Normand's avatar
Thibault Normand committed
770
        }
Thibault Normand's avatar
Thibault Normand committed
771
    }
772
    if (m_SecondaryFallback.isEmpty()) {
773
        uError() << m_name << ": cannot find type with id " << m_SecondaryId;
774
775
        return false;
    }
Thibault Normand's avatar
Thibault Normand committed
776
#ifdef VERBOSE_DEBUGGING
777
    uDebug() << m_name << ": could not resolve secondary ID " << m_SecondaryId
778
             << ", using secondary fallback " << m_SecondaryFallback;
Thibault Normand's avatar
Thibault Normand committed
779
780
781
782
783
#endif
    m_SecondaryId = m_SecondaryFallback;
    // Assume we're dealing with the older Umbrello format where
    // the type name was saved in the "type" attribute rather
    // than the xmi.id of the model object of the attribute type.
784
    m_pSecondary = pDoc->findUMLObject(m_SecondaryId, ot_UMLObject, this);
Thibault Normand's avatar
Thibault Normand committed
785
    if (m_pSecondary) {
786
        m_SecondaryId = QString();
Thibault Normand's avatar
Thibault Normand committed
787
788
789
790
791
        maybeSignalObjectCreated();
        return true;
    }
    // Work around Object_Factory::createUMLObject()'s incapability
    // of on-the-fly scope creation:
Oliver Kellogg's avatar
Oliver Kellogg committed
792
    if (m_SecondaryId.contains(QLatin1String("::"))) {
Thibault Normand's avatar
Thibault Normand committed
793
        // TODO: Merge Import_Utils::createUMLObject() into Object_Factory::createUMLObject()
794
        m_pSecondary = Import_Utils::createUMLObject(ot_UMLObject, m_SecondaryId, umlPackage());
Thibault Normand's avatar
Thibault Normand committed
795
796
797
        if (m_pSecondary) {
            if (Import_Utils::newUMLObjectWasCreated()) {
                maybeSignalObjectCreated();
Laurent Montel's avatar
Laurent Montel committed
798
                qApp->processEvents();
799
                uDebug() << "Import_Utils::createUMLObject() created a new type for "
800
                         << m_SecondaryId;
Thibault Normand's avatar
Thibault Normand committed
801
            } else {
802
                uDebug() << "Import_Utils::createUMLObject() returned an existing type for "
803
                         << m_SecondaryId;
Thibault Normand's avatar
Thibault Normand committed
804
            }
805
            m_SecondaryId = QString();
Thibault Normand's avatar
Thibault Normand committed
806
807
            return true;
        }
808
        uError() << "Import_Utils::createUMLObject() failed to create a new type for "
809
                 << m_SecondaryId;
Thibault Normand's avatar
Thibault Normand committed
810
811
        return false;
    }
812
    uDebug() << "Creating new type for " << m_SecondaryId;
Thibault Normand's avatar
Thibault Normand committed
813
814
815
    // This is very C++ specific - we rely on  some '*' or
    // '&' to decide it's a ref type. Plus, we don't recognize
    // typedefs of ref types.
Oliver Kellogg's avatar
Oliver Kellogg committed
816
817
    bool isReferenceType = (m_SecondaryId.contains(QLatin1Char('*')) ||
                            m_SecondaryId.contains(QLatin1Char('&')));
818
    ObjectType ot = ot_Class;
Thibault Normand's avatar
Thibault Normand committed
819
    if (isReferenceType) {
820
        ot = ot_Datatype;
Thibault Normand's avatar
Thibault Normand committed
821
822
    } else {
        if (Model_Utils::isCommonDataType(m_SecondaryId))
823
            ot = ot_Datatype;
Thibault Normand's avatar
Thibault Normand committed
824
    }
825
826
    m_pSecondary = Object_Factory::createUMLObject(ot, m_SecondaryId, 0);
    if (m_pSecondary == 0)
Thibault Normand's avatar
Thibault Normand committed
827
        return false;
828
    m_SecondaryId = QString();
Thibault Normand's avatar
Thibault Normand committed
829
    maybeSignalObjectCreated();
Laurent Montel's avatar
Laurent Montel committed
830
    //qApp->processEvents();
Thibault Normand's avatar
Thibault Normand committed
831
832
833
    return true;
}

Ralf Habacker's avatar
Ralf Habacker committed
834
void UMLObject::saveToXMI1(QDomDocument &qDoc, QDomElement &qElement)
Ralf Habacker's avatar
Ralf Habacker committed
835
{
Andi Fischer's avatar
Andi Fischer committed
836
    Q_UNUSED(qDoc); Q_UNUSED(qElement);
Ralf Habacker's avatar
Ralf Habacker committed
837
838
}

839
/**
Ralf Habacker's avatar
Ralf Habacker committed
840
 * Auxiliary to saveToXMI1.
841
842
843
844
 * Create a QDomElement with the given tag, and save the XMI attributes
 * that are common to all child classes to the newly created element.
 * This method does not need to be overridden by child classes.
 */
845
QDomElement UMLObject::save1(const QString &tag, QDomDocument & qDoc)
Andi Fischer's avatar
Andi Fischer committed
846
{
847
    m_d->isSaved = true;
Thibault Normand's avatar
Thibault Normand committed
848
    /*
Ralf Habacker's avatar
Ralf Habacker committed
849
      Call as the first action of saveToXMI1() in child class:
Thibault Normand's avatar
Thibault Normand committed
850
851
852
      This creates the QDomElement with which to work.
    */
    QDomElement qElement = qDoc.createElement(tag);
Oliver Kellogg's avatar
Oliver Kellogg committed
853
    qElement.setAttribute(QLatin1String("isSpecification"), QLatin1String("false"));
854
855
856
    if (m_BaseType != ot_Association &&
        m_BaseType != ot_Role &&
        m_BaseType != ot_Attribute) {
Oliver Kellogg's avatar
Oliver Kellogg committed
857
858
        qElement.setAttribute(QLatin1String("isLeaf"), QLatin1String("false"));
        qElement.setAttribute(QLatin1String("isRoot"), QLatin1String("false"));
Thibault Normand's avatar
Thibault Normand committed
859
        if (m_bAbstract)
Oliver Kellogg's avatar
Oliver Kellogg committed
860
            qElement.setAttribute(QLatin1String("isAbstract"), QLatin1String("true"));
Thibault Normand's avatar
Thibault Normand committed
861
        else
Oliver Kellogg's avatar
Oliver Kellogg committed
862
            qElement.setAttribute(QLatin1String("isAbstract"), QLatin1String("false"));
Thibault Normand's avatar
Thibault Normand committed
863
    }
Oliver Kellogg's avatar
Oliver Kellogg committed
864
865
    qElement.setAttribute(QLatin1String("xmi.id"), Uml::ID::toString(m_nId));
    qElement.setAttribute(QLatin1String("name"), m_name);
866
867
868
    if (m_BaseType != ot_Operation &&
        m_BaseType != ot_Role &&
        m_BaseType != ot_Attribute) {
Andi Fischer's avatar
Andi Fischer committed
869
        Uml::ID::Type nmSpc;
870
871
        if (umlPackage())
            nmSpc = umlPackage()->id();
Thibault Normand's avatar
Thibault Normand committed
872
        else
873
            nmSpc = UMLApp::app()->document()->modelID();
Oliver Kellogg's avatar
Oliver Kellogg committed
874
        qElement.setAttribute(QLatin1String("namespace"), Uml::ID::toString(nmSpc));
Thibault Normand's avatar
Thibault Normand committed
875
876
    }
    if (! m_Doc.isEmpty())
Oliver Kellogg's avatar
Oliver Kellogg committed
877
        qElement.setAttribute(QLatin1String("comment"), m_Doc);    //CHECK: uml13.dtd compliance
Thibault Normand's avatar
Thibault Normand committed
878
#ifdef XMI_FLAT_PACKAGES
879
880
    if (umlParent()->asUMLPackage())             //FIXME: uml13.dtd compliance
        qElement.setAttribute(QLatin1String("package"), umlParent()->asUMLPackage()->ID());
Thibault Normand's avatar
Thibault Normand committed
881
#endif
Andi Fischer's avatar
Andi Fischer committed
882
    QString visibility = Uml::Visibility::toString(m_visibility, false);
Oliver Kellogg's avatar
Oliver Kellogg committed
883
    qElement.setAttribute(QLatin1String("visibility"), visibility);
884
    if (m_pStereotype != 0)
Oliver Kellogg's avatar
Oliver Kellogg committed
885
        qElement.setAttribute(QLatin1String("stereotype"), Uml::ID::toString(m_pStereotype->id()));
Thibault Normand's avatar
Thibault Normand committed
886
    if (m_bStatic)
Oliver Kellogg's avatar
Oliver Kellogg committed
887
        qElement.setAttribute(QLatin1String("ownerScope"), QLatin1String("classifier"));
Thibault Normand's avatar
Thibault Normand committed
888
    /* else
889
        qElement.setAttribute("ownerScope", "instance");
Thibault Normand's avatar
Thibault Normand committed
890
891
892
893
     *** ownerScope defaults to instance if not set **********/
    return qElement;
}

894
895
896
897
898
899
/**
 * Auxiliary to loadFromXMI.
 * This method is usually overridden by child classes.
 * It is responsible for loading the specific XMI structure
 * of the child class.
 */
900
bool UMLObject::load1(QDomElement&)
Andi Fischer's avatar
Andi Fischer committed
901
{
Thibault Normand's avatar
Thibault Normand committed
902
903
904
905
906
    // This body is not usually executed because child classes
    // overwrite the load method.
    return true;
}

907
908
909
910
911
912
/**
 * Analyzes the given QDomElement for a reference to a stereotype.
 *
 * @param element   QDomElement to analyze.
 * @return          True if a stereotype reference was found, else false.
 */
Andi Fischer's avatar
Andi Fischer committed
913
914
bool UMLObject::loadStereotype(QDomElement & element)
{
915
    QString tag = element.tagName();
Oliver Kellogg's avatar
Oliver Kellogg committed
916
    if (!UMLDoc::tagEq(tag, QLatin1String("stereotype")))
917
        return false;
Oliver Kellogg's avatar
Oliver Kellogg committed
918
    QString stereo = element.attribute(QLatin1String("xmi.value"));
919
920
921
922
923
924
925
926
927
    if (stereo.isEmpty() && element.hasChildNodes()) {
        /* like so:
         <UML:ModelElement.stereotype>
           <UML:Stereotype xmi.idref = '07CD'/>
         </UML:ModelElement.stereotype>
         */
        QDomNode stereoNode = element.firstChild();
        QDomElement stereoElem = stereoNode.toElement();
        tag = stereoElem.tagName();
Oliver Kellogg's avatar
Oliver Kellogg committed
928
929
        if (UMLDoc::tagEq(tag, QLatin1String("Stereotype"))) {
            stereo = stereoElem.attribute(QLatin1String("xmi.idref"));
930
931
932
933
        }
    }
    if (stereo.isEmpty())
        return false;
934
    Uml::ID::Type stereoID = Uml::ID::fromString(stereo);
935
    UMLDoc *pDoc = UMLApp::app()->document();
936
937
    if (m_pStereotype)
        m_pStereotype->decrRefCount();
938
939
940
941
942
943
944
945
    m_pStereotype = pDoc->findStereotypeById(stereoID);
    if (m_pStereotype)
        m_pStereotype->incrRefCount();
    else
        m_SecondaryId = stereo;  // leave it to resolveRef()
    return true;
}

946
947
948
949
950
951
952
953
/**
 * This method loads the generic parts of the XMI common to most model
 * classes.  It is not usually reimplemented by child classes.
 * Instead, it invokes the load() method which implements the loading
 * of the specifics of each child class.
 *
 * @param element   The QDomElement from which to load.
 */
954
bool UMLObject::loadFromXMI1(QDomElement & element)