jump to navigation

How cute can modeling be ? November 18, 2013

Posted by Sandro Andrade in planetkde-sandroandrade, planetqt-sandroandrade.
8 comments

Heya planet,

It’s been a long time since my last post (as usual ­čÖé – hopefully that’s going to change in next months). A lot of things have happened in QtModeling land tough. In case you haven’t read my previous ‘call for arms‘ and ‘XMI serialization and metamodel plugins‘ posts, QtModeling is intended to provide a complete and flexible infrastructure for software (meta)modeling, (automated) architecture design and analysis, model recovery, and whatever comes in mind when you think about software models. Although we are still far from have those features fully available, some interesting capabilities are quite functional.

MOF/UML Metamodel Implementation

The new refactored templates for automatic metamodel implementation make programmatic model manipulation easier by taking ownership of properties with ‘composite’ aggregation and automatically removing elements from all involved property lists when it’s deleted. While Qt properties features described in┬áQTBUG-17301 are still being considered, QtModeling extends Qt property system by defining roles for aggregation, opposite association ends, redefined and subsetted properties, and derived and derived union constructors. Some UML functionalities defined as derived properties/operations still miss implementation but basic features involving class and components diagrams are already in place. XMI serialization, profiles and stereotypes definitions/applications, and imported elements/packages are also currently available.

When building QtModeling, the following Qt Add-ons modules will be available:

  • QtModeling: defines base classes for model elements (QModelingElement and QModelingObject) and QXmiWriter/QXmiReader classes for XMI serialization.
  • QtModelingWidgets: provides two new model classes (QModelingObjectModel and QModelingObjectPropertyModel) and two new view classes (QModelingObjectView and QModelingObjectPropertyEditor) for manipulating models and model element’s properties.
  • QtUml: implements the full UML 2.4.1 metamodel. It comprises 239 classes which are 91.83% automatically implemented by templates. The remaining code represents derived properties and are being implemented on demand.
  • QtMof: implements the full MOF (Meta Object Facility) 2.4.1 metamodel. It comprises 51 classes also almost entirely implemented automatically.
  • QtDuSE: implements the DuSE language/metamodel (part of my PhD research) for automated software architecture design and analysis.
  • QtSADuSEProfile: defines an UML Profile for automated software architecture design of self-adaptive systems. This is required by SADuSE – a specific DuSE design space instance devoted to self-adaptive systems design.
  • Qt metamodel plugins: bind the previous model modules as Qt plugins used during XMI serialization.

DuSE-MT

DuSE-MT is a software modeling framework built on top of QtModeling which provide underlying mechanisms for defining new modeling languages (metamodels), creating models described in available languages (currently UML, MOF, and DuSE), manipulate models via JavaScript, and basic constructs for UML diagrams.

DuSE-MT

DuSE-MT defines a plugin-based architecture (shamelessly ­čÖé inspired by Qt Creator’s one), which supports not only Qt metamodel plugins but also extensions implementing new DuSE-MT functionalities such as concrete syntax handling, architecture recovery, automated architecture design, and so on. One of the good things of being a professor is to have students eager to graduate ­čÖé and willing to do their final projects in some Qt/KDE related project. Let’s see what happens …

DuSE-MT Plugins

Model Scripting

DuSE-MT’s JavaScript Console allows for defining scripts which might, for example, verify well-formedness rules and/or extend/change the currently opened model. For instance, if you know in advance you are going to use Java to implement the currently modeled system, you might use the following script to verify if any multiple inheritance was found in your model:

DuSE-MT JavaScript Console

While a full OCL parser isn’t already available, such kind of scripts can be really useful (actually it’s even more expressive than OCL since it’s not limited to read-only operations).

QML for UML Concrete Syntax

It’s really amazing how QML seems to be born to make certain things real without much pain ­čÖé Even in such highly dynamic cases with many requirements for integrating with C++ objects, QML property binding and some new defined QML items can rapidly gives you some nifty diagrams:

DuSE-MT UML Concrete Syntax

Wondering how much QML code was required to do that ? Here it is:

import QtQuick 2.0

