Copyright © 2004, 2008 International Business Machines Corp.

 

 

Introduction to UML2 Profiles

Summary
This article describes how to work with profiles using the UML2 plug-ins for Eclipse. In particular, it gives an overview of how to create and apply profiles (and their contents) both programmatically and by using the sample UML editor

Kenn Hussey, IBM
July 18, 2006 (Updated: July 11, 2008 for Eclipse 3.4; James Bruck)


Prerequisites

To start using UML2 (and to follow along with the example in this article), you must have Eclipse 3.4, EMF 2.4, and UML2 2.2 installed. 

You can either download the zips individually and expand them out or follow the steps below: 

1)  Download and run Eclipse 3.4

2)  Select the Help > Software Updates … menu

3)  Select the Available Software tab and expand the Ganymede\Model and Model Development tree item.

4)  Select UML2 End-User Features and UML2 Extender SDK.  The version should indicate 2.2.0 (or later).

5)  Click Install.

At this stage, UML2 2.2 and all dependencies should be installed.

Introduction

This article will walk you through the basics of creating and applying profiles using UML2. Using a subset of the Ecore profile (see below) and the model we described in the “Getting Started with UML2” article [1] (the ExtendedPO2 model, shamelessly “borrowed” from the EMF “bible” [2]) as an example, we’ll look at what’s involved in creating some of the more common elements that make up a profile. For each type of element, we’ll first explain the creation process using the sample UML editor and explore how to accomplish the same thing using Java code. Then we’ll look at what’s involved in applying profiles and stereotypes to models. The ExtendedPO2 model is shown below.

As described in the EMF book, some of the attributes of certain Ecore elements cannot be expressed directly in UML™. EMF provides a Rose property file (ecore.pty) that defines all the non- UML™ properties needed to completely express an EMF model using Rational Rose. The same thing can be achieved for a UML2 model using a profile.

The Ecore properties for attributes are shown below. These properties can be expressed in UML2 as properties owned by a stereotype that extends the Property metaclass.

The Ecore properties for association ends are shown below. These properties can also be expressed in UML2 as properties owned by a stereotype that extends the Property metaclass.

Getting Started

Before getting started, you’ll need to create a simple project in your workspace. This project will serve as the container for the profile that we’ll create using the UML editor. To create a simple project for this article, follow these steps:

1) Select the Window > Open Perspective > Other menu item.

2) Select the Resource perspective and press the OK button.

3) Select the File > New > Project... menu item.

4) Select the Project wizard from the General category and press the Next > button.

5) Enter a project name (i.e. “Introduction to UML2 Profiles”), and press the Finish button.

At this point your workspace should look something like this:

OK, that should be enough to get us going with the UML editor. Now, to follow along with the programmatic approach to creating profiles, we’ll assume that you’ve created a Java class (named, say, “IntroductionToUML2Profiles”) in which you can write some code to construct our sample profile. The code snippets we’ll show assume you’ve defined the following utility methods to give the user information on the program’s status:

  public static boolean DEBUG = true;
 
     protected static void out(String output) {
 
            if (DEBUG) {
                 System.out.println(output);
            }
     }
 
     protected static void err(String error) {
         System.err.println(error);
     }

A static debug flag  can be used to enable or disable verbose information printed to the system’s output stream . Errors will always be printed to the system’s error stream .

All righty then! In each of the following subsections, we’ll look at how to create or manipulate a different kind of UML element, starting with profiles.

Creating Profiles

At the root of every UML2 profile is a profile element. It defines limited extensions to a reference metamodel (i.e. UML) with the purpose of adapting the metamodel to a specific platform or domain (e.g. EMF). Metaclasses from the reference metamodel are extended via stereotypes, which are defined as part of profiles. To create a profile using the UML editor, follow these steps:

1) Select a project (i.e. Introduction to UML2 Profiles) in the Project Explorer view and select the File > New > Other... menu item.

2) Select the UML Model wizard from the Example EMF Model Creation Wizards category and press the Next > button.

3) Enter a file name (i.e. “Ecore.profile.uml”) and press the Next > button.

4) Select Profile for the model object and press the Finish button.

5) Select the Window > Show View > Properties menu item.

6) Select the Profile element in the UML editor.

7) Enter a value (i.e. “ecore”) for the Name property in the Properties view.

 By convention, resources that contain profiles end with a .profile.uml file extension in UML2.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically creates and returns a profile with a specified name.

     protected static Profile createProfile(String name) {
         Profile profile = UMLFactory.eINSTANCE.createProfile();
         profile.setName(name);
 
         out("Profile '" + profile.getQualifiedName() + "' created.");
 
          return profile;
     }

First, we ask the UML factory singleton  to create a profile, and we set its name . Then, we output information  to the user to let them know that the profile has been successfully created. Finally, we return  the profile. You’ll notice most, if not all, of the code snippets in this article will follow this pattern – create the element (and set some properties on it), inform the user, and return it.

