Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Graphical Modeling Framework/Tutorial/Part 2

< Graphical Modeling Framework‎ | Tutorial
Revision as of 22:21, 6 March 2006 by Rgronback (Talk | contribs) (Another Connection)

Warning: this section of the tutorial is undergoing an update to 1.0M5 of GMF

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, nested child nodes, and manual extension of generated features will be covered.


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.

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=280 (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 ThreadNodes within a compartment of TopicNodes, 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 also mentioned we'd like to have Thread items have their own compartment to contain a list of thread items for the subject. While we're at it, we'll simply create another compartment for our ThreadNode named ThreadItemCompartment which will use StickyNoteFigure as its Figure. Similarly, add a new Label to the Figure Gallery for our ThreadItems named ThreadItemFigure. Create a corresponding Node on the Canvas named ThreadItemNode and link it to the ThreadItemFigure.

Now, about the labels we'll need. 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 RoundedRectangleFigure, making it a child element. Add a similar Label as a child of your StickyNoteFigure named NoteNameFigure. 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.

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.

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 'DashedLine' as its 'Figure'.

Dependency link mapping.png

In the mapping definition, create a new 'Link Mapping' and fill in its properties to match what's here:

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 'Label Display Feature' and 'Label Edit Feature' are both mapped to the 'EAttribute label' feature of the Relationship class. 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 Meta Feature' maps to the 'EReference source' domain model element. And of course, we have our straightforward tool mapping.


Now, we can regenerate our diagram code as before, launch our diagram workspace and test this new link. Below is an example of the results:

Dependency link.png

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 possible initialization is to set the 'label' attribute of the Relationship as well, if the appearance of the link is not enough to distinguish between the types.

Graph links.png

First, create a three distinct polyline connections with properties and decorations as you see fit. For each add a connection for each of the new Dependency, Includes, and Extends links as shown in the figure. For each connection, create a Creation Tool in your mindmap.gmftool model.

Tip : Don't forget that you can use copy/paste to duplicate elements in your models. This will come in handy as you create three links, connections, tools, and mappings.


Feature init.png


In the mapping model, for each of your Link Mappings, 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' while in the case of initilizing the label attribute, you'll enter the string value within single quotes. 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_3004
				.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 select 'EClass Map' and set the remaining properties in accordance with the figure. Add a new '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. 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.



Manual Extension

Summary

Back to the top