UmlElement {
    UmlSlot {
        id: nameSlot
        anchors.top: parent.top
        Text {
            id: label
            text: element.name
            anchors.centerIn: parent
        }
    }
    UmlSlot {
        id: attributeSlot
        anchors { top: nameSlot.bottom; topMargin: -1 }
        height: (parent.height - nameSlot.height)/2
        ListView {
            model: element.ownedAttributes
            anchors { fill: parent; margins: 4 }
            delegate: Text {
                text: visibility(modelData.visibility) + modelData.name + ": " + (modelData.type ? modelData.type.name:"<no type>")
            }
        }
    }
    UmlSlot {
        anchors { top: attributeSlot.bottom; topMargin: -1; bottom: parent.bottom }
        ListView {
            model: element.ownedOperations
            anchors { fill: parent; margins: 4 }
            delegate: Text {
                text: visibility(modelData.visibility) + modelData.name
            }
        }
    }
    function visibility(visibilityEnum)
    {
        switch (visibilityEnum) {
        case 0: return " "
        case 1: return "+"
        case 2: return "-"
        case 3: return "#"
        case 4: return "~"
        }
    }
}

… and for UML associations:

import QtQuick 2.0
import QtModeling.Uml 1.0
import "util.js" as Util

Relationship {
    name: element.name
    relationshipType: "association"
    source: Util.findQuickItem(parent, element.memberEnds[0].class_.name)
    target: Util.findQuickItem(parent, element.memberEnds[0].type.name)
    sourceAggregation: aggregation(element.memberEnds[0].aggregation);
    targetAggregation: aggregation(element.memberEnds[1].aggregation);
    sourceMultiplicity: element.memberEnds[0].lowerValue.value + ".." + element.memberEnds[0].upperValue.value
    targetMultiplicity: element.memberEnds[1].lowerValue.value + ".." + element.memberEnds[1].upperValue.value
    function aggregation(aggregationEnum)
    {
        switch (aggregationEnum) {
        case 0: return "none"
        case 1: return "shared"
        case 2: return "composite"
        }
    }
}

… where Relationship is a new QML item we implemented based on QPainter API (I still need to learn how to use the new SG API). With such things done, UML concrete syntax implementation becomes the simple creation of QML files with names matching the corresponding UML metamodel element (UmlNode, UmlState, and so on).

QtOptimization

Supporting automated architecture design involves, among other things, the use of some search-based approach to find out several candidate architectures and then choose one that satisfies some specific quality attribute criteria. Elitist population-based algorithms have been successfully applied in such scenario because of their improved performance and fast convergence. If you’ve already played before with JMetal or some other framework for multi-objective optimization, that’s exactly what QtOptimization is intended to be. Currently, only NSGA-II (Nondominated Sorting Genetic Algorithm) is implemented (QNsga2 class), in conjunction with classes for bitwise mutation (QBitwiseMutationOperator), crossover (QCrossoverOperator), polynomial mutation (QPolynomialMutationOperator), simulated binary crossover (QSbxCrossoverOperator), and tournament selection (QTournamentSelectionOperator) operators.

Here is a brief description on how to use QtOptimization for a single-variable/two-objective optimization problem:

// Define objective function 1
class MyObjectiveFunction1 : public QObjectiveFunction
{
public:
    explicit MyObjectiveFunction1(QtOptimization::OptimizationGoal optimizationGoal = QtOptimization::OptimizationMinimizes) :
        QObjectiveFunction(optimizationGoal) {}
    virtual qreal evaluateOnIndividual(const QIndividual *individual) const
    {
        qreal x = individual->realVariableValue(0);
        return x*x;
    }
};

// Define objective function 2
class MyObjectiveFunction2 : public QObjectiveFunction
{
public:
    explicit MyObjectiveFunction2(QtOptimization::OptimizationGoal optimizationGoal = QtOptimization::OptimizationMinimizes) :
        QObjectiveFunction(optimizationGoal) {}
    virtual qreal evaluateOnIndividual(const QIndividual *individual) const
    {
        qreal x = individual->realVariableValue(0);
        return (x-2)*(x-2);
    }
};

QNsga2 nsga2;
MyObjectiveFunction1 *f1 = new MyObjectiveFunction1;
f1->setOptimizationGoal(QtOptimization::OptimizationMinimizes);
MyObjectiveFunction2 *f2 = new MyObjectiveFunction2;
f2->setOptimizationGoal(QtOptimization::OptimizationMinimizes);
nsga2.setObjectiveFunctions(QList<QObjectiveFunction *>() << f1 << f2);