OK, let’s see this method in action. For example, we could create a profile named ‘ecore’ as follows:

                         Profile ecoreProfile = createProfile("ecore");

Importing Primitive Types

Just like a class, a stereotype may have properties, which may be referred to as tag definitions. The types of these properties may be pre-defined in (domain-specific) libraries referenced by the profile. A profile can be made to reference primitive types from libraries (such as those provided in the org.eclipse.uml2.uml.resources plug-in) by creating an import relationship between the profile and the primitive type. To import a primitive type using the UML editor, follow these steps:

1) Select a package (i.e. <Profile> ecore) in the UML editor.

2) Select the UML Editor > Package > Import Type… menu item.

3) Choose a primitive type (i.e. UMLPrimitiveTypes::Boolean), press the Add button, then press the OK button.

 Import the other required primitive type (UMLPrimitiveTypes::String) into the Ecore profile using the UML editor.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically imports the primitive type with a specified name from the UML primitive types library into a specified package and returns it.

     protected static PrimitiveType importPrimitiveType(
                    org.eclipse.uml2.uml.Package package_, String name) {
         Model umlLibrary = (Model) load(URI
                    .createURI(UMLResource.UML_PRIMITIVE_TYPES_LIBRARY_URI));
 
         PrimitiveType primitiveType = (PrimitiveType) umlLibrary
                    .getOwnedType(name);
 
         package_.createElementImport(primitiveType);
 
            out("Primitive type '" + primitiveType.getQualifiedName()
                    + "' imported.");
 
            return primitiveType;
     }

Here we load the model library  containing the UML primitive types (Boolean, Integer, String, and UnlimitedNatural) using a utility method (described later) and a URI defined on the UMLResource interface. Next, we retrieve the desired (owned) primitive type from the model by name  using one of the convenience methods defined in the UML2 API. Finally, we invoke another convenience method  to create the element import relationship between the package and the element (with default public visibility), notify the user, and return the primitive type.

 The UML resources plug-in (org.eclipse.uml2.uml.resources) provides several model libraries (which by convention have a .library.uml file extension) that contain commonly used primitive types, such as those defined by Java and Ecore (in addition to those defined by UML™ itself). These libraries can be accessed using URIs defined on the UMLResource interface, as shown above.

 In UML2, a method of the form get<feature name>(String) exists for every feature that can contain or reference named elements. In this case, the package has a feature (ownedType) that can contain types, so we pass the unqualified name of the type we are looking for to the getOwnedType(String) convenience method. Behind the scenes, the package will iterate over all of its owned types and return the first one that it finds that has the specified (unqualified) name.

OK, let’s see this method in action. For example, we could import the primitive type named ‘Boolean’ into profile ‘ecore’ as follows:

            PrimitiveType booleanPrimitiveType = importPrimitiveType(ecoreProfile,
                    "Boolean");

 Write code to programmatically import the other required primitive type (i.e. ‘String’) into the Ecore profile.

Creating Enumerations

As with packages, profiles can contain enumerations. An enumeration is a kind of data type whose instances may be any of a number of user-defined enumeration literals. To create an enumeration using the UML editor, follow these steps:

1) Select a profile (i.e. <Profile> ecore) in the UML editor.

2) Right-click and select the New Child > Packaged Element > Enumeration option from the context menu.

3) Enter a value (i.e. “VisibilityKind”) for the Name property in the Properties view.

 Create the other enumeration (i.e. “FeatureKind”) for the Ecore profile using the UML editor.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below (from the GettingStartedWithUML2 class) shows a method that programmatically creates and returns an enumeration with a specified name in a specified package.

     protected static Enumeration createEnumeration(
                    org.eclipse.uml2.uml.Package package_, String name) {
         Enumeration enumeration = (Enumeration) package_
                    .createOwnedEnumeraton(name);
 
            out("Enumeration '" + enumeration.getQualifiedName() + "' created.");
 
            return enumeration;
     }

Here we call the createOwnedEnumeration(String) convenience factory method  to ask the package to create a primitive type with the specified name as one of its packaged elements.

OK, let’s see this method in action. For example, we could create an enumeration named ‘VisibilityKind’ in profile ‘ecore’ as follows:

            Enumeration visibilityKindEnumeration = GettingStartedWithUML2
                    .createEnumeration(ecoreProfile, "VisibilityKind");

 Write code to programmatically create the other enumeration (i.e. ‘FeatureKind’) for the Ecore profile.

Creating Enumeration Literals

An enumeration literal is a user-defined data value for an enumeration. To create an enumeration literal using the UML editor, follow these steps:

1) Select an enumeration (i.e. <Enumeration> VisibilityKind) in the UML editor.

2) Right-click and select the New Child > Owned Literal > Enumeration Literal option from the context menu.

