Skip to main content
Jump to: navigation, search

Graphical Modeling Framework/Tutorial/Part 2

< Graphical Modeling Framework‎ | Tutorial
Revision as of 08:35, 8 March 2006 by Rgronback (Talk | contribs) (Graphical Definition)

In this second part of the GMF Tutorial, some of the more advanced capabilities of the generation and runtime frameworks will be explored. Specifically, information on adding compartments, connections, feature initializers, diagram validation, and nested child nodes will be covered. A project containing the full solution set of projects for this segment is found here. Viewlets will be available after appropriate sections below to focus their content and keep them short.


Compartments

Stickynote.png

Let's add a compartment to our Topic node to allow discussion threads to be added. In order to illustrate how to allow nodes within compartments, we will represent Thread items as yellow sticky notes and allow ThreadItem elements to appear as list items within them. So, we will have nodes with a compartment list nested within a parent node compartment. A preview of where we're going is seen to the right. And yes, it's a bit contrived, but it does give us an opportunity to combine a lot of features in one section.

Some of the steps in this section are a bit complex, so the viewlet should prove helpful in getting things right. Of course, in the future an improved user interface will be provided to hide some of the complexities found here, but for now consider it an opportunity to see how things really work ;)

Graphical Definition

Rounded figure.png

Open up your graphical definition again and let's first take care of giving our Topics a rounded rectangle. Right-click the Figure Gallery and add a new Rounded Rectangle, naming it RoundedTopicFigure. Adjust the Corner Width and Height property values to 20 (or whatever you'd like). To alter the default line color, right-click on the RoundedTopicFigure and add a new Child | Foreground Color RGB Color with R=220, G=220, B=250 (or whatever you'd like). Then, just change the Figure property on your TopicNode to use this figure and not the Rectangle TopicFigure.

We'll reuse the old TopicFigure rectangle for our sticky note. Rename it StickyNoteFigure and give it a Background Color RGB Color child with values R=250, G=250, B=190 (a pale yellow, but feel free to change to suit your taste). Create a new Node on the Canvas to use this figure and name it ThreadNode.

Compartment graph.png

As we'd like to have discussion threads within a compartment of our topic nodes, we'll need to add one to our Canvas by right-clicking and selecting New Child | Compartment. Give it the name ThreadCompartment and select RoundedTopicFigure for its Figure property.

We'd also like to give threads their own compartment to contain a list of thread items for the subject. Create another compartment for our ThreadNode named ThreadItemCompartment which will use StickyNoteFigure as its Figure. So, our topics will be rounded rectangles which will contain child rectangles within a compartment. These child rectangles will also have a compartment, but they will not contain rectangles, but rather, just a simple list of discussion threads. These will be represented with a Label.

Label graph.png

Add a new Label to the Figure Gallery for our thread items named ThreadItemFigure. Create a corresponding Node on the Canvas named ThreadItemNode and set its Figure to the ThreadItemFigure label.

Now, regarding the other labels we have. If you noticed in the diagram test run, the labels for Topics were external to the node. What we'd like for both our Topics and our Sticky Notes is to have the label be within the figure. To accomplish this for the Topic, simply drag and drop the TopicNameFigure label to under the RoundedTopicFigure, making it a child element. Add a similar Label as a child of your StickyNoteFigure named ThreadNameFigure. Finally, create new child Diagram Labels on the Canvas named ThreadNameLabel (assign it our ThreadNameFigure) and ThreadItemLabel (assign it to our ThreadItemFigure).

In summary, you should now have three nodes, one connection, two compartments, and three diagram labels as shown in the figure.

In the future, graphical definitions will be done using a designer bootstrapped by GMF itself. The figures you have created here will be done visually, rather than by manipulating a model using the generated EMF editor.

Tooling Definition

We'll need a tool to add Thread nodes and ThreadItems, so open mindmap.gmftool and copy/paste the Topic tool and rename Thread. Repeat for a ThreadItem tool. Next, we'll need to complete our mappings, so reopen your mindmap.gmfmap file.

Mapping Definition

Compartment map.png

First, we'll need to add a new Compartment Mapping to our TopicNode Node Mapping and select ThreadCompartment for its Compartment property. Add a Child Reference to the Node Mapping and select 'EReference comments' for its Containment Feature and ThreadCompartment for the Compartment. To the Child Reference, add a child Node Mapping for our ThreadNode and select our Thread class for the domain Element. Set the Tool to our Thread tool as well.

To our ThreadNode mapping, add a child Label Mapping. Select our ThreadNameLabel for the Diagram Label and the 'EAttribute subject' feature from our Thread class for the label's Feature property.

Repeat these steps to add a Compartment Mapping for our ThreadItemCompartment, a Child Reference for our 'EReference items' feature on our Thread class, a child Node Mapping for our ThreadItem class (with corresponding ThreadItemNode and tool), and finally a Label Mapping for our ThreadItemLabel to display the 'EAttribute body' feature of our ThreadItem class.

Although this mapping seems rather complicated, it will not appear so when the graphical UI is available for this and the graphical definition model. At this point, you can regenerate the mindmap.gmfgen model and diagram plugin code.

Tip : A level of validation is available for mapping definitions and will take place prior to creation of the generator model. For example, remove the mapping for 'Target Feature' on a Link Mapping and attempt to create the mindmap.gmfgen model. When you do, you will receive an error message indicating that the property is required.

Constraints

Link2self.png

Currently, the diagram will allow you to make a subtopic link from one Topic to itself, as seen below. Clearly, this does not make sense for our Mindmap, so we'd like to prevent this somehow.

Tip : Before continuing on with this section, be sure you have copied the antlr.jar file into the 'lib' folder of the plug-in, as referenced on the download page for GMF. The OCL capabilities are temporarily dependent on ANTLR, which is not bundled with GMF. Also, note that the EMF Technology (EMFT) project now has downloads and more information on the OCL and model validation components here.


Link constraint.png

Let's return to our mapping definition, and to the 'Link Mapping' we created earlier. To add a constraint, we begin by right-clicking on the 'Link Mapping' and selecting 'New Child > Link Constraints'. To the Link Constraint, right-click and select 'New Child > Source End Constraint'. The 'Language' property defaults to 'ocl' and we'll need to add the following OCL statement to the 'Body' property: self <> oppositeEnd, as seen in the image below. Then, go through the usual regeneration of mindmap.gmfgen and diagram code and try it out. You will no longer be able to make a link from a Topic to itself.

So, now to explain what is happening here. As you can tell from the context above, we've added a constraint to the creation of a link, based on its source end; that is, the Topic element from which a link is being created. In the OCL we've specified the only condition that will evaluate to true, and therefore allow the link to be created, is the condition where the source element is not equal to the 'oppositeEnd' of the link (the target). In this case, the context of 'self' is the source Topic, and 'oppositeEnd' is a custom variable added to the parser environment for link constraints.

Clearly, this is a very simple constraint, and one that could very well have been defined in the domain model itself and respected by the graphical editor automatically. We will look more closely at constraints in future versions of this tutorial as support for their use matures.

Another Connection

Dependency link def.png

Let's look now at the 'Relationship' element of our domain model. It specifies a number of possible relationships that may be indicated between Topic elements, in addition to the subtopic relationship we have supported thus far. We will add support for this type of Connection, as it will illustrate more completely the properties available for a Link Mapping within GMF.

Returning to our graphical definition model (mindmap.gmfgraph), let's right-click on our gallery and add a 'New Child > Polyline Connection'. Name it 'DashedLineOpenArrow' and change 'Line Kind' to LINE_DASH. Then add a 'New Child > Polyline Decoration' element to the Figure Gallery and name it 'OpenArrow'. Add three Template Point children to the OpenArrow with X:Y values of -1:1, 0:0, and -1:-1. Finally, add the OpenArrow as a Target Decoration to the DashedLineOpenArrow polyline.

Now that you have the figure defined, create a corresponding Connection on our Canvas named 'RelationshipLink', selecting our 'DashedLineOpenArrow' as its 'Figure'.

We'll need a tool to create these links, so reopen your mindmap.gmftool model. We already have one link tool for subtopics, but its in the same tool group as our node elements. Let's create a new Tool Group under our Palette for links named 'Links' and drag-n-drop our TopicSubtopics tool into this new group. Then, copy/paste the tool to create our new Relationship Creation Tool. While we're here, rename the TopicSubtopics tool to just Subtopic.

Dependency link mapping.png

In the mapping definition, due to our changes in the tooling model, you'll need to update your tool selections for your existing nodes and link. Then, create a new 'Link Mapping' and fill in its properties to match what's displayed in the image.

Tip : Be aware of such limitations when working with multiple models, as until there is true refactoring support, you will need to be conscious of changes in definition models and how they will impact 'downstream' models, such as our mapping model.

In this mapping, we'll start with the 'Domain meta information > Element' property. It represents the element represented by this link in the domain, which is simply the 'EClass Relationship' element. Recall that in our previous link mapping, we left this and other properties blank. In that case, our target element for the link was represented by an element (Topic) added to a list of reference held in our source element (also a Topic). In this case, the link is represented in the domain by a class of its own, so more information is required in the link mapping. This class, the Relationship class of the domain model, is contained in a list of references in the Map element, which explains the 'Domain meta feature > Containment Feature' map to 'EReference relations'.

Continuing the mapping description, the 'Target Feature' in this case is mapped to the 'EReference target' domain model element, indicating that targets of the link are added to this list in the domain when the link is created. Similarly, the 'Source Feature' maps to the 'EReference source' domain model element. And of course, we have our straightforward tool mapping and 'Diagram Link' mapping to our RelationshipLink.

Dependency link.png

Now, we can regenerate our diagram code as before, launch our diagram workspace and test this new link. Here is an example of the results. What we will now need to do is initialize the link to be of the proper type (dependency, includes, extends) when created. We will use separate tools for each, and could also opt to use distinct visualization. For now, we'll simply add a label to the link to indicate its type and maintain the dashed line with open arrow appearance for each.

Feature Initializers

When you create a new element on a diagram, there is typically a domain element created or modified as a result. In some cases, it's necessary to provide additional initialization information to ensure that objects are properly created. For example, the links we create between topics in our mindmap diagram come in three flavors: dependency, includes, and extends. The 'type' attribute of the Relationship class is used to hold the RelationshipType enum value for the new instance. In our graphical definition, we will create a figure and corresponding link for each type, along with a creation tool for each in our tooling definition. We'll then use a feature sequence initilizer in our mapping definition to properly initialize our domain objects, depending on the type of link created on the diagram.

Tool links.png

Another initialization we will take care of is to set the 'label' attribute of the Relationship. As we've indicate above, this will serve to distinguish between the types as our visualization will remain the same for each.

First, return to our tooling model and rename the 'Relationship' tool to 'Dependency'. Copy/paste this tool in order to create additional 'Include' and 'Extend' tools. Similarly, in the mapping model, first change the tool used by our current Relationship Link Mapping to use the 'Dependency' tool. Then, copy/paste this Link Mapping and change the tool to use 'Include' and 'Extend'. We now have a single link in our graphical definition being used by 3 tools with corresponding mappings to the same domain element. Now, we need to initialize them properly to the appropriate type and label.


Feature init.png

In the mapping model, below your dependency Link Mapping, create a 'Feature Seq Initializer' element. This will hold subsequent 'Feature Value Spec' elements as seen in the figure. OCL is the language currently supported, so be careful that the body expressions you enter are valid. In the case of initializing the enumeration field, you'll enter 'RelationshipType::DEPENDENCY' to set the 'EAttribute type' feature of our Relationship class. In the case of initilizing the 'EAttribute label' feature, you'll enter the string value 'depend' (within single quotes).

Copy/paste your Feature Seq Initializer to your include and extend Link Mappings and change their properties as necessary (e.g. 'RelationshipType::INCLUDES' and 'RelationshipType::EXTENDS' with corresponding 'includes' and 'extends' body values)

Tip : Keep in mind that the order of the 'Feature Value Spec' elements will determine the order in which they are executed.


With these steps complete, we can regenerate our mindmap.gmfgen and code. When the diagram code is generated, below is what willl be generated within the Initializers inner class of MindmapElementTypes:

public static final ObjectInitializer Relationship_3003 = new ObjectInitializer(
	new FeatureInitializer[] {
			new FeatureInitializer(
					"RelationshipType::DEPENDENCY", //$NON-NLS-1$
					MindmapPackage.eINSTANCE.getRelationship(),
					MindmapPackage.eINSTANCE.getRelationship_Type()),

			new FeatureInitializer(
					"'depends'", //$NON-NLS-1$
					MindmapPackage.eINSTANCE.getRelationship(),
					MindmapPackage.eINSTANCE
							.getRelationship_Label())

	});

During link creation, the following code is executed in CreateIncomingRelationship3XXXCommand, found in the TopicItemSemanticEditPolicy class:

protected EObject doDefaultElementCreation() {
	Relationship newElement = (Relationship) super
			.doDefaultElementCreation();
	if (newElement != null) {
		newElement.setTarget((Topic) getTarget());
		newElement.setSource((Topic) getSource());
		MindmapElementTypes.Initializers.Relationship_3003
				.init(newElement);
	}
	return newElement;
}

This generated code within FeatureInitializer will ultimately be called on each value spec you've added, which as you can see constructs an OCL query for evaluation and uses the result to initialize the field you selected.

void init(EObject contextInstance) {
	if (this.query == null) {
		this.query = QueryFactory.eINSTANCE.createQuery(
				expressionBody, contextClass);
	}
	Object value = query.evaluate(contextInstance);
	if (sFeature.getEType() instanceof EEnum
			&& value instanceof EEnumLiteral) {
		value = ((EEnumLiteral) value).getInstance();
	} else if (value != null && sFeature.isMany()) {
		value = new BasicEList((Collection) value);
	}
	contextInstance.eSet(sFeature, value);
}


Runtime init.png

If you launch your runtime instance and test these new initializers, you will find that the type attribute is set according to the Relationship tool selected, and that the label attribute is preset to the names you defined above.


Validation

As we saw with the OCL constraint added in the first part of the tutorial, it is possible to restrict connections made between nodes by declaring constraints in our mapping definition. Sometimes, it is more appropriate to validate connections and other aspects of a diagram content using batch or even "live" validation using the Validation framework provided by the EMF Technology project. In this section, we will add such a validation feature to our mindmap in order to alert us of cyclic dependencies that have been created between Topics.

Audit rule.png

To begin, open the mapping definition (mindmap.gmfmap) and right-click the Mapping node. Select 'Audit Container' and give it a name (e.g. Mindmap Audits). Assign it an id and description as well. To the container, add a new 'Audit Rule' named 'Cyclic relationship check'. We are going to target the Map class for the audit, so add a child 'Domain Element' to the Audit Rule and select 'EClass Map' as the Element. Add a new child 'Constraint' to the Audit Rule and enter the following for the Body, leaving the Language set to ocl.

Tip : When authoring these constraint expressions in OCL, you may find it helpful to contribute an action to open the OCL Interpreter view on instances of your domain model. See the OCL Interpreter Example in the online documentation for more information.


self.relations->forAll(r1, r2 |  r1.target = r2.source and r1.type = r2.type 
     implies r2.target <> r1.source)

This will only detect cycles that exist between two directly linked Topic elements, but is sufficient for our purposes here. If someone more OCL-savvy can provide a statement to detect cycles between more than two Topics (if possible), it would be appreciated ;).

