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
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):
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 io 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 ...