3) Enter a value (i.e. “Unspecified”) for the Name property in the Properties view.

 Create the remaining enumeration literals for the Ecore profile (i.e. “None”, “ReadOnly”, “ReadWrite”, “ReadOnlyUnsettable”, and “ReadWriteUnsettable” for <Enumeration> VisibilityKind; “Unspecified”, “Simple”, “Attribute”, “Element”, “AttributeWildcard”, “ElementWildcard”, and “Group” for <Enumeration FeatureKind>) using the UML editor.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below (from the GettingStartedWithUML2 class) shows a method that programmatically creates and returns an enumeration literal with a specified name in a specified enumeration.

     protected static EnumerationLiteral createEnumerationLiteral(
                    Enumeration enumeration, String name) {
         EnumerationLiteral enumerationLiteral = enumeration
                    .createOwnedLiteral(name);
 
            out("Enumeration literal '" + enumerationLiteral.getQualifiedName()
                    + "' created.");
 
            return enumerationLiteral;
     }

Here we call the createOwnedLiteral(String) convenience factory method  to ask the enumeration to create an enumeration literal with the specified name as one of its owned literals.

OK, let’s see this method in action. For example, we could create an enumeration literal named ‘Unspecified’ in enumeration ‘VisibilityKind’ as follows:

            GettingStartedWithUML2.createEnumerationLiteral(
                    visibilityKindEnumeration, "Unspecified");            

 Write code to programmatically create the remaining enumeration literals (i.e. ‘None’, ‘ReadOnly’, ‘ReadWrite’, ‘ReadOnlyUnsettable’, and ‘ReadWriteUnsettable’ in enumeration ‘VisibilityKind’; ‘Unspecified’, ‘Simple’, ‘Attribute’, ‘Element’, ‘AttributeWildcard’, ‘ElementWildcard’, and ‘Group’ in enumeration ‘FeatureKind’) for the Ecore profile.

Creating Stereotypes

A stereotype defines how an existing metaclass may be extended, and enables the use of platform- or domain-specific terminology or notation in place of, or in addition to, the ones used for the extended metaclass. Each stereotype may extend one or more classes through extensions as part of a profile. To create a stereotype using the UML editor, follow these steps:

1) Select a profile (i.e. <Profile> ecore) in the UML editor.

2) Right-click and select the New Child > Packaged Element > Stereotype option from the context menu.

3) Enter a value (i.e. “EStructuralFeature”) for the Name property in the Properties view.

4) Select a value (i.e. true) for the Is Abstract property in the Properties view.

 Create the remaining stereotypes for the Ecore profile (i.e. “EAttribute” and “EReference”) using the UML editor.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically creates and returns a(n) (abstract) stereotype with a specified name in a specified profile.

     protected static Stereotype createStereotype(Profile profile, String name,
                    boolean isAbstract) {
         Stereotype stereotype = profile.createOwnedStereotype(name, isAbstract);
 
            out("Stereotype '" + stereotype.getQualifiedName() + "' created.");
 
            return stereotype;
     }

Here we call the createOwnedStereotype(String, boolean) convenience factory method  to ask the profile to create a stereotype with the specified name as one of its owned members, and set the isAbstract attribute of the stereotype based on the specified boolean argument.

OK, let’s see this method in action. For example, we could create an abstract stereotype named ‘EStructuralFeature’ in profile ‘ecore’ as follows:

            Stereotype eStructuralFeatureStereotype = createStereotype(
                    ecoreProfile, "EStructuralFeature", true);

 Write code to programmatically create the remaining (non-abstract) stereotypes (i.e. ‘EAttribute’ and ‘EReference’) for the Ecore profile.

Creating Stereotype Generalizations

Just like classes, stereotypes may be involved in generalizations. A generalization is a taxonomic relationship between a specific classifier and a more general classifier whereby each instance of the specific classifier is also an indirect instance of, and inherits the features of, the general classifier. To create a stereotype generalization using the UML editor, follow these steps:

1) Select a stereotype (i.e. <Stereotype> EAttribute) in the UML editor.

2) Right-click and select the New Child > Generalization > Generalization option from the context menu.

3) Select a value (i.e. ecore::EStructuralFeature) for the General property in the Properties view.

 Create the other generalization (i.e. between EReference and EStructuralFeature) for the Ecore profile using the UML editor.

 Be sure to pick the stereotype (the first ecore::EStructuralFeature item) and not the class (it should show up in Properties view as <Stereotype> EStructuralFeature, not <Class> EStructuralFeature), which comes from the Ecore metamodel.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below (from the GettingStartedWithUML2 class) shows a method that programmatically creates and returns generalization between specified specific and general classifiers.

     protected static Generalization createGeneralization(
                    Classifier specificClassifier, Classifier generalClassifier) {
            Generalization generalization = specificClassifier
                 .createGeneralization(generalClassifier);
 
            out("Generalization " + specificClassifier.getQualifiedName() + " ->> "
                    + generalClassifier.getQualifiedName() + " created.");
 
            return generalization;
     }

