Jump to: navigation, search

Graphical Modeling Framework/Tutorial/Part 2

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 ;)

Tip : Be careful 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. Editing models open in the same resource set will help, so you might consider making changes to your graphical and tooling models from within the open mapping model.


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

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). Rename its child Label to StickyNoteNameFigure.

We'll need to create a child Label on our new Rounded Rectangle named TopicNameFigure, similar to the original Rectangle. With that, change the Diagram Label TopicName to use this new Label TopicNameFigure as its Figure. Then, just change the Figure property on your Node Topic to use the Rounded Rectangle RoundedTopicFigure.

Create a new Node on the Canvas to use your StickyNoteFigure and name it Thread, along with a new Diagram Label named ThreadName that uses your StickyNoteNameFigure. Select 'true' for the Element Icon property of the label.

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. Select 'true' for its Collapsible property and 'false' for its Needs Title 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 ThreadItemLabelFigure. Create a Diagram Label to the Canvas named ThreadItemLabel and assign the ThreadItemLabelFigure as its Figure property.

In summary, you should now have two nodes, one connection, two compartments, and three diagram labels as shown in the figure. These correspond to the Rectangles, child Labels, and our Polyline Connection in the Figure Gallery.

Note : 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. To test drive this editor, download and install the "Experimental SDK" from the download site and provide feedback on the newsgroup.


Tooling Definition

Thread tool.png

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 Topic Node Mapping and select ThreadCompartment for its Compartment property. Also, add a Child Reference to the Node Mapping. In the properties, select 'EReference comments' for its Containment Feature and Compartment Mapping <ThreadCompartment> for the Compartment. Leave the rest of the properties blank.

Note : If you're wondering about the difference between 'Children Feature' and 'Containment Feature' the idea is this (as described in the newsgroup): the 'Containment Feature' refers to where children are stored, while the 'Children Feature' refers to where to take them from. These are usually the same, and there is no need to set Children Feature if Containment Feature is set as it will default to this value. An example of when you'd need to specify this property is with EClass and EAttribute where eStructuralFeatures is the 'containment' for both operations and attributes, but where eAttributes is 'children' for instances of EAttribute.

Right now, the node that represents our topic has a compartment with a reference to the domain element which will contain its contents. The contents of this compartment we have decided will be thread discussions, which are represented by yellow sticky notes. To represent this in our mapping, add a Node Mapping to our Child Reference. In the properties, select our Thread class for the domain Element, our Thread Node for the Diagram Node, and our Thread creation tool from the palette.

The last thing we'll need for our child thread node is a mapping to its label. To our Thread Node mapping, add a child Label Mapping. Select our ThreadName for the Diagram Label and the 'EAttribute subject' feature from our Thread class for the label's Feature property.

At this point, you can regenerate the generator model and diagram plugin to see how things look. Let's continue on and map our thread node compartment to display thread items.

Tip : In the generator model, you will find a property on the compartment that will allow you to show children as 'floating' nodes and not stacked in a list. The property is 'List Layout' and should be set to 'false' in this case.


Thread Item compartment.png

We will basically repeat the steps above to add a Compartment Mapping for our ThreadItemCompartment. Add a Child Reference for our 'EReference items' feature on our Thread class and select ThreadItemCompartment for its Compartment.

To our Child Reference, add a child Node Mapping for our ThreadItem class with corresponding ThreadItemLabel as the Diagram Node and ThreadItem tool. Finally, add a Label Mapping to the node for our ThreadItemLabel to display the 'EAttribute body' feature of our ThreadItem class.

At this point, you can regenerate the mindmap.gmfgen model and diagram plugin code. You should be able to run the diagram and produce a diagram similar to the one shown at the beginning of this section.

Link Constraints

Link2self.png

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


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. A viewlet of this section can be found here.

Graphical Definition

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'. Finally, we'd prefer to have subtopics just connected with a solid line (no arrow), so delete the Polyline Decoration TopicsSubtopicsTargetDecoration element from the Figure Gallery which was added by the wizard in part one.

Tooling Definition

Relationship tool.png

We'll need a tool to create these links, so reopen your mindmap.gmftool model. We already have one link tool for subtopics, but it's in the same tool group as our node elements. Let's create a new Tool Group under our Palette for links named 'Links' and add to it new Relationship Creation Tool. Copy/paste the TopicSubtopics tool into this new group and name it simply 'Subtopic'. Delete the old TopicSubtopics tool. Finally, rename the 'mindmap' tool group to 'Nodes'. Make both tool groups collapsible by setting their Collapsible to 'true'.

Mapping Definition

Dependency link mapping.png

In the mapping definition, due to our changes in the tooling model, you'll need to check/update your existing tool selections. Right-clicking and running validation after a close/open of the mapping model can make it easier to spot invalid tool references. Then, create a new 'Link Mapping' and fill in its properties to match what's displayed in the image.

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.

Another initialization we will perform 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. A viewlet of this section can be found here.

Graphical Definition

First, we need to add a Label to our graphical definition to use in displaying the relationship type. Add a new Label to our Figure Gallery and name it DashedLineLabelFigure. Right-click on the Canvas and add a new Diagram Label named 'RelationshipLabel' with a Figure assigned to our DashedLineLabelFigure. Set the Element Icon property to 'false'.

Tooling Definition

Tool links.png

Return to our tooling model and rename the 'Relationship' tool to 'Dependency'. Copy/paste this tool in order to create additional 'Includes' and 'Extends' tools.

Mapping Definition

In the mapping model, change the tool used by our current Relationship Link Mapping to the 'Extends' tool. After we add the initializers, we will copy/paste this Link Mapping and change the properties to be used for includes and extends links. In the end, we will have a single link in our graphical definition being used by 3 tools with corresponding mappings to the same domain element.

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::EXTENDS' 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 'extends' (within single quotes).

In addition to the initializer, we'll need to add a Constraint to the link itself. To the Link Mapping, right-click and add a new Constraint with language set to 'ocl' and a body of 'self.type = RelationshipType::EXTENDS'.

Lastly, we'll need to provide a new Label Mapping to let the 'EAttribute label' feature of our Relationship domain element be represented by our RelationshipLabel.

Copy/paste your Dependency Link Mapping to create an Include and Dependency link mapping, changing their properties as necessary (e.g. 'RelationshipType::INCLUDES' and 'RelationshipType::DEPENDENCY' with corresponding 'includes' and 'depends' body values). Be sure not to forget the tool selection for each as well.

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 'New Child... 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 Target' 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. To view familiar Eclipse decorators on our elements when audits are violated, set the 'Validation Decorators' property to 'true'. Finally, 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. For example, a simple contraint added to our Topic elements would be to verify each has a name. Add such an Audit Rule to our container where the Domain Element is our Topic class and the constraint is 'name.size() > 0'.


Audit violation.png

To test the new audits, launch your runtime workspace and create a dependency link between two Topic elements. Also, add a Topic and give it no name. Then, from the Diagram menu, select 'Validate' and observe the errors 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.

Note: this bug will prevent creating shortcuts for xsd-based mindmap editors in 1.0, and is slated to be fixed in 1.0.1.

Shortcut element.png

Test the shortcut capability by creating two new Mindmap diagrams and adding elements to each. On the background of 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.

There is understandably no viewlet for this section.


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, along with adding some polish. See GMF Tutorial Part 3.