nsga2.setSelectionOperator(new QTournamentSelectionOperator);
nsga2.setCrossoverOperator(new QSbxCrossoverOperator(0.9, 5.0));
nsga2.setMutationOperator(new QPolynomialMutationOperator(0.5, 10.0));
nsga2.createRandomPopulation(48);

// Improve population by 250 generations
for (int i = 0; i < 250; ++i) {
    nsga2.appendNewPopulation();
    nsga2.reducePopulation(48);
}

We’re currently implementing some quality indicators for Pareto-fronts and finish integration of such features in DuSE-MT in order to support better automated architecture design and analysis.

So, interesting stuff coming … I’d really love to see those things effectively integrated in QtCreator, KDevelop, and Umbrello. If you are willing to join the effort, fell free to try it from gitorious and don’t hesitate to contact me if you face any trouble.

See you,

 

Advertisements

QtMof/QtUml: XMI serialization and metamodel plugins January 21, 2013

Posted by Sandro Andrade in planetkde-sandroandrade, planetqt-sandroandrade.
4 comments

Hi there,

A lot has happening since my last post about QtMof/QtUml┬á– a Qt5 add-on module devoted to (meta-)modeling and model-driven support in Qt (currently in Qt Playground). Auto-generation of┬ámeta-models┬áhas been further improved, making of use Qt5’s new features for metatypes, XMI serialization is already in place even though not all specified configuration parameters are currently supported by now. In addition, QtUmlEditor example has been a full testbed for QtMof/QtUml and can probably be useful as a working application afterwards.

Demo video here

Meta-models (like MOF and UML) are now implemented as Qt plugins. That enables editor (and XMI serializer) to be fully independent from modeling language and also sets the stage to deal with user-defined meta-models. We are currently implementing the OCL (Object Constraint Language) parser which will allow users to define and execute ‘sanity checks’ and conformance rules on models. That will also enhance auto-generation of meta-models since a lot of operations and derived properties can indeed by specified in OCL (as already happen in Uml meta-model).

How to try it !

Contribute !

As usual, any help with testing, developing, and reviewing is quite welcome ­čÖé Maybe we can expect some model-driven features in QtCreator and KDevelop in a near future ? ­čÖé

See you …

Call for arms: QtMof/QtUml December 11, 2012

Posted by Sandro Andrade in planetkde-sandroandrade, planetqt-sandroandrade.
4 comments

Hi planet,

As roughly mentioned in a previous post, in last two months┬áI’ve been working on a Qt5-based implementation of OMG’s MOF (Meta Object Facility) and UML (Unified Modeling Language) specifications. For those of you who are more familiar with a user-perspective view of UML, it’s worth mention that UML (and your defining metalanguage, MOF) provides a powerful infrastructure which enables, for example, programmatic handling of models, early analysis of architectural models, effective middleware/framework-driven artifact generation, use of deployable models in┬árun-time, and so on. A considerable amount of work has been done towards the development of high-quality tools which leverage the use of models in software development processes. Efforts like the┬áEclipse Modeling Framework have been setting the stage for a number of plugins, environments, or even tailored Eclipse versions (Eclipse Modeling Project), which rely on modeling features to provide a complete toolset for model-driven engineering. QtMof/QtUml aim at bringing such features to the Qt5 world.

QtMof/QtUml rationale has been driven by the following desired properties:

  • We should support automatic code generation as much as possible. Only 8.17% of UML 2.4.1 metamodel┬áproperties are declared as ‘derived’ (and not derived unions), which prevent automatic code generation. For those properties, the specification provide the guidelines for implementation but maybe some of them could be automatically derived from their OCL specifications. QtMof/QtUml already provides a mapping from UML to Qt5 by first generating a more Qt-ish representation of a UML model (XQuery-based xmi to qtxmi conversion) and then generating source code from a couple of well-design templates. Such infrastructure has successfully been used to generate the initial skeletons of QtMof/QtUml and can also be used to convert user-defined UML models into Qt5 source code. As a consequence, we hopefully end up with a faithful implementation of OMG’s standardized metamodels.
  • We should provide a metamodel-agnostic tool for editing, analysing, and converting any MOF-based model. By having no compile-time commitments with any specific MOF-based metamodel, such tool would be also able to handle other MOF-based metamodels (such as CWM) or even user-defined meta-models.
  • QtMof/QtUml should provide a powerful modeling notation for early analysis of MOF-based models. A number of preset scripts for checking well-formed rules should be available and specific scripts may be defined by the user. OCL is a potential candidate as script language, but XQuery or even QtScript can also be considered. That would make possible, for example, analysis of conformance to architectural styles/patterns, continuously integrated models, and so on.
  • A flexible mechanism for mapping of models into middleware/framework-specific artifacts should also be provided. A uml2qt mapping is already in place, but new ones should be easily defined.