Here we call a convenience factory method on the specific classifier that creates a generalization  as one of its children and sets the general classifier to the specified argument.

OK, let’s see this method in action. For example, we could create a generalization between specific stereotype ‘EAttribute’ and general stereotype ‘EStructuralFeature’ as follows:

            GettingStartedWithUML2.createGeneralization(eAttributeStereotype,
                    eStructuralFeatureStereotype);

 Write code to programmatically create the other generalization (i.e. between ‘EReference’ and ‘EStructuralFeature’) for the Ecore profile.

Creating Stereotype Properties

Again just like classes, stereotypes may have properties (attributes). When a stereotype is applied to a model element, the values of the properties may be referred to as tagged values. To create a stereotype property using the UML editor, follow these steps:

1) Select a stereotype (i.e. <Stereotype> EStructuralFeature) in the UML editor.

2) Right-click and select the New Child > Owned Attribute > Property option from the context menu.

3) Enter a value (i.e. "isTransient”) for the Name property in the Properties view.

4) Select a value (i.e. UMLPrimitiveTypes::Boolean) for the Type property in the Properties view.

5) Enter a value (i.e. 0) for the Lower property in the Properties view.

6) Select the property (i.e. <Property> isTransient) in the UML editor.

7) Right-click and select the New Child > Default Value Literal Boolean option from the context menu.

 Default values for properties (and parameters) are represented as value specifications (first-class objects) in UML™ 2.0. Here we have selected a literal Boolean (whose default value is false) as the default value of our property since its type is Boolean. If the type of the property were String, we’d have used a literal string instead. Once a default value specification has been created, its value can alternatively be set with the owning property selected via the Default property in the Properties view.

 Create the remaining stereotype properties for the Ecore profile (i.e. “isUnsettable”, “isVolatile”, “visibility”, “xmlName”, “xmlNamespace”, and “xmlFeatureKind” for <Stereotype> EStructuralFeature; “attributeName” and “isID” for <Stereotype> EAttribute; “referenceName” and “isResolveProxies” for <Stereotype> EReference) using the UML editor.

 Be sure to set the appropriate default value for each stereotype property so that it is consistent with the corresponding Ecore property for Rose. In particular, the default value for the “isResolveProxies” should be a literal Boolean with a value of true instead of the default (!) default value of false. Note also that the types of the “visibility” and “xmlFeatureKind” properties should be the enumerations we created earlier (i.e. ecore::Visibility and ecore::FeatureKind, respectively).

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below (from the GettingStartedWithUML2 class) shows a method that programmatically creates and returns an attribute with a specified upper bound, lower bound, type, and name in a specified class.

     protected static Property createAttribute(org.eclipse.uml2.uml.Class class_,
                    String name, Type type, int lowerBound, int upperBound) {
         Property attribute = class_.createOwnedAttribute(name, type,
                    lowerBound, upperBound);
 
            StringBuffer sb = new StringBuffer();
 
            sb.append("Attribute '");
 
            sb.append(attribute.getQualifiedName());
 
            sb.append("' : ");
 
            sb.append(type.getQualifiedName());
 
            sb.append(" [");
            sb.append(lowerBound);
            sb.append("..");
            sb.append(LiteralUnlimitedNatural.UNLIMITED == upperBound
                    ? "*"
                    : String.valueOf(upperBound));
            sb.append("]");
 
            sb.append(" created.");
 
            out(sb.toString());
 
            return attribute;
     }

Here we call the createOwnedAttribute(String, Type, int, int) convenience factory method  to ask the class to create a property as one of its owned attributes, set the type of the attribute to the specified type, and set the lower and upper bounds of the attribute (the factory method creates a literal integer and literal unlimited natural, respectively, and sets its value to the specified integer value).

 The LiteralUnlimitedNatural.UNLIMITED constant represents the unlimited value for upper bounds (-1), as it does in EMF.

OK, let’s see this method in action. For example, we could create an attribute with multiplicity 0..1 of type ‘UMLPrimitiveTypes::Boolean’ named ‘isTransient’ in stereotype ‘EStructuralFeature’ as follows:

            Property isTransientProperty = GettingStartedWithUML2.createAttribute(
                    eStructuralFeatureStereotype, "isTransient", booleanPrimitiveType,
                    0, 1);

 Write code to programmatically create the remaining stereotype properties (i.e. ‘isUnsettable’, ‘isVolatile’, ‘visibility’, ‘xmlName’, ‘xmlNamespace’, and ‘xmlFeatureKind’ in stereotype ‘EStructuralFeature’; ‘attributeName’ and ‘isID’ in stereotype ‘EAttribute’; ‘referenceName’ and ‘isResolveProxies’ in stereotype ‘EReference’) for the Ecore profile.

Referencing Metaclasses