Validation extensions.png

After reproducing the mindmap.gmfgen model, you will need to set the 'Validation Enabled' property of the Gen Diagram element to 'true' in order for the new audit to be run. Just below that property is one called 'Validation Provider Priority' that you should set to 'Medium' (something higher than 'Lowest'). Do this and regenerate your editor code. After doing so, you will notice some new extensions listed in your editor's plugin.xml file. Primarily, you should notice the constraintProviders and constraintBindings extension-points to which your editor contributes. Examine these and take a closer look at the EMF Validation framework if you wish.

An improvement here would be to write validations that identify the offensive element to allow for selection via the problems view. Currently, violations result in the canvas itself being selected, as the context is the Map and not a particular Topic or Relationship.


Audit violation.png

To test the new audit, launch your runtime workspace and create a dependency link between two Topic elements. Then, from the Diagram menu, select 'Validate' and observe an error in the Problems view, as shown here. To enable/disable the audit, you can find it now listed in the preferences dialog under 'Validation Constraints'.


Shortcuts

In order to share content between diagrams, and indeed between domain model instances, GMF provides the capability to allow shortcuts to be added to diagrams. In order to enable this functionality, you'll need to locate the 'Shortcuts Provided For' and 'Contains Shortcuts To' properties of the Gen Diagram root of your generator model (e.g. mindmap.gmfgen). In order to allow the shortcutting of Mindmap elements, set both of these properties to 'mindmap' and regenerate your editor.

Shortcut element.png

Test the shortcut capability by creating two new Mindmap diagrams and adding elements to each. On one, right-click and select 'Create Shortcut...' to bring up a selection dialog. Browse to the other diagram and select a Topic node to add as a shortcut.


Summary

In this section of the tutorial, we saw how to add compartments (including nested nodes), links representing classes, feature initializers, constraints, validation, and shortcuts. The next section of the tutorial will dig deeper and focus on altering generated output and manual extension of the editor.

Back to the top