Getting right down to the business instead of talking on abstractions ­čÖé that’s what a QtUml application currently looks like:



QWrappedObjectPointer<QModel> model = new QModel;
model->setName("MyModel");

QWrappedObjectPointer<QPackage> package = new QPackage;
package->setName("Package1");

QWrappedObjectPointer<QPrimitiveType> primitiveType = new QPrimitiveType;
primitiveType->setName("String");

QWrappedObjectPointer<QEnumeration> enumeration = new QEnumeration;
enumeration->setName("DirectionKind");
QWrappedObjectPointer<QEnumerationLiteral> directionIn = new QEnumerationLiteral;
directionIn->setName("DirectionIn");
enumeration->addOwnedLiteral(directionIn);

QWrappedObjectPointer<QClass> class_ = new QClass;
class_->setName("Student");
class_->setAbstract(false);

package->addOwnedType(enumeration);
package->addOwnedType(class_);

model->addPackagedElement(package);
model->addOwnedType(primitiveType);

// query model or perform 'well-formed/sanity/architectural conformance' checks

delete model.data();

Let’s shed some light on the code above. First of all, UML 2.4.1 metamodel specifies a set of 242 metaclasses (193 out of those are concrete metaclasses) with a couple of dreaded diamonds. In order to overcome the inherent QObject disabilities to cope with virtual inheritance, QtMof/QtUml provides (and relies on) the QtWrappedObjects library: an easy-to-use Qt5-based implementation of “smart delegators” and “delegation-based multiple inheritance”. All behind-the-scene hard work is handled by a new smart pointer (QWrappedObjectPointer) and a new casting operator (qwrapperobject_cast). Secondly, model elements can easily be imported from other (meta)models instead of explicitly instantiating them by “new QPrimitiveType“, for example (use QElementImport/QPackageImport for that purpose). Thirdly, an element takes ownership of any (multi-valued or single-valued) UML property which was declared with “aggregate = composite”, so that a single “delete model.data()” do the whole work.

Even though not 100% implemented, UML Profiles and profile applications may currently be defined programmatically:


// Create a profile
 QWrappedObjectPointer<QProfile> profile = new QProfile;
 profile->setName("MyProfile");

// Add a stereotype to profile
 QWrappedObjectPointer<QStereotype> stereotype = new QStereotype;
 stereotype->setName("MyStereotype");
 profile->addPackagedElement(stereotype);

// Show profile's owned stereotypes
 QScopedPointer<QStereotypeList> ownedStereotypes (profile->ownedStereotypes());
 qDebug() << "Owned stereotypes:";
 foreach (QStereotype *ownedStereotype, *ownedStereotypes)
qDebug() << " " << ownedStereotype->name();

// Adding attribute to stereotype
 QWrappedObjectPointer<QPrimitiveType> booleanPrimitiveType = new QPrimitiveType;
 booleanPrimitiveType->setName("boolean");
 QWrappedObjectPointer<QProperty> property = new QProperty;
 property->setName("isTransient");
 property->setType(booleanPrimitiveType);
 stereotype->addOwnedAttribute(property);

The following lines apply an existing UML profile to a package:


// Create UML meta-model element import
 QWrappedObjectPointer<QElementImport> elementImport = new QElementImport;
 elementImport->setImportedElement(umlModel->packagedElements()->toList().first());

// Add meta-class reference to profile
 profile->addMetaclassReference(elementImport);
 profile->addOwnedType(booleanPrimitiveType);