A profile is a restricted form of a metamodel that must always be related to a reference metamodel (i.e. UML). A profile cannot be used without its reference metamodel; it defines a limited capability to extend metaclasses of the reference metamodel via stereotypes. Profiles can be made to reference metaclasses from metamodels by creating an import relationship between the profile and the reference metaclass. To reference a metaclass using the UML editor, follow these steps:

1) Select a profile (i.e. <Profile> ecore) in the UML editor.

2) Select the UML Editor > Profile > Reference Metaclass menu item.

3) Choose a metaclass (i.e. uml::Property), press the Add button, then press the OK button.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically references the metaclass with a specified name in the UML metamodel from a specified profile and returns it.

     protected static org.eclipse.uml2.uml.Class referenceMetaclass(Profile profile,
                    String name) {
         Model umlMetamodel = (Model) load(URI
                    .createURI(UML2Resource.UML2_METAMODEL_URI));
 
         org.eclipse.uml2.Class metaclass = (org.eclipse.uml2.uml.Class) umlMetamodel
                    .getOwnedType(name);
 
         profile.createMetaclassReference(metaclass);
 
            out("Metaclass '" + metaclass.getQualifiedName() + "' referenced.");
 
            return metaclass;
     }

Here we load the metamodel  containing the UML metaclasses using a utility method (described later) and a URI defined on the UMLResource interface. Next, we retrieve the desired (owned) metaclass from the (meta)model by name  using the convenience method. Finally, we invoke another convenience method  to create the element import relationship between the profile and the metaclass, notify the user, and return the metaclass.

 The UML resources plug-in (org.eclipse.uml2.uml.resources) provides two metamodels (which by convention have a .metamodel.uml file extension), UML2 and Ecore. These metamodels can be accessed using URIs defined on the UMLResource interface, as shown above.

OK, let’s see this method in action. For example, we could reference the metaclass named ‘Property’ from profile ‘ecore’ as follows:

            org.eclipse.uml2.uml.Class propertyMetaclass = referenceMetaclass(
                    ecoreProfile, UMLPackage.Literals.PROPERTY.getName());

Creating Extensions

Extensions are used to indicate that the properties of metaclasses are extended through stereotypes, and give the ability to flexibly apply (and later unapply) stereotypes to elements. An extension is a kind of association, one end of which is an ordinary property, and the other is an extension end. An extension may be required (depending on the lower bound of the extension end), which indicates that an instance of the extending stereotype must be created whenever an instance of the extended metaclass is created. To create an extension using the UML editor, follow these steps:

1) Select a stereotype (i.e. <Stereotype> EAttribute) in the UML editor.

2) Select the UML Editor > Stereotype > Create Extension… menu item.

3) Choose a metaclass (i.e. uml::Property), press the Add button, then press the OK button.

 Create the other extension (i.e. between uml::Property and <Stereotype> EReference) for the Ecore profile using the UML editor.

At this point your workspace should look something like this:

 You’ll notice that a number of new elements have appeared in the UML editor. In particular, you’ll see a new stereotype property with a name of the form base_<metaclass name>, an extension with a name of the form <metaclass name>_<stereotype name>, and an extension end with a name of the form extension_<stereotype name>. It’s important that these elements not be modified, since the UML2 profile mechanism depends on these constructs.

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically creates and returns a(n) (required) extension between a specified metaclass and a specified stereotype.

     protected static Extension createExtension(
                    org.eclipse.uml2.uml.Class metaclass, Stereotype stereotype,
                    boolean required) {
         Extension extension = stereotype.createExtension(metaclass, required);
 
            out((required
                    ? "Required extension '"
                    : "Extension '") + extension.getQualifiedName() + "' created.");
 
            return extension;
     }

Here we call a convenience method on the stereotype that creates an extension (and its ends) between it and a metaclass  as one of its siblings (i.e. as a child of its profile namespace). Behind the scenes, the stereotype also creates the ends of the extension, resulting in a new property on the stereotype (with a special name) and an extension end owned by the extension (again, with a special name).

OK, let’s see this method in action. For example, we could create a non-required extension between metaclass ‘Property’ and stereotype ‘EAttribute’ in profile ‘ecore’ as follows:

            createExtension(propertyMetaclass, eAttributeStereotype, false);

 Write code to programmatically create the other extension (i.e. between metaclass ‘Property’ and stereotype ‘EReference’) for the Ecore profile.

Defining Profiles

There. We’re done creating (a scaled down version of) the Ecore profile, and we’re ready to start using it. But before we can, there’s one final thing we need to do – define it. Since a profile effectively represents an augmentation of a reference metamodel (UML), in order for the extensions we’ve defined to appear as though they’re part of the UML metamodel, they need to be “defined” at the meta-metamodel (i.e. Ecore) level. The implementation of profile support in UML2 does this by converting the contents of the profile to an equivalent Ecore representation that is stored as an annotation on the profile. Then, when a profile and its stereotypes are applied to a model and its elements, dynamic EMF (see the EMF book for details) is used to store property values for the stereotypes. For the most part, you can ignore this complexity, as long as you remember to define your profile before using it. To define a profile using the UML editor, follow these steps:

1) Select a profile (i.e. <Profile> ecore) in the UML editor.

2) Select the UML Editor > Profile > Define menu item.

At this point your workspace should look something like this:

 You’ll notice that an annotation with source http://www.eclipse.org/uml2/2.0.0/UML has been attached to the profile. It contains the generated Ecore representation (an Ecore package with classes, attributes, enums, etc.) of the profile. As with extensions, it’s important that this annotation (and its contents) not be modified, since the UML2 profile mechanism depends on these constructs.

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically defines a specified profile.

     protected static void defineProfile(Profile profile) {
         profile.define();
 
            out("Profile '" + profile.getQualifiedName() + "' defined.");
     }

Here we call a convenience method on the profile  that generates the Ecore representation of the profile and increments its version.

OK, let’s see this method in action. For example, we could define the ‘ecore’ profile as follows:

            defineProfile(ecoreProfile);

Saving Profiles

Now that we’ve spent all this time creating and defining a profile, we’d better save our work. When we created our profile using the UML model wizard, a UML resource was created for us, so now all that needs to be done is to serialize the contents of our profile as XMI to a file on disk (i.e. Ecore.profile.uml). To save a profile using the UML editor, follow these steps:

1) Select the File > Save menu item.

It’s that simple. Programmatically, we have a bit more work to do because so far, we’ve been creating our profile in a vacuum, i.e. without a containing resource. The code snippet below shows a method that saves a specified package to a resource with a specified URI.

     protected static void save(org.eclipse.uml2.uml.Package package_, URI uri) {
         Resource resource = RESOURCE_SET.createResource(uri);
         resource.getContents().add(package_);
 
            try {
                 resource.save(null);
                    out("Done.");
            } catch (IOException ioe) {
                 err(ioe.getMessage());
            }
     }

Here we use a statically initialized resource set  to create a resource with the specified URI, add the package to the resource’s contents , and ask the resource to save itself  using the default options. If an exception occurs, we notify the user  via our handy utility method.

OK, let’s see this method in action. For example, we could save the ‘ecore’ profile to a resource with URI ‘Ecore.profile.uml’ (relative to a URI passed in as an argument) as follows:

            save(ecoreProfile, URI.createURI(args[0]).appendSegment("Ecore")
                    .appendFileExtension(UMLResource.PROFILE_FILE_EXTENSION));

 The UMLResource.PROFILE_FILE_EXTENSION constant represents the file extension for UML2 profiles (.profile.uml).

Loading Models

In order to make use of our profile, we’ll need to open a model and load the profile. We’ll use the ExtendedPO2 model that was created in the “Getting Started with UML2” article (you’ll need to copy it into your project and refresh the workspace first).

To open a model using the UML editor, follow these steps:

1) Double-click on the resource (i.e. ExtendedPO2.uml) in the Navigator view.

Behind the scenes, a resource is obtained from the right kind resource factory (based on the extension ‘.uml’) and loaded, and then a new UML editor is opened and populated with the resource’s contents.

To load a profile (or any resource, actually) using the UML editor, follow these steps:

1) Select the UML Editor > Load Resource… menu item.

2) Press the Browse Workspace… button.

3) Select a resource (i.e. Introduction to UML2 Profiles/Ecore.profile.uml) and press the OK button.

4) Press the OK button.

At this point your workspace should look something like this:

Programmatically, we have a bit more work to do. The code snippet below shows a method that loads a package from a resource with a specified URI.

   protected static org.eclipse.uml2.uml.Package load(URI uri) {
           org.eclipse.uml2.uml.Package package_ = null;
 
           try {
                Resource resource = RESOURCE_SET.getResource(uri, true);
 
                package_ = (org.eclipse.uml2.uml.Package) EcoreUtil.getObjectByType(
                          resource.getContents(), UMLPackage.Literals.PACKAGE);
           } catch (WrappedException we) {
                err(we.getMessage());
                   System.exit(1);
           }
 
        return package_;
   }

Here we obtain a resource with the specified URI from our statically initialized resource set , asking that it be loaded on demand. Next, we use an EMF utility method  to obtain the first object of type Package from the resource’s contents. If an exception occurs, we notify the user  via our handy utility method. Finally, if all is well, we return  the package.

 The EcoreUtil class (provided by EMF) defines a number of utilities that you may find quite useful when working with EMF-based resources.

OK, let’s see this method in action. For example, we could load the ‘epo2’ model from a resource with URI ‘ExtendedPO2.uml’ (relative to a URI passed in as an argument) as follows:

           Model epo2Model = (Model) load(URI.createURI(args[0]).appendSegment(
                   "ExtendedPO2").appendFileExtension(UMLResource.FILE_EXTENSION));