// Create extension
 QWrappedObjectPointer<QExtension> extension = new QExtension;
 extension->setName("class_stereotype");

QWrappedObjectPointer<QProperty> stereotypeProperty = new QProperty;
 stereotypeProperty->setName("base_class");
 stereotypeProperty->setType(class_);

QWrappedObjectPointer<QExtensionEnd> extensionEnd = new QExtensionEnd;
 extensionEnd->setName("extension_stereotype");
 extensionEnd->setType(stereotype);

extension->addMemberEnd(stereotypeProperty);
 extension->addMemberEnd(extensionEnd);
 extension->setOwnedEnd(extensionEnd);

stereotype->addOwnedAttribute(stereotypeProperty);

Of course, a helper function or a QtScript can be created to provide a simpler API for creating and applying profiles. As for the editor, current status is still far from usable but it already works as a proof-of-concept for a metamodel-agnostic tool (it links only against QtWrappedObjects):

arcade

Potential future uses of QtMof/QtUml include: development of model-driven mechanisms in QtCreator/KDevelop, full UML2.4.1/2.5 support in Umbrello, effective Qt code generation from UML models, and so on.

So, if you like modeling and would like to do some code reviewing and/or help hacking QtMof/QtUml you are quite welcome :). You can grab QtMof/QtUml source code at Qt Playground Repository. Alternatively, if you run openSUSE, there is an OBS repository which provides QtMof/QtUml binaries. In order to use them you must include Qt5 and QtMof/QtUml repositories and install the required packages. The libQtMof-devel package provide the examples for profiles and the current editor:

zypper ar -f http://download.opensuse.org/repositories/KDE:/Qt50/openSUSE_12.2 openSUSE-12.2-Qt5
zypper ar -f http://download.opensuse.org/repositories/home:sandroandrade/openSUSE_12.2 openSUSE-12.2-QtMof
zypper ref
zypper in libQtUml-devel

See you …

QObject, multiple inheritance, and smart delegators October 29, 2012

Posted by Sandro Andrade in planetqt-sandroandrade.
8 comments

Hi there,

In last weeks I’ve been working on a Qt-based implementation of OMG’s UML/MOF specifications (more about that coming soon). The normative XMI files provide the full UML meta-model, featuring a number of multiple inheritances and dreaded diamonds. So, you can expect some hard times because of QObject disabilities for handling virtual inheritance and templates (no mix-ins ? no traits ?). Furthermore, a design and analysis tool for handling MOF-based models is also been developed and, as such, should be highly reflective in order to not be tied to any specific meta-model. Qt’s property system could easily do the trick, but keeping reading :).

Since a number of classes in UML meta-model are abstract, a possible solution to overcome QObject’s multiple inheritance issues would be to postpone QObject inheritance to lowest possible class in hierarchy. Also cross you fingers for not having a concrete class inheriting from two (or more) other concrete classes (actually, in UML this only occurs in ‘AssociationClass’). Current implementation is somehow akin to:

QElement is an “abstract” (protected constructor) class:

class Q_UML_EXPORT QElement
{
public:
    virtual ~QElement();
    const QSet<QElement *> *ownedElements() const;
    ...
protected:
    explicit QElement();
};

QNamedElement is also an “abstract” (protected constructor) class:

class Q_UML_EXPORT QNamedElement : public virtual QElement
{
public:
    virtual ~QNamedElement();
    QString name() const;
    void setName(QString name);
    QtUml::VisibilityKind visibility() const;
    void setVisibility(QtUml::VisibilityKind visibility);
    QString qualifiedName() const; // read-only
    QNamespace *namespace_() const;
    ...
protected:
    explicit QNamedElement();
};

QPackage is a concrete class:

class Q_UML_EXPORT QPackage : public QObject, public QNamespace, public QPackageableElement, public QTemplateableElement // dreaded diamonds here
{
    Q_OBJECT
    // From QElement
    Q_PROPERTY(const QSet<QElement *> * ownedElements READ ownedElements)
    // From QNamedElement
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString qualifiedName READ qualifiedName)
    Q_PROPERTY(QElement * namespace_ READ namespace_)

public:
    explicit QPackage(QObject *parent = 0);
    virtual ~QPackage();
};