Applying Profiles

OK, our profile has been created, defined, and saved, and we’re ready to apply it to our model. Applying a profile means that it is allowed (but not necessarily required) to apply the stereotypes that are defined in the profile to elements in the package. A profile application is a special type of package import that indicates that a profile has been applied to a package. To apply a profile to a package using the UML editor, follow these steps:

1) Select a package (i.e. <Model> epo2) in the UML editor.

2) Select the UML Editor > Package > Apply Profile… menu item.

3) Choose a profile (i.e. ecore), press the Add button, then press the OK button.

 Be sure to pick the profile we’ve created instead of the built-in profile provided by the UML resources plug-in (i.e. ecore, not Ecore).

At this point your workspace should look something like this:

 You’ll notice another annotation (with source http://www.eclipse.org/uml2/2.0.0/UML) has been attached, in this case to keep track of the Ecore representation for the definition of the profile that is currently applied to the package. Again, it’s important that this annotation not be modified, since the UML2 profile mechanism depends on this construct. Note that a newer definition of the profile can be applied using the same menu item, and a profile (along with all of its stereotypes) can be unapplied using the UML Editor > Package > Unapply Profile… menu item.

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically applies a specified profile to a specified package.

     protected static void applyProfile(org.eclipse.uml2.uml.Package package_,
                    Profile profile) {
         package_.applyProfile(profile);
 
            out("Profile '" + profile.getQualifiedName() + "' applied to package '"
                    + package_.getQualifiedName() + "'.");
     }

Here we call a convenience method on the package  that creates a profile application on the package and sets the profile as the imported profile.

OK, let’s see this method in action. For example, we could apply the ‘ecore’ profile to the ‘epo2’ model as follows:

              applyProfile(epo2Model, ecoreProfile);

Applying Stereotypes

We’re on the home stretch now… Once a profile has been applied to a package, stereotypes defined in the profile can be applied to instances of the appropriate metaclasses (as per the defined extensions). When a stereotype is applied to an element, that element is effectively extended with the properties that are defined as part of the stereotype. To apply a stereotype to an element using the UML editor, follow these steps:

1) Select an element (i.e. <Property> pendingOrders : PurchaseOrder [0..*] in <Class> Supplier) in the UML editor.

2) Select the UML Editor > Element > Apply Stereotype… menu item.

3) Choose a stereotype (i.e. ecore::EReference), press the Add button, then press the OK button.

 Apply the appropriate stereotypes to other properties (<Property> shippedOrders : PurchaseOrder [0..*] in <Class> Supplier; <Property> totalAmount : int [0..1], <Property> previousOrder : PurchaseOrder [0..1], and <Property> customer : Customer in <Class> PurchaseOrder; <Property> orders : PurchaseOrder [0..*] in <Class> Customer) in the ExtendedPO2 model using the UML editor.

At this point your workspace should look something like this:

 A stereotype (and its tagged values) can be unapplied using the UML Editor > Element > Unapply Stereotype… menu item.

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically applies a specified stereotype to a specified (named) element.

   protected static void applyStereotype(NamedElement namedElement,
                   Stereotype stereotype) {
        namedElement.applyStereotype(stereotype);
 
           out("Stereotype '" + stereotype.getQualifiedName()
                   + "' applied to element '" + namedElement.getQualifiedName() + "'.");
   }

Here we call a convenience method on the element  that creates an instance of the Ecore class representing the specified stereotype (using dynamic EMF) and attaches it to the element using an annotation.

OK, let’s see this method in action. For example, we could apply the ‘EReference’ stereotype to the ‘pendingOrders’ property of the ‘Supplier’ class in the ‘epo2’ model as follows:

           org.eclipse.uml2.uml.Class supplierClass = (org.eclipse.uml2.uml.Class) epo2Model
                   .getOwnedType("Supplier");
 
           Property pendingOrdersProperty = supplierClass
                   .getOwnedAttribute("pendingOrders", null);
           applyStereotype(pendingOrdersProperty, eReferenceStereotype);

 Write code to programmatically apply the appropriate stereotypes to other properties (i.e. ‘epo2::Supplier::shippedOrders’, ‘epo2::PurchaseOrder::totalAmount’, ‘epo2::PurchaseOrder::previousOrder’, ‘epo2::PurchaseOrder::customer’, and ‘epo2::Customer::orders’) in the ExtendedPO2 model.

Accessing Stereotype Property Values

At long last, we’re ready to get and set values for the properties (tagged values) defined in our extensions. To get and set the value for a stereotype property using the UML editor, follow these steps:

1) Select the element to which the stereotyped has been applied (i.e. <<eReference>> <Property> pendingOrders : PurchaseOrder [0..*] in <Class> Supplier) in the UML editor.

2) Enter or select a value (i.e. false) for the property (i.e. Is Resolve Proxies) under the category named for the stereotype (i.e. EReference) in the Properties view.

 Get and set values for other stereotype properties (i.e. Is Transient and Is Resolve Proxies under EReference for <<eReference>> <Property> pendingOrders : PurchaseOrder [0..*] in <Class> Supplier; Is Volatile, Is Transient, and Is Resolve Proxies under EReference for <<eReference>> <Property> shippedOrders : PurchaseOrder [0..*] in <Class> Supplier; Is Volatile and Is Transient under EAttribute for <<eAttribute>> <Property> totalAmount : int [0..1] in <Class> PurchaseOrder; Is Resolve Proxies under EReference for <<eReference>> <Property> previousOrder : PurchaseOrder [0..1] in <Class> PurchaseOrder; Is Resolve Proxies under EReference for <<eReference>> <Property> customer : Customer in <Class> PurchaseOrder; Is Resolve Proxies under EReference for <<eReference>> <Property> orders : PurchaseOrder [0..*] in <Class> Customer) in the ExtendedPO2 model using the UML editor.

At this point your workspace should look something like this:

Let’s look at how to perform the same task using Java code. The code snippet below shows a method that programmatically gets and returns the value of a specified property of a specified stereotype for a specified element.

   protected static Object getStereotypePropertyValue(
                   NamedElement namedElement, Stereotype stereotype, Property property) {
        Object value = namedElement.getValue(stereotype, property.getName());
 
           out("Value of stereotype property '" + property.getQualifiedName()
                   + "' on element '" + namedElement.getQualifiedName() + "' is "
                   + String.valueOf(value) + ".");
 
           return value;
   }

Here we call a convenience method on the (named) element  that retrieves the value of a property with a specified name from the dynamically created Ecore object instance corresponding to the specified applied stereotype, notifies the user, and returns it.

OK, let’s see this method in action. For example, we could get the value of the ‘isVolatile’ property of the ‘EReference’ stereotype for the ‘pendingOrders’ property of the ‘Supplier’ class in the ‘epo2’ model as follows:

           getStereotypePropertyValue(pendingOrdersProperty, eReferenceStereotype,
                   isVolatileProperty);

 Write code to programmatically get the values of the other stereotype properties for elements in the ExtendedPO2 model.

The code snippet below shows a method that programmatically sets the value of a specified property of a specified stereotype for a specified element to a specified value.

   protected static void setStereotypePropertyValue(NamedElement namedElement,
                   Stereotype stereotype, Property property, Object value) {
        namedElement.setValue(stereotype, property.getName(), value);
 
           out("Value of stereotype property '" + property.getQualifiedName()
                   + "' on element '" + namedElement.getQualifiedName() + "' set to "
                   + String.valueOf(value) + ".");
   }

Here we call a convenience method on the (named) element  that sets the value of a property with a specified name in the dynamically created Ecore object instance corresponding to the specified applied stereotype and notifies the user.

OK, let’s see this method in action. For example, we could set the value of the ‘isVolatile’ property of the ‘EReference’ stereotype for the ‘pendingOrders’ property of the ‘Supplier’ class in the ‘epo2’ model to Boolean.TRUE as follows:

           setStereotypePropertyValue(pendingOrdersProperty, eReferenceStereotype,
                   isVolatileProperty, Boolean.TRUE);

 Write code to programmatically set the values of the other stereotype properties for elements in the ExtendedPO2 model.

Conclusion

Congratulations! If you’ve made it this far, you’ve successfully created and applied a simple profile programmatically and/or using the UML editor. There’s a whole lot more that could be said, but the purpose of this article was just to introduce you to the concepts. Stay tuned for more articles on how to develop tools with UML2.

For more information on UML2, visit the home page or join the newsgroup.

References

[1] K. Hussey. “Getting Started with UML2”. International Business Machines Corp., 2004, 2006.

[2] F. Budinsky, D. Steinberg, E. Merks, R. Ellersick, and T. J. Grose. Eclipse Modeling Framework. Pearson Education, Inc., Boston, MA, 2003.

Source Code

To run the example or view the source code for this article, unzip uml2.articles_200607181325.zip into your Eclipse home directory and import the com.ibm.uml2.articles plug-in into your workspace as a binary project with linked content (File > Import… > External Plug-ins and Fragments). You can run the IntroductionToUML2Profiles class as a Java application with two file URIs (e.g. “file:/C:/Introduction to UML2 Profiles” “jar:file:/C:/Eclipse 3.2/eclipse/plugins/org.eclipse.uml2.uml.resources_2.0.0.v200606221411.jar!/”) as program arguments. The first URI should refer to a folder that contains the ExtendedPO2 model described in the “Getting Started with UML2” article. The second URI should use the JAR scheme (since the UML resources plug-in is JAR’d as of UML2 1.1) and refer to the location of the org.eclipse.uml2.uml.resources plug-in’s JAR archive.

Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.