That’s fine, even though generated documentation becomes somehow weird because of property accessors being implemented in base classes.

Issues arise, however, when implementing the analysis tool. It would be great if each property could be dynamically verified if it is a QNamedElement and its name would be exhibited in a property editor. Something like:

...
int propertyCount = currentElement->metaObject()->propertyCount();
for (int i = 0; i < propertyCount; ++i) {
    QMetaProperty property = element->metaObject()->property(i);
    QString typeName = property.typeName();
    if (typeName.endsWith('*') and !typeName.contains(QRegularExpression ("QSet|QList"))) {
        if (QtUml::QNamedElement *namedElement = property.read(element).value<QtUml::QElement *>()) {
                tableItem->setText(namedElement->name());
            }
}
...

The sad thing is: in Qt4, values can be extracted from QVariant’s only by using the same type used when constructing the QVariant. Qt5 introduces a nifty feature where you can safely extract a QObject * from any QVariant built from a QObjectDerived *. That would easily solve the issue above┬áprovided that┬áQNamedElement would inherit from QObject :).

So, it seems the solution requires inheriting from QObject early in hierarchy (QElement) and get rid of multiple QObject inheritance by using delegation. In addition, accessing ‘sub-objects due to inheritance’ and ‘sub-objects due to delegation’ by using a common API would be a plus. That’s what I came up with:

class MyQObject : public QObject
{
    Q_OBJECT
public:
    explicit MyQObject(QObject *parent = 0);
    virtual ~MyQObject();

    template <class T> friend inline T myqobject_cast(MyQObject *object);

protected:
    QSet<MyQObject *> _subObjects;
};

template <class T>
inline T myqobject_cast(MyQObject *base)
{
    if (dynamic_cast<T>(base))
        return dynamic_cast<T>(base);
    foreach(MyQObject *myqobject, base->_subObjects) {
        T returnValue = myqobject_cast<T>(myqobject);
        if (returnValue != T())
            return returnValue;
    }
    return dynamic_cast<T>(base); // not found
}

A first MyQObject-derived class:

class Derived1 : public MyQObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName)
public:
    explicit Derived1(QObject *parent = 0);
    QString name() const;
    void setName(QString name);

private:
    QString _name;
};

A second MyQObject-derived class (Derived2) would be defined similarly.

A class “multiple inheriting” indirectly from MyQObject would be:

class Join : public MyQObject // no inheritance, no dreaded diamonds, 'smart delegation' instead
{
    Q_OBJECT
public:
    explicit Join(QObject *parent = 0);
    ~Join();

private:
    Derived2 *_derived2;
    Derived1 *_derived1;
};

 

Join::Join(QObject *parent) :
    MyQObject(parent), _derived2(new Derived2), _derived1(new Derived1)
{
    _subObjects.insert(_derived11);
    _subObjects.insert(_derived2);
}

Join::~Join()
{
    delete _derived2;
    delete _derived1;
}

That done, how a client would look like ?

Join *join = new Join;
if (myqobject_cast<MyQObject *>(join))
    qDebug() << "myqobject_cast<MyQObject *>(join) ok";
if (myqobject_cast<Derived1 *>(join))
    qDebug() << "myqobject_cast<Derived1 *>(join) ok";
if (myqobject_cast<Derived2 *>(join))
    qDebug() << "myqobject_cast<Derived2 *>(join) ok";
if (!myqobject_cast<Unrelated *>(join))
    qDebug() << "myqobject_cast<Unrelated *>(join) fail";

That would remove the burden of creating tons of stub methods (if aggregated object is deeply lower in hierarchy) and one more level of indirection (how to simulate virtual methods ?). Yes, sub-objects representing the tip of dreaded diamonds would be duplicated but myqobject_cast would return always the same sub-object.

I’m wondering how much of this could be automated by moc so that we could have something like:

class Join : public MyQObject, wraps Derived1, wraps Derived2
{
    Q_OBJECT
public:
    explicit Join(QObject *parent = 0);
    ~Join();
};

And that’s all, all hard work behind the scenes would be generated by moc ­čÖé Only a little change in perspective when using such object would be needed (think always in terms of myqobject_cast’s).

So, too much engineering ? ­čÖé Any alternative solution ? Comments ?

See you !