Documente Academic
Documente Profesional
Documente Cultură
Eclipse Development
nt
using the Graphical Editing Framework
and the Eclipse Modeling Framework
Code examples
Bill Moore
David Dean
Anna Gerber
Gunnar Wagenknecht
Philippe Vanderheyden
ibm.com/redbooks
International Technical Support Organization
February 2004
SG24-6302-00
Note: Before using this information and the product it supports, read the information in
“Notices” on page vii.
This edition applies to Version: 2.1.1 of the Eclipse Platform, Version 1.1.0 of the Eclipse
Modeling Framework (EMF), and Version 2.1.1 of the Graphical Editing Framework (GEF) on
Microsoft Windows.
Notices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
Trademarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viii
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
The team that wrote this redbook. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Become a published author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii
Comments welcome. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii
iv Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Chapter 4. GEF examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.1 Additional concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
4.1.1 RootEditParts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
4.1.2 Coordinate systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.1.3 Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.2 Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.2.1 Drag and drop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.2.2 Palette: Implementing a sticky tool preference . . . . . . . . . . . . . . . . 144
4.2.3 Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
4.2.4 Zooming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
4.2.5 Decorating connections. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
4.2.6 Resource management. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.2.7 Feedback techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.2.8 Palette-less applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
4.2.9 Using direct edit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.2.10 Accessibility. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Contents v
7.2.4 Factories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
7.2.5 Policies and commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
7.3 The model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
7.3.1 Modifying the WorkflowModel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
7.3.2 Modifying the code generated from the model . . . . . . . . . . . . . . . . 216
7.3.3 Respecting model constraints in the editor . . . . . . . . . . . . . . . . . . . 216
7.4 Implementing the multi-page editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
7.4.1 Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
7.4.2 Sharing an EditDomain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
7.4.3 The editor’s dirty state. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
7.4.4 Actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
7.4.5 Support for the properties view . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
7.4.6 The outline view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
7.4.7 The palette . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
vi Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Notices
This information was developed for products and services offered in the U.S.A.
IBM may not offer the products, services, or features discussed in this document in other countries. Consult
your local IBM representative for information on the products and services currently available in your area.
Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM
product, program, or service may be used. Any functionally equivalent product, program, or service that
does not infringe any IBM intellectual property right may be used instead. However, it is the user's
responsibility to evaluate and verify the operation of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter described in this document.
The furnishing of this document does not give you any license to these patents. You can send license
inquiries, in writing, to:
IBM Director of Licensing, IBM Corporation, North Castle Drive Armonk, NY 10504-1785 U.S.A.
The following paragraph does not apply to the United Kingdom or any other country where such provisions
are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES
THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer
of express or implied warranties in certain transactions, therefore, this statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically made
to the information herein; these changes will be incorporated in new editions of the publication. IBM may
make improvements and/or changes in the product(s) and/or the program(s) described in this publication at
any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in any
manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of the
materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without
incurring any obligation to you.
Information concerning non-IBM products was obtained from the suppliers of those products, their published
announcements or other publicly available sources. IBM has not tested those products and cannot confirm
the accuracy of performance, compatibility or any other claims related to non-IBM products. Questions on
the capabilities of non-IBM products should be addressed to the suppliers of those products.
This information contains examples of data and reports used in daily business operations. To illustrate them
as completely as possible, the examples include the names of individuals, companies, brands, and products.
All of these names are fictitious and any similarity to the names and addresses used by an actual business
enterprise is entirely coincidental.
COPYRIGHT LICENSE:
This information contains sample application programs in source language, which illustrates programming
techniques on various operating platforms. You may copy, modify, and distribute these sample programs in
any form without payment to IBM, for the purposes of developing, using, marketing or distributing application
programs conforming to the application programming interface for the operating platform for which the
sample programs are written. These examples have not been thoroughly tested under all conditions. IBM,
therefore, cannot guarantee or imply reliability, serviceability, or function of these programs. You may copy,
modify, and distribute these sample programs in any form without payment to IBM for the purposes of
developing, using, marketing, or distributing application programs conforming to IBM's application
programming interfaces.
Microsoft, Windows, and the Windows logo are trademarks of Microsoft Corporation in the United States,
other countries, or both.
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.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Other company, product, and service names may be trademarks or service marks of others.
viii Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Preface
This redbook is written for developers who use the Eclipse SDK to develop
plug-in code. It is intended for a technical readership and for developers who
already have good knowledge and experience in Eclipse plug-in development.
We expect that you understand the concepts of Eclipse views and editors, and
have some familiarity with Draw2D.
In this redbook, we examine two frameworks that are developed by the Eclipse
Tools Project for use with the Eclipse Platform:
The Graphical Editing Framework (GEF)
The Eclipse Modeling Framework (EMF)
Important: This redbook covers both the Graphical Editing Framework and
the Eclipse Modeling Framework, but readers should remember that these
frameworks can be used separately and there is no dependency between the
two frameworks. We do write about using GEF and EMF together, but please
remember that this is not required, and many applications you develop will not
require both GEF and EMF.
x Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Authors: Gunnar Wagenknecht, Anna Gerber, Philippe Vanderheyden
Preface xi
Become a published author
Join us for a two- to six-week residency program! Help write an IBM Redbook
dealing with specific products or solutions, while getting hands-on experience
with leading-edge technologies. You'll team with IBM technical professionals,
Business Partners and/or customers.
Your efforts will help increase product acceptance and customer satisfaction. As
a bonus, you'll develop a network of contacts in IBM development labs, and
increase your productivity and marketability.
Find out more about the residency program, browse the residency index, and
apply online at:
ibm.com/redbooks/residencies.html
Comments welcome
Your comments are important to us!
xii Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Part 1
The Eclipse Modeling Framework is part of the Model Driven Architecture (MDA).
It is the current implementation of a portion of the MDA in the Eclipse family
tools. The idea behind MDA it is to be able to develop and manage the whole
application life cycle by putting the focus to the model. The model itself is
described in a meta-model. Then, by using mappings, the model is used to
generate software artefacts, which will implement the real system.
1.1.2 Objectives
In this section we explain the main purpose of EMF and what it can currently be
used for.
4 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
When not to use EMF
At the moment, EMF implements a subset of the MDA approach. As such,
it does not contain all the mappings we would need to make and deploy an
application at a company wide level, where XML, EAI, EJBs, Web services, and
other technologies have to be combined.
Note: You should send your questions to the newsgroup rather than to the
mailing list.
1.2.1 Prerequisites
When we wrote this redbook, the current version of EMF was v1.1.0. A valid
Eclipse product installation is a prerequisite to use EMF. As of EMF v1.0.2,
Eclipse v2.1 is required. For the purpose of writing the redbook, Eclipse v2.1 and
EMF v1.1.0 have been used.
EMF is packaged in three parts: the first one is the runtime, the second contains
the documentation, and the third contains the source code.
Note: If Eclipse was running, while doing EMF and document installation,
Eclipse will need to be restarted for changes to take effect.
6 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 1-1 Welcome page window
2. Select the Eclipse Modeling Framework welcome page from the list and click
OK. Figure 1-2 shows the EMF welcome page that will be displayed.
8 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
1.3 Building a simple model
In this section, we build a simple but realistic model. The purpose is to
demonstrate the main steps of the process. Later in our redbook, we use the
Graphical Editing Framework (GEF), to build a workflow application on top of this
model. The workflow editor will help us to create and visualize the content of the
model. For more information, the application requirements and design can be
found in “Sample application requirements” on page 188.
The model
Before starting to describe the modelling process using Eclipse and EMF, we
need to understand the complete underlying UML model that we will build. This is
shown in Figure 1-4 and discussed in more detail in “The workflow model” on
page 192.
WorkflowElement
id [1..1]: EString
name: EString
comment: EString
x: EInt
y: EInt
width: EInt
height: EInt
0..* Subworkflow
Edges
Edge Task Transformation Choice
Edges
Edges Node 1 transformationExpression: EString
0..*
0..* Port
1 1..*
Target 1..* Outputs
1 Inputs Source
InputPort OutputPort CompoundTask
LoopTask
ConditionalOutputPort FaultPort
whileCondition [1..1]: EString
conditionalOutput [1..1]: EString
plug-in installation
Omondo’s EclipseUML plug-in can be downloaded from the site:
http://www.eclipseuml.com
Install the product in the same folder you installed the Eclipse product.
Note: In our case, we did not install the versions of GEF and EMF that are
provided with the EclipseUML plug-in, because we wanted to use the latest
versions of GEF and EMF.
10 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
b. Enter a project name, for example, WorkflowModel, and click Next.
c. Select Create a Java project, and click Next.
d. Select Create a blank plug-in project, and click Finish.
2. Create a Java Package:
a. Click File -> New -> Other..., select Java -> Package, and click Next.
b. Click Browse... to select the src folder in the WorkflowModel project.
c. Enter a package name, for example, com.ibm.itso.sal330r.workflow,
and click Finish. Figure 1-5 shows a view of the Eclipse workbench after
we have completed our initial setup tasks.
Note: Our project must be a plug-in project, but it also needs to be a Java
project, in order to allow package creation. If we had selected Create a
simple project, package creation would not have been possible. Creating an
EMF Project directly is another way to achieve the same result.
Two files have been created: Workflow.ecd, which contains the class diagram;
and workflow.ecore, which contains the core model definition.
12 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Notes:
1. In the Eclipse,new EMF class diagram dialog, the package name in the
advanced section corresponds to the EMF EPackage.
2. With the EclipseUML plug-in, two types of diagram can be created: UML
and EMF models. The file for UML extension is .u?? (ex: Workflow.ucd)
while the extension for EMF is .e?? (ex: Workflow.ecd). Available
modeling operations and data types are adapted to the type of file you are
working in. Remember that if you work with an EMF model, only .e?? files
and the associated editors give you access to EMF functionality.
Interface design
We first create the root interface, which is called WorkflowElement, then we
implement the WorkflowNode hierarchy, the Port hierarchy, and finally Workflow,
Edge, and Comment.
At any point, if you realize that something is wrong or that you have forgotten
something, do not worry; most of the time, you do not have to delete your model
and start again. Simple corrections can be made in the property view, and more
complex corrections can be made using either the Sample Ecore Model editor or
the default text editor. These give you different ways of accessing the underlying
model, and allow you to correct, enhance, or even totally redefine the model.
Available editors
To open the Sample Ecore Model or the text editor:
Select the Workflow.ecore file, right-click and either choose Open With ->
Sample Ecore Model Editor or Open With -> Text Editor. See Figure 1-8
for an example of using the Ecore Model Editor.
14 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 1-8 EMF Class Diagram and Sample Ecore Model Editor together
Note: The Workflow.ecd file cannot be open in the EMF Class Diagram editor
and the text editor at the same time. To chose the editor to open the file in,
select the file, right-click Open With -> EMF Class Diagram Editor, or Open
With -> Text Editor.
Figure 1-9 The property view with the ID attribute set to true
16 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Table 1-1 WorkflowElement attribute properties
Name Type Features and properties
id EString volatile="true"
lowerBound="1"
iD="true"
name EString
comment EString
x EInt defaultValueLiteral="0"
y EInt defaultValueLiteral="0"
We repeat the same process to create the other classes of the Workflow model.
See Table 1-2 for a summary of their attributes, features, and properties. For the
classes which are not in the table, but are in the model, depicted in Figure 1-4 on
page 9, simply create them with no attribute. Do not forget that WorkflowElement
and WorkflowNode are two abstract classes.
Note: The Default Value Literal can only be set in the property editor.
WorkflowNode (abstract)
Transformation
transformExpression EString
LoopTask
ConditionalOutputPort
WorkflowElement
id [1..1]: EString
name: EString
comment: EString
x: EInt
y: EInt
width: EInt
height: EInt
WorkflowNode
Comment Workflow
isFinish [1..1]: EBoolean
isStart [1..1]: EBoolean
LoopTask
ConditionalOutputPort FaultPort
whileCondition [1..1]: EString
conditionalOutput [1..1]: EString
Generalization definition
Generalization or inheritance links are made using the generalization tool. Select
the tool by clicking its icon, which is an arrow with a big triangle at the end. Click
the specialized interface, hold the mouse button down, then move to the
generalized interface or connect to a generalization link going to the superclass.
Figure 1-11 shows our model with the generalization relationships added.
18 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
WorkflowElement
id [1..1]: EString
name: EString
comment: EString
x: EInt
y: EInt
width: EInt
height: EInt
WorkflowNode
Comment Workflow
isFinish [1..1]: EBoolean
isStart [1..1]: EBoolean
LoopTask
ConditionalOutputPort FaultPort
whileCondition [1..1]: EString
conditionalOutput [1..1]: EString
Association definition
Using the association tool, we set up the associations between the classes. We
show how to set up the association between Workflow and Edge, then we provide
a summary of all the other associations with their features; see Table 1-3.
Each association has two endpoints. So far, we have defined the characteristics
of the Workflow to Edge association, now we complete the opposite association
end, which is called Workflow.
We do the same for all the associations in the model. Any mistake can be
corrected later, by simple double-clicking the link itself in the editor. Table 1-3
shows the associations that you should create.
workflow lowerBound="1"
workflow lowerBound="1"
20 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Origin End Association end Attributes
workflow lowerBound="1"
node lowerBound="1"
node lowerBound="1"
source lowerBound="1"
target lowerBound="1"
WorkflowElement
id [1..1]: EString
name: EString
comment: EString
x: EInt
y: EInt
width: EInt
height: EInt
0..* Subworkflow
Edges
Edge Task Transformation Choice
Edges
Edges Node 1 transformationExpression: EString
0..*
0..* Port
1 1..*
Target 1..* Outputs
1 Inputs Source
InputPort OutputPort CompoundTask
LoopTask
ConditionalOutputPort FaultPort
whileCondition [1..1]: EString
conditionalOutput [1..1]: EString
Interface definition
The abstract=”true” attribute is used for WorkflowElement and WorkflowNode.
Example 1-1 shows the @model tag for the WorkflowNode. All the other interfaces
use the standard @model tag to enhance the model.
22 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 1-1 The WorkflowNode interface@model tag
package com.ibm.itso.sal330r.workflow;
import org.eclipse.emf.ecore.EObject;
/**
* @author Vanderheyden
*
* @model abstract="true"
*/
Adding attributes
An attribute is not added directly to the interface, instead, we have to define an
accessor for it. Code generation completes the interface by defining the setter
and provides the implementation of both the setter and the getter. Example 1-2
shows the x attribute @model tag.
Adding associations
For each reference, we have to define:
The type of object it gives access to.
If it is a containment reference.
The name of the second association endpoint.
If it is required or not.
See Example 1-3
import org.eclipse.emf.common.util.EList;
/**
* @model abstract="true"
/**
* @model type="com.ibm.itso.sal330r.workflow.OutputPort" opposite="node"
containment="true" required="true"
*/
EList getOutputs();
Note: The complete rebuild of the model using the Java annotation
mechanism is a very long process, and there is no real added value in
providing complete instructions in the context of our redbook.
Here is a short summary of what has to be done, for those who want to do it:
1. Create an EMF project.
2. Create a Java package.
3. Create a Java interface for all the model objects.
4. Add a getter method for each attribute.
5. Add a method for each association which is navigable. Two methods are
added for navigation navigable from both ends.
6. Create an EMF model inside the EMF project, by using the Java annotation
mechanism.
24 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Table 1-4 EMF features for an attribute
EMF feature Description
Transient The object referenced through this association will not get
persisted.
Changeable If true, the value of the attribute is not hard coded, fixed.
Containment If true, it means that any object, called the containment, which
is referenced by this one, called the container, are considered
as being part of it.
26 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
5. Choose Workflow package selection, and click Finish.
These are the steps to create an EMF model from Java interface annotations:
1. Create an EMF Model:
Click File -> New -> Other..., choose Eclipse Modeling Framework and
EMF Models and click Next.
2. Choose the project and the package you want to contain the generator model
resource. Define a file name for the model, for example, Workflow.genmodel,
and click Next.
3. Select load from annotated Java and click Next.
4. Choose the package selection, and click Finish.
1.3.10 Conclusion
We have demonstrated how to create an EMF model, which can be used directly
as the model for our application. For more information on the Object, View, and
Interaction Diagram (OVID) vocabulary used, see:
http://www-3.ibm.com/ibm/easy/eou_ext.nsf/Publish/589
Accordingly, our model contains all the model objects that we need and all the
object relationships and navigation paths to easily move from one object to the
next. The model needs to be enhanced with some convenience methods, for
example, the connectTo() method in the Workflow object, that will even
encapsulate more of the model specifics and give a higher level model entry
point.
28 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
2
Note: The sample code we describe in this chapter is available as part of the
redbook additional material. See Appendix A, “Additional material” on
page 225 for details on how to obtain and work with the additional material.
The sample code for this chapter is provided as Eclipse projects that can be
imported into your Eclipse workbench. Each major section of this chapter has
a matching Eclipse project in the additional material. The projects are
cumulative and they also depend on your having completed the modelling and
code generation described in Chapter 1, “Introduction to EMF” on page 3. You
will need to make sure that you have created the Java build path variables
described in 1.3.9, “Compiling the code” on page 27, otherwise you may get
classpath errors when importing the sample projects.
Note: For a handy overview of the Ecore model concepts, consult the
JavaDoc for the org.eclipse.emf.ecore package. Aside from the APIs for each
model object, you will also find a class diagram of the Ecore model as well as
a list of the EMF Datatypes and their corresponding Java types.
30 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Task
1 Task Task 1
Port
0..*
Edge 0..*
Edges Edges
In our model, Tasks represent units of work, and Edges represent the
connections (flows of control and data) between them. Each Edge flows from an
OutputPort on a Task to an InputPort on another Task, indicating that data
resulting from the completion of the source’s Task becomes the input of the
target’s Task. We have used the multiplicity of the references from Task to
InputPort and OutputPort, to express the constraint that each Task must have at
least one InputPort and at least one OutputPort.
Tip: If you are using the model to drive code generation, we suggest that you
follow Java conventions for naming model elements:
Heed Java case conventions:
– Use lower case for package names.
– Use lower case for the initial letter of feature and operation names.
– Begin class names with an upper case letter.
Use the plural form for names of multi-valued features and the singular
form for single-valued features.
Although we have shown associations in the Class Diagram in Figure 2-1, the
Ecore model does not represent associations explicitly. Instead, we use an
EReference to represent each navigable end of an association. An association
that is navigable in both directions is represented by two EReferences, one on
each associated class, with eOpposites that refer to each other. For example, the
association between Edge and InputPort is navigable from both ends, and so we
see the edges EReference in InputPort and the target EReference in Edge.
It is important to make sure that the eOpposites of a pair of corresponding
EReferences match, and that both EReferences have their eOpposite set.
32 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
An association that is navigable in one direction only is represented as a single
EReference, with no eOpposite. The multiplicity of the association ends is
represented by the upperBound and lowerBound attributes on the eReferences
elements representing each EReference.
As we can see from our example, associations that represent containment, such
as the associations between Task and Ports, are represented by an EReference
where containment is true, on the containing class. The containment of the
InputPorts and OutputPorts within Tasks is represented by the inputs and
outputs eReferences inside the Task eClassifiers element.
If you wish to have the flexibility of choosing whether or not to contain instance
objects in the top-level container, make sure that any references back to the
container have a lowerBound of zero.
Figure 2-2 shows the model with the additional Workflow class.
Port
Outputs
1..* 1..* Inputs
Source OutputPort InputPort
1 1 Target
Edges 0..*
0..*
Edges
Edge
0..*
Edges
Example 2-2 shows the XMI fragment that represents the Workflow class. The
eClassifiers element is added to the contents of the workflow EPackage.
References to the Workflow are also added to Task and Edge as eReferences
elements within the eClassifiers representing each class, for example:
<eReferences name="workflow" eType="#//Workflow" lowerBound="1"
eOpposite="#//Workflow/edges"/>
When we start adding detail to the classes that we use to model workflow, we
notice that many of the elements share common features, such as name. This is
often the case when modelling, and it is usual to create a common supertype that
represents an abstraction of all objects in the model, and which provides these
common features. When you are using such a model, you have the benefit of
knowing that all objects in the model are of that type, which can be useful when
34 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
you are working with the objects reflectively. For EMF models, this is less of an
issue, as all model elements already have a common supertype, EObject, and a
rich reflective API is provided to allow you to work with your model objects in this
way.
Figure 2-3 shows the model with the added WorkflowElement class.
WorkflowElement
name: EString
1 Tasks 0..*
Workflow Task
Workflow
1
Workflow Task 1 Task
1
Port
Outputs Inputs
1..* 1..*
OutputPort InputPort
Source Target
1 1
Edges Edges
0..* 0..*
Edge
Edges
0..*
Typically packages are used to group related concepts into reusable modules.
When creating an editor for a model, it is often necessary to store additional
information about model objects, such as layout information or display
properties. For the sample application described in Chapter 7, “Implementing the
sample” on page 203, we add this information directly to the WorkflowModel;
however, another approach is to use a separate package to represent the
information about each diagram.
targetNode
DiagramNode
0..1
x: EInt
y: EInt Children
width: EInt 0..*
SourceNode height: EInt
1
The following examples illustrate two ways of using our DiagramModel and
WorkflowModel together:
We construct a new package WorkflowDiagram, which merges concepts from
the two packages using inheritance.
We store the diagram information separately from the workflows, using
references between DiagramNode and DiagramConnection and the
appropriate classes from the WorkflowModel to maintain the relationship
between the two models.
36 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="WorkflowDiagram"
nsURI="http://www.redbooks.ibm.com/sal330r/example/workflowdiagram">
<eClassifiers xsi:type="ecore:EClass" name="WorkflowDiagramTask"
eSuperTypes="workflowWithSupertype.ecore#//Task
Diagram.ecore#//DiagramNode"/>
<eClassifiers xsi:type="ecore:EClass" name="WorkflowDiagramWorkflow"
eSuperTypes="workflowWithSupertype.ecore#//Workflow
Diagram.ecore#//DiagramNode"/>
<eClassifiers xsi:type="ecore:EClass" name="WorkflowDiagramEdge"
eSuperTypes="workflowWithSupertype.ecore#//Edge
Diagram.ecore#//DiagramNode"/>
</ecore:EPackage>
In the second approach, the diagram and the workflow are more loosely coupled.
We add references to the classes in the DiagramModel to represent the linkage
between the two models, as shown in Example 2-4.
Notice that because we are referencing classes from another package, we have
to be explicit about the type. For example, we refer to the Task class as follows:
<eReferences name="model"
eType="ecore:EClass WorkflowWithCommonSupertype.ecore#//Task"/>
Notice also that the references are one-way references, as we do not wish to
pollute the WorkflowModel with references to the DiagramModel.
The Ecore model also allows us to define nested packages, which are
represented in the XMI as eSubpackages elements. For example, we could
package the DiagramModel and the WorkflowModel together as sub-packages
of a new package NestedWorkflowDiagram. Example 2-5 shows the XMI for our
NestedWorkflowDiagram package, with some details omitted for brevity. Notice
that reference strings also now include the subpackage, such as:
<eReferences name="model" eType="#//workflowsupertype/Edge"/>
38 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
<eReferences name="model" eType="#//workflowsupertype/Workflow"/>
<eReferences name="children" eType="#//diagram/DiagramNode"
upperBound="-1" containment="true"
eOpposite="#//diagram/DiagramNode/container"/>
</eClassifiers>
</eSubpackages>
</ecore:EPackage>
Tip: When using nested sub-packages, be sure that each package has a
unique nsURI.
Declaring datatypes
EMF provides datatypes such as EString and EInt, which represent the basic
Java types that you can use for simple attributes. If you need to use a different
Java type, you need to create an EDataType to represent it. For example, we use
EString to represent attributes such as condition of ConditionalOutputPort and
whileCondition for LoopTask from the WorkflowModel for the sample application.
If we wanted to represent these conditions with a specific existing Java type
instead, we would declare an EDataType corresponding to that type, as follows:
<eClassifiers xsi:type="ecore:EDataType" name="Condition"
instanceClassName="com.example.Condition"/>
Adding operations
We can augment the classes in our model by adding operations to them. Aside
from the convenience of having the signatures and skeletons generated into the
code, there is little difference between adding the operations directly to the code
as methods and adding the operations to the model. In both cases you will need
to implement the methods in the generated code. A good approach is to define
the signatures of the methods that you want to be public in your model, then
complete the generated skeletons to implement them.
UML:
Tutorial: Generating an EMF model
Specifying Package Information in Rose
40 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
<xsd:element name="shipTo" type="USAddress"/>
<xsd:element name="billTo" type="USAddress"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
<xsd:attribute name="country" type="xsd:NMTOKEN" fixed="US"/>
</xsd:complexType>
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date"
minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<!-- Stock Keeping Unit, a code for identifying products -->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
If the schema that you are importing from has a targetNamespace, then the
nsURI of the generated EPackage is set to that URI, and the name and nsPrefix
are derived from that URI. For example, if the targetNamespace is
http://www.example.com, then the nsPrefix is com.example, and the name is
example. If the targetNamespace is http://www.example.com/foo, then the
name is foo and the nsPrefix is com.example.foo.
Example 2-7 shows how the features of the EPackage created from po.xsd are
populated by the mapping. The purchase order schema did not have a
targetNamespace, so the URI to the schema file is used as the nsURI instead,
and the name of the file is used for the nsPrefix and name.
42 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 2-7 EPackage from XML Schema
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="po"
nsURI="platform:/resource/MigrateFromXMLSchema/po.xsd" nsPrefix="po">
...
</ecore:EPackage>
You may notice that the XSD plug-in generates EAnnotations for each of the
objects in the model. These annotations describe how the model maps to the
schema, and is used to serialize model instances so that they conform to the
XML Schema from which the EMF model was generated. We discuss how to
modify these annotations to control serialization in 2.3.3, “Using the XSD plug-in
to customize serialization” on page 70.
Types from the XML Schema become EClassifiers: complex types, which
represent types that contain elements or attributes, are represented by EClasses
in EMF. Example 2-8 shows the EClass mapped from the USAddress complex
type. Notice that the representation of this EClass is type, because it has been
generated from a type.
Elements of this type are mapped to EReferences within the EClass representing
the containing type. For example, the USAddress type is the type of the shipTo
element, contained within the PurchaseOrderType. Hence, as shown in
Example 2-9, shipTo is represented as an EReference within the EClass created
for PurchaseOrderType. Notice that the representation is element, because the
EReference was mapped from an element declaration in the XML Schema.
EDataTypes are used to represent simple types that represent atomic values.
For example, SKU is represented by EString in the model. For XML elements
that are of a simple type, such as Comment from the purchase order schema, an
EClass representing the element is created, and an EAttribute is used to
represent the content. Example 2-10 shows the Comment EClass. Notice that
the representation of the value attribute is simple-content, that is, it provides the
actual content of the comment element.
44 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
2.2 EMF.Edit-based editors and code generation
The Tutorial: Generating an EMF Model describes how to use the GenModel
wizard to create a GenModel for the WorkflowModel, and how to generate
plug-ins that can be used to create and edit WorkflowModel instances. In this
section, we describe the correspondences between generated plug-ins and the
model from which they are generated, by examining the code produced for the
model, edit, and editor plug-ins generated from the WorkflowModel. We then
discuss how to customize these generated plug-ins using code generation
properties.
Note: The JET framework is used to generate model, edit, and editor plug-ins
from EMF models. The templates that are used to generate these plug-ins are
located in:
<ECLIPSEHOME>/plugins/org.eclipse.emf.codegen.ecore_<EMFVERSION>
/templates
Where <ECLIPSEHOME> is the location where you installed Eclipse, and
<EMFVERSION> is the version of the EMF plug-in that you have installed.
We discuss the JET framework and how to customize code generation using
templates in 2.4, “Using JET to customize code generation” on page 79.
Packages
For each EPackage, two or three Java packages are generated. For the
WorkflowModel, these packages are workflow, workflow.impl, and workflow.util.
Notice that there may be a prefix used to generate package names, as discussed
in “Package-level GenModel properties” on page 54; however, for the purposes
of describing the generated plug-ins in this section, we will ignore it. The util
package is optional: Its presence will depend on the code generation properties.
The util package is generated when the code generation properties are set to
their defaults.
If a class has multiple supertypes, then the first supertype in the eSuperTypes list
is considered to be the primary supertype. The generated implementation for a
subclass extends the corresponding implementation class of the primary
supertype, and implements methods defined in the interfaces generated for any
other supertypes. For example, for the WorkflowDiagram model from “Working
with packages” on page 35, WorkflowDiagramTask extends TaskImpl (the
primary supertype), and implements the methods from DiagramNode.
Features
For each feature, getter and setter methods are generated in the class and
interface generated from their containing class. A field to cache the value of the
feature is also generated if the feature is not volatile. If a feature is not
changeable, then only getter methods are generated.
The reflective methods such as eSet() are generated from all features for the
containing class.
Operations
For each EOperation, a public method signature is generated in the interface of
the containing class, and a skeleton implementation is generated in the
corresponding implementation.
DataTypes
For each EEnum, an implementation is generated that extends
org.eclipse.common.util.AbstractEnumerator. For other EDataTypes, there are
no interfaces or implementations generated; instead, their instanceClass is used
directly for EAttributes of that type.
46 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The edit plug-in
ItemProviders are generated for each class in the edit plug-in in the provider
package. In addition, an EMFPluginClass is generated for the entire plug-in. The
ItemProviders extend org.eclipse.emf.edit.provider.ItemProviderAdaptor and
adapt the corresponding EObject from the model. For example,
WorkflowElementItemProvider adapts a WorkflowElement. The ItemProvider
forwards some notifications received when the model object changes via
fireNotifyChanged(), and filters the rest. You can control which notifications are
filtered when you generate the plug-in, as described in 2.2.2, “Customizing code
generation through GenModel properties” on page 47.
ItemProviders also manage property descriptors for all features of the class, as
well as an icon and description for the class, returned by the getImage() and
getText() methods.
The final class generated in the editor plug-in is a wizard, which allows you to
create a new resource containing an instance of one of your model objects.
Example 2-12 shows the top-level XMI element from the GenModel for the
WorkflowModel. As we can see, the GenModel has properties (represented in
the XMI as attributes) that identify the model from which the edit, and editor
plug-ins are generated, and that specify the name and package of the generated
plug-ins. We provide details of the effect that these properties have on code
generation in “Top-level GenModel properties” on page 52.
Nested within the top level element of the WorkflowModel’s GenModel XMI, we
find elements corresponding to each object from the WorkflowModel, with
attributes representing the properties specific to each object. The nesting of the
contents of a GenModel XMI matches the nesting within the source Ecore model,
with elements corresponding to classes, data types, and sub-packages nested
within the element corresponding to their containing package; and elements
corresponding to references, attributes, and operations nested within the
element corresponding to their containing class.
In Example 2-13, we see a fragment of the GenModel for the WorkflowModel that
corresponds to the Workflow class.The genClasses element corresponding to
the Workflow class contains genFeatures elements that correspond to the name
attribute, and to the task and edge references of the Workflow class. The effect
on code generation of the properties represented for each class is discussed in
“Class-level GenModel properties” on page 55.
2
For more information, refer to the XML Metadata Interchange (XMI) Specification, Version 2.0,
which can be found at: http://www.omg.org/technology/documents/formal/xmi.htm
48 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Aside: If you have installed the org.eclipse.emf.source plug-in, you can take a
look at the file GenModel.ecore, which describes the GenModel. The zip file
containing the file is usually installed at the following location:
<ECLIPSEHOME>/plugins/org.eclipse.emf.source_<EMFVERSION>/src/org.
eclipse.emf.codegen.ecore_<EMFVERSION>/runtime/codegen.ecoresrc.zip
While you can control the generation of plug-ins by editing the GenModel XMI
directly, you can also edit the GenModel properties with the editor provided by
the GenModel plug-in. Figure 2-6 shows the GenModel editor displaying the
top-level properties from the GenModel for the WorkflowModel. As the figure
shows, the tree view provided by the GenModel editor mirrors the containment
hierarchy from the GenModel XMI and the source model, and displays the
properties for the selected item in the Properties view. If you do not see the
properties, select Window -> Show View -> Other and then select Properties
from the Basic item in the tree.
An advantage of using the GenModel editor over editing the XMI directly is that
the properties are organized by category, as we see in Figure 2-6, where the
properties for the WorkflowModel GenModel are categorized according to
whether they relate to the generation of the model, edit, or editor plug-ins. There
is also a Templates & Merge category that is not shown in the figure, which we
discuss in 2.4, “Using JET to customize code generation” on page 79.
50 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
You can toggle between the categorized view and a flat view of the properties by
clicking the button identified by the tree icon, as shown in Figure 2-7, with the tool
tip Show Categories. Here we see the properties for the name attribute of the
class Workflow. The property view allows us to view and edit all of the properties
associated with each object in the model. The editor provides a brief description
of each of the properties, which is displayed in the status bar whenever a
property is selected, as shown for the Property Type property. For properties that
have a fixed set of values, such as Property Type here, the editor provides a
pull-down list from which you may select an alternate value. Notice that the XMI
file representing the model may not explicitly persist a property that is unchanged
from its default value, as shown in Example 2-13 on page 49, where none of the
properties in the Edit category for the name attribute are present in the XMI.
The copyrightText property provides the value for the final static field copyright in
every generated Java class in the model and edit plug-ins. By default, the
copyright field is set to be the empty string. Notice that this field is not generated
in the classes for the editor plug-in.
52 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The creationCommands property controls whether or not the generated edit
plug-in includes support for creating new objects. If creationCommands is false,
the generated editor only allows properties of existing objects to be modified, and
the menu options for creating new child or sibling objects are not present. If
creationCommands is true, in the edit plug-in, each ItemProvider generated from
each class in the model contains a method collectNewChildDescriptors, which
constructs a list of the types of children objects that can be created. These lists
are used by the editor plug-in to construct actions that can be used to create
children and sibling objects.
The modelName property is used to construct the default names of the edit, and
editor plug-ins. The values of the modelDirectory, editDirectory and
editorDirectory properties determine the projects, and path within those projects,
into which the plug-ins are generated, while the modelPluginClass,
editPluginClass and editorPluginClass properties determine the Java package of
each generated plug-in.
The modelPluginID property is used as the plug-in ID of the model plug-in and is
referenced from the edit plug-in, which depends on the model plug-in. If you
change the value of this property, you need to delete the plugin.xml files from the
model and edit plug-ins before regenerating the code, to ensure that they are
updated.
Setting the generateSchema property to true means that the XML Schema for
the model is generated whenever the model plug-in is generated. When you
generate the schemas, you will notice new schema files appear in the project;
XMI.xsd and <PackageName>XMI.xsd, where <PackageName> is the name of
the top-level package from your model. For example, the XML Schema files
generated from the WorkflowModel are XMI.xsd and WorkflowXMI.xsd. XMI.xsd
declares XMI elements and attributes that are common to all models, while
WorkflowXMI.xsd contains the only element and attribute declarations specific to
serializing WorkflowModel instances.
Tip: For the XML Schema generation to succeed, you must have installed the
XSD plug-in available from http://www.eclipse.org/xsd/.
54 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
If a value is specified for basePackage, that value is used as the package prefix
for the generated plug-ins. For example, to generate the model interfaces for our
WorkflowModel plug-in into the package com.ibm.itso.workflow, we set
basePackage for the Workflow package to com.ibm.itso and check that the
name of the Workflow package in the WorkflowModel is lower case to ensure
that we follow java package naming convention. If you generate the GenModel
from a model where the top-level package has a fully qualified Java name, the
wizard fills in the basePackage property with the prefix from that package.
The provider property indicates which item provider pattern is used to generate
the ItemProvider for this class in the editor plug-in; Singleton, Stateful, or None.
Refer to the Item provider implementation classes section, from The EMF.Edit
framework and code generator overview, in the Documents section of the EMF
project site at: http://www.eclipse.org/emf/ for details of the Singleton and
Stateful pattern. If the property is set to None, no Item Provider is generated for
the class.
The image property indicates whether an icon is generated for the class in the
corresponding ItemProvider, which is returned by the getImage() method.
The labelFeature property identifies the attribute that is used to provide the
default label for objects of this type, which is returned by the getText() method in
the generated ItemProvider. If this property is not set, then the code generation
will look for an attribute called name (or with name as a substring in its name) to
use instead, and if that does not exist, it will use the first simple attribute (that is,
an attribute that is of a simple type such as EString) from the class.
56 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The children property indicates whether this feature is considered to be a child of
the containing class, for the purposes of constructing the tree view in the editor,
and whether the context menu for the parent provides an option to create this
feature as a child. Most features are represented as properties, and so children
is usually false, however for containment references, children is true by default.
Our example customizes the code generation so that the model, edit, and editor
are generated into a single plug-in, to make it easier to package for distribution.
These are the steps that we take to generate a single plug-in called
com.ibm.itso.sal330r.workflow, for our WorkflowModel editor:
1. In our existing WorkflowModel project, we generate the GenModel for the
WorkflowModel and open it using the GenModel editor.
2. We change the modelPluginID to com.ibm.itso.sal330r.workflow, as shown
in Figure 2-8. This is the identifier that is used for the plug-in containing the
model, edit, and editor code.
3. We edit modelDirectory, editDirectory, and editorDirectory properties so that
they are all set to /com.ibm.itso.sal330r.workflow/src. When we generate
the plug-ins, the com.ibm.itso.sal330r.workflow project is created if it does not
already exist. The code for all three plug-ins is generated to the src directory
of this project, and a single plugin.xml is generated to describe the plug-in
containing the model, edit, and editor code.
4. Edit the editPluginClass and editorPluginClass properties, as shown in
Figure 2-8, so that they have the same package prefix.
58 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 2-8 Changing the top-level GenModel properties to generate a single plug-in
6. Select Generate All from the context menu of the top-level GenModel
element to generate the model, edit, and editor code into the
com.ibm.itso.sal330r.workflow plug-in. It is important to select Generate All
the first time you generate the code, rather than choosing the model, edit, or
editor options individually, so that plugin.xml contains all of the required
entries for the combined plug-in.
Tip: Be sure to generate from the context menu of the top-level element in the
GenModel. Generating from any item lower down in the tree will only generate
code associated with that item and its children.
60 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
2.2.3 Modifying the generated code
Once you have generated the code for the model, edit, and editor plug-ins, there
may still be some customizations that you need to make before you can use it.
Common additions that you may make to the model code include implementing
methods generated from EOperations, implementing getter and setter methods
for volatile features, or adding methods that were not represented in the model.
62 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
id = newId;
}
}
}
We remove the @generated tag from the comments to ensure our methods are
not overwritten. Notice that volatile attributes are quite commonly not
changeable, and are usually also transient. This means that usually you would
not need to cache the value of the attribute or provide a setter implementation. In
our case, although the ids are generated, and we don’t care what the value of the
ids are while the objects are in memory, we use them in the serialization to make
the XMI references more readable, which means that the id attribute has to be
non-transient and changeable.
You can see the result of the changes in the default WorkflowModel editor in
Figure 2-10.
64 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 2-18 shows how we create a Workflow instance and a Task instance
using the WorkflowFactory. The example also demonstrates how we use the
methods from the generated code to set properties such as the name on the
Task, and maintain references, in this case adding the Task to the nodes of the
Workflow, and adding an InputPort and OutputPort to the Task.
If we were using the reflective API to manipulate our instance objects, we would
replace methods such as setName() and getNodes() that are specific to the
WorkflowModel with generic eSet() and eGet() methods, for example:
t1.eSet(WorkflowPackage.eINSTANCE.getTask_Name(), "Task 2")
to set the name of the Task.
We can create instances of this model using the reflective API, as Example 2-20
demonstrates.
Example 2-20 Using the reflective API to create dynamic model instances
EFactory wfFactory = workflowPackage.getEFactoryInstance();
EObject task1 = wfFactory.create(taskClass);
task1.eSet(taskNameAttr, "Task 1");
Obviously, this dynamic approach only works for some applications — if you are
using a model where you would normally customize the code generated from the
model, for example, to implement EOperations, then this approach is not
suitable.
For a more complete description of XMI 2.0, please refer to the XML Metadata
Interchange (XMI) Specification, Version 2.0, found at:
http://www.omg.org/technology/documents/formal/xmi.htm
66 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Note: We have already seen several examples of XMI representing model
instances. The Ecore model is itself an EMF model, so the Ecore documents
describing the models that we created in 2.1, “EMF modeling techniques” on
page 30, were all examples of the default serialization of Ecore model
instances to XMI.
All of the example serializations discussed in this section can be found in the
Serializations directory in the examples provided for this section.
You can customize the way that references are represented in the XMI by
overriding the eURIFragmentSegment() and eObjectForURIFragmentSegment()
methods in your model implementation classes. The default positional references
are provided by these methods in EObjectImpl, which is a superclass of all the
implementation classes generated from a model.
When the references are to objects contained by another resource, then the
scheme for finding the file that is the serialization of the resource (for example,
http) and the name of the file is also added to the reference. An example of this is
when we use cross-package references and serialize the containing Ecore
EPackages into separate files, such as the following snippet taken from
Example 2-4 on page 37:
<eReferences name="model"
eType="ecore:EClass WorkflowWithCommonSupertype.ecore#//Task"/>
68 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Each model instance that is created by the generated editor plug-in is added to a
Resource, which can later be used to serialize that instance. Within any
EMF-based application, we can use an XMIResource to serialize or deserialize
instance objects. In the sample application discussed in Chapter 7,
“Implementing the sample” on page 203, we use XMIResources to contain
WorkflowModel instances. To create or get each resource, we first create a
ResourceSet, as Example 2-23 shows.
Once we have the resource, we can add objects to the contents of the resource.
Objects contained by the same resource will be serialized to the same file.
Example 2-26 shows how we create and add a Workflow object to a resource.
Whenever we create an Ecore model from an XML Schema using the XSD
plug-in, annotations that describe how each object is serialized to XML are
added to the model, so that serialized model instances conform to the source
schema. Use of these annotations is not limited to models imported from XML
Schema; we show how to use the same annotations on any Ecore model to
control how its instances are serialized.
70 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
For example, within Workflow:
– comments becomes comment
– edges becomes edge
– nodes becomes node
Change the Workflow element to be lower case, to provide consistent
capitalization throughout the document.
Example 2-28 shows the eAnnotations element we use to annotate the comment
EAttribute of WorkflowElement, to indicate that it should be represented as an
element, rather than as an attribute. This is achieved by setting the value of the
representation key to element. To force serialization as an attribute, we would
use the value attribute instead. We add similar eAnnotations to the
eAttributes elements representing transformExpression, condition and
whileCondition so that they are also represented as XML elements.
We use the same technique to ensure that a lower-case element name is used
for Workflow, however we have to be careful to make sure we specify the
targetNamespace correctly, as the workflow element is the top-level element of
our XML instance documents, so we cannot rely on XML namespace scoping for
this value. Because we have constraints in the WorkflowModel that mean that all
other objects are contained either directly or indirectly by a Workflow, we do not
have to specify targetNamespace for any other elements, however, if you are
using this technique to customize serialization for other models, make sure you
specify this value for any elements that could appear as the top-level element in
a serialized instance. Example 2-30 shows how we annotate the eClassifiers
element representing the Workflow class. The targetNamspace that we specify in
this annotation should match the nsURI of the containing package exactly.
72 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
1. Create or reload the GenModel from the annotated WorkflowModel, as
described in “Java annotation and the code generation process” on page 24.
To reload, select Reload... from the context menu that appears when you
right-click the GenModel file in the Navigator or Package Explorer view, and
then open the GenModel file.
2. We modify the GenModel so the regenerated code supports our
customizations. Refer to 2.2.2, “Customizing code generation through
GenModel properties” on page 47 for more information about setting
properties in the GenModel. Select the workflow package from the GenModel
tree and set Resource Type to XML.
3. Save the GenModel and then select Generate Model Code from the
right-click context menu of the top-level element in the GenModel. You may
wish to select Generate All instead if you do not have an up-to-date editor
generated from your model. If adding these annotations is the only change
that you have made to the model since generating the edit, and editor
plug-ins, you do not need to regenerate them.
The model plug-in now includes code that supports serializing to our custom
XML syntax. When we run our editor and create new model instances as
described in Chapter 1, “Introduction to EMF” on page 3, the object instances are
represented as elements or attributes according to the annotations that we
added to the model. If we take a look at a new instance in a text editor, such as
the instance shown in Example 2-31, and compare this to the default serialization
shown in Example 2-21, we can see evidence of the changes that we have made
to the serialization format.
You can find the completed annotated model in the WorkflowXSD folder in the
examples for this section. This example demonstrates how to control whether
model objects are serialized as XML elements or attributes, and allows us to
provide names of our choosing for those elements and attributes. There are
other annotations that are used by XSD to control feature-order and map XML
Schema types to model objects, as discussed in 2.1.2, “Migrating existing
models” on page 40, which you may also use to customize the serialization
further.
The XMI 2.0 production rules allow features to be serialized either as elements or
attributes. We can control whether each feature is serialized as an element or as
an attribute by creating an XMLMap and adding appropriate mappings, as the
following example illustrates. You can find the code for this example in the
XMLMapExample directory in the examples for this section.
74 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
We perform the following steps to customize the serialization of a type or feature
from the model:
1. Create an XMLMap to store the information about mapping the model to XML.
2. For each model object with a custom serialization:
a. Create an XMLInfo and use the setName(), setTargetNamespace() and
setXMLRepresentation() method to specify the how the object is
represented in the XML.
b. Add the XMLInfo to the XMLMap, using the object for which you want to
customize serialization as the key. We do this in the example with:
map.add(WorkflowPackage.eINSTANCE.getWorkflowElement_Comment(), x);
3. Put the XMLMap as the OPTION_XML_MAP in the options map that you use
to save the resource.
Use the setName() method on the XMLInfoImpl to customize the name of the
element or attribute tag used in the XML, and setTargetNamespace() to set the
namespace for that element or attribute.
Another change that you can make while still complying with the standard is to
modify how ids are generated in the serialization. Instead of using an attribute
with the id property set to true in the model, you may wish to generate ids for all
elements in the serialization. Although the ids are not accessible from the model,
the advantage of generating them is that you can ensure that they remain
unique. Be aware, however, that generating ids means that elements can
potentially have a different id each time they are serialized.
The ids are mapped to objects from the model by the resource, which uses a
map to map ids to each EObject. You can assign ids specifically to particular
objects before you serialize by using the setID() method on the resource, as
shown in Example 2-33.
If you want ids to be generated automatically for your objects, you can override
the getID() in your resource implementation to do this, as Example 2-34 shows.
String id = super.getID(obj);
if (id == null){
id = generateID();
setID(obj,id);
}
return id;
}
76 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Customizing XML serialization
The XML that we generated in 2.3.3, “Using the XSD plug-in to customize
serialization” on page 70, used the names of references to contained objects for
the XML elements representing those objects. A different representation would
be to use a name that identifies the type of the reference, particularly in a model
where there is usually only a single reference between objects of each type.
The mapping of element and attribute names to model objects is taken care of by
an XMLHelper. We provide our own custom XMLHelper, to override the default
names for references, replacing reference names with the name of the type
instead. Example 2-35 shows how we override this method in our XMLHelper
implementation.
Note: When there are multiple references to a type from the same object, we
have to be careful, for example in the case of CompoundTask, because it has
two references to Workflow, we have to be able to distinguish between the
two, so we might need to add additional information to the serialization to do
this. Generally you would only want to use a serialization such as this one if
the type implied the reference.
So far we have only dealt with serializing to the custom format, we would also
have to override the getFeatureWithoutMap() method to map the types back to
features, however we leave this an exercise for the reader.
The serialization would produce such elements if you added explicit references
to each class with specific names for each reference, in the same way that we
already have specific references to OutputPort and InputPort rather than a
general reference to Port.
However, then you would need to maintain all of these references separately or
lose the benefits of polymorphism, and your model would be cluttered. However,
we could implement such a serialization by providing our own subclasses of
XMLSaveImpl and XMLLoadImpl and use them within WorkflowResourceImpl,
as these are the classes that actually serialize our instances, and override
methods such as saveElement() to provide a more specific name.
These examples are provided to give you an idea of the types of things you can
customize by providing your own XMLHelper, XMLSave or XMLLoad
implementations. You may choose to override the methods from those classes to
produce any XML serialization.
78 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Other serializations
To serialize to other formats, all you need to do is to implement your own
versions of the doSave() and doLoad() methods in your ResourceImpl subclass.
For an introduction to JET in general, refer to the two-part JET Tutorial by Remko
Popma, available from Eclipse Corner, at:
http://eclipse.org/articles/Article-JET/jet_tutorial1.html
http://eclipse.org/articles/Article-JET/jet_tutorial2.html
The JET-related GenModel properties are described in Table 2-5. All of these
properties are represented at the top-level of the GenModel, and are grouped by
the GenModel editor into the Templates & Merge category. Setting these
properties allows us to override the default JET templates used to generate the
model, edit, and editor plug-ins. Descriptions of the properties are provided by
the GenModel editor in the status bar whenever you select one of the properties.
<ECLIPSEHOME>/plugins/org.eclipse.emf.codegen.ecore_<EMFVERSION>/
templates
80 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The header template provides the comment that is located at the head of each
generated class file. We begin by creating our own templates directory, and by
supplying a new Header.javajet. To do this, perform the following steps:
1. Add a directory called templates to the WorkflowModel project.
2. Create a new text file called Header.javajet in the templates directory. If you
prefer, you can copy the existing Header.javajet file as a basis for your
template.
3. Edit Header.javajet to contain the comment that is to be included at the top of
every generated class file. We edit the file to read as shown in Example 2-38:
4. Generate the GenModel for the WorkflowModel. This step may be skipped if
you already have a GenModel for the WorkflowModel.
5. Edit the GenModel properties:
a. Set the dynamicTemplates property to true.
b. Set the templateDirectory property to the location of your templates
directory, for example, /WorkflowModel/templates.
By default, the header is only generated the first time the code is generated from
your model, so if you already have a version of the model plug-in in your project,
you will need to override this behavior. The merging of existing content with new
content is handled by EMF’s jmerge. The rules for merging the model, edit, and
editor code generated from EMF models are expressed in the file
emf-merge.xml. Copy emf-merge.xml into your templates directory from the
org.eclipse.emf.ecore.codegen plug-in’s templates directory and modify the file
so that it includes an additional rule to set the header each time the code is
generated, as shown in Example 2-39.
Now when you generate the model plug-in and take a look at the generated
code, the contents of Header.javajet should appear in place of the default
header.
Tip: Whenever you modify a template, you may need to close and then
re-open the GenModel file before regenerating code so that the new version of
the template is used.
JET templates use a simplified Java Server Pages (JSP) syntax. You can get a
feel for how JET templates work by examining and modifying the templates used
to generate the interface and implementation corresponding to each class in a
model. Begin by making a copy of the templates into the WorkflowModel
project’s templates directory:
1. Create a model sub-directory within the templates directory in the
WorkflowModel project.
2. Copy the files Interface.javajet Class.javajet and from the model directory in
the org.eclipse.emf.codegen.ecore plug-in’s templates directory to the
directory created in the previous step.
At the first line in Interface.javajet, we see the tag shown in Example 2-40.
The tags used within JET templates are identified by an opening <% and a closing
%>. Inside the tags, you can use Java code to script what is generated from the
template, or you can use special tags to represent JET directives or expressions.
The jet tag shown in Example 2-40 is a directive. Expression tags are used to
create values based on expressions in the files generated from the templates.
82 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Directives start with <%@ and a name that identifies them, and expressions start
with <%=. We can see examples of each of these types of tags just a few lines
further down in Interface.javajet, as shown in Example 2-41.
The first tag shown in Example 2-41, is a scriptlet that declares and initializes
variables that can be referenced from other tags in the rest of the template. In this
case, we see genClass, which represents the class for which the interface is
being generated using this template, genPackage, which represents its containing
package, and genModel, which is the model that contains genPackage.
The second tag shown in Example 2-41 is another directive; this one indicates
that the code produced from the Header.javajet template is included at this point
in the code generated from this Interface template. The include directive has a
single attribute, file, that indicates the location of the file to be included. There
are two directives that can be used within JET templates; the include directive,
seen in this example, and the jet directive seen in Example 2-40. The jet
directive may appear only on the first line of a template, and every template must
have a jet directive. The attributes of the jet directive are described in the JET
Tutorial part one, (Introduction to JET). Note Header.javajet did not have a jet
directive, because it is just a fragment included into other templates.
The third tag from Example 2-41 is an expression tag, which in this case provides
the expression used to get the package name for the interface that is generated
using the template.
You may notice that the names of most of the types and methods that end up in
the generated code come from expression tags that call methods provided by the
GenModel. The reason for this is that the code is generated from GenModel
objects that are provided as arguments to each template. We can change the
structure or the literal content of the generated code by editing the templates,
however changing the names of the methods and types in the generated code
would require providing our own implementation of the interfaces in
org.eclipse.emf.codegen.ecore.genmodel and then providing those objects as
arguments to the templates. For our additions to the generated code, we edit the
templates only. If you would like to find out more about providing different objects
as arguments to a JET template, please refer to the JET Tutorial.
In Interface.javajet, we can see that the section of the template that generates
accessor methods for features is contained within a for-loop that iterates over the
features of the class. We are adding template to generate additional methods for
some features, so we make our additions within this loop.
Example 2-42 shows concrete examples of the method signatures that we are
adding to the generated interfaces. In this case, the methods are for the inputs
feature of the class WorkflowNode.
To generate similar methods for all multi-valued features using the templates, we
substitute types and methods specific to the inputs reference with expression
tags. We use method calls on genFeature, which represents each feature, to
provide the values. We also add @generated to indicate that these methods are
now generated. Example 2-43 shows the code that we add to Interface.javajet to
generate the extra methods in the interface for each generated type. We wrap the
template for the new methods inside conditions, to make sure that we only
generate these methods for multi-valued features, that is, features that are
regular list types.
84 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
<%=genFeature.getQualifiedListItemType()%>
<%=genFeature.getGetAccessor()%>(String name);
<%}//if%>
<%}//if%>
<%}//for%>
Again, we generalize by substituting expression tags for the parts of the method
implementations that are specific to the feature. Example 2-45 shows the code
that we add to Class.javajet. We add the method templates to the existing
for-loop that iterates over all of the implemented features. Notice that because
we introduce the class java.util.Iterator into the generated code in the second
additional method, we need to use the getImportedName method from the
GenModel to make sure it is added to the imports in the generated class.
/**
* Get an item from the list by name
* @generated
*/
public <%=genFeature.getQualifiedListItemType()%>
<%=genFeature.getGetAccessor()%>(String name){
<%=genModel.getImportedName("java.util.Iterator")%> i =
this.<%=genFeature.getGetAccessor()%>().iterator();
while (i.hasNext()) {
<%=genFeature.getQualifiedListItemType()%> l =
(<%=genFeature.getQualifiedListItemType()%>) i.next();
if (name.equals(l.getName()))
return l;
}
return null;
}
<%}//if%>
<%}//if%>
<%}//for%>
Now, when you generate the model plug-in, you should see the additional
methods in the generated interfaces and implementation classes. You may also
notice a new project .JETEmitters appear in your workspace in the resource
view. This project is created by default when the templates are translated, as
described in the JET Tutorial, Part Two, and it contains the actual
implementations of our templates.
86 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
3
After the introduction, we show you how to build a graphical editor skeleton using
our step-by-step instructions, and then explain how to map your model into GEF
edit parts.
Note: The sample code we describe in this chapter is available as part of the
redbook additional material. See Appendix A, “Additional material” on
page 225 for details on how to obtain and work with the additional material.
The sample code for this chapter is provided as Eclipse projects that can be
imported into your Eclipse workbench. Each major section of this chapter has
a matching Eclipse project in the additional material.
All graphical visualization is done via the Draw2D framework, which is a standard
2D drawing framework based on SWT from eclipse.org.
The editing possibilities of the Graphical Editing Framework allow you to build
graphical editors for nearly every model. With these editors, it is possible to do
simple modifications to your model, like changing element properties or complex
operations like changing the structure of your model in different ways at the same
time.
All these modifications to your model can be handled in a graphical editor using
very common functions like drag and drop, copy and paste, and actions invoked
from menus or toolbars.
For our demonstration code and for explanations of the GEF API, we used the
latest code releases that were available during the creation of this redbook:
Eclipse 2.1.1 and GEF 2.1.1.
Note: Only the GEF SDK is shipped with the developer documentation of GEF
and Draw2D.
88 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Any questions and topics not answered by the frequently asked questions (FAQ)
feature, available at the GEF Web site, can be discussed in the GEF newsgroup
(eclipse.tools.gef), which is available at the Eclipse news server
(news.eclipse.org).
The Eclipse Wiki also has a section for GEF related topics, which provides an
additional list of answers for frequently asked questions and additional examples
and other resources.
The most common case might be a modelling application. You can build
graphical editors for modelling nearly every kind of situation (for example,
business processes, application models, or even UI screens).
There are also graphical editors available for designing documents such as
reports, Web sites, or forms. You can develop graphical editors for modifying
environments (for example, configuration files of applications, servers, or
deployment descriptors for enterprise applications — or even for routing trains).
90 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
AcmeStudio
AcmeStudio (Figure 3-2) is a customizable editing environment and visualization
tool for software architecture designs based on the Acme architectural
description language (ADL). With AcmeStudio, you can define new Acme
families for specific domains and customize the environment to work with those
families by defining new diagram styles. AcmeStudio is an adaptable front-end
that may be used in a variety of modeling and analysis applications. Written as
an Eclipse plug-in, AcmeStudio provides the opportunity to integrate third-party
architectural analysis tools.
Plant
HLAex.Plant
ExternalOut
Externalinputs
Scheduler
92 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Jeez Report Designer
Jeez Report Designer (Figure 3-4) allows visual designing of reports that can be
executed using a report engine. It is freely available at:
http://jeez.sourceforge.net
Figures can be transparent or opaque, and can be ordered into layers, thus
allowing parts of a diagram to be hidden or excluded from certain operations.
Draw2D is a standalone graphics library that can be used by itself to create
graphical views in Eclipse. A complete coverage of Draw2D in depth is beyond
the scope of this book. Instead, we discuss some key Draw2D concepts and
focus on the Draw2D features and classes that are most important to GEF
developers.
Tip: When you create a standalone Draw2D application, you need to make
sure that your operating system is able to locate the SWT native library.
For instance, on Microsoft Windows, make sure that the following file is added
to your class path:
ECLIPSE_HOME\plugins\org.eclipse.swt.win32_2.1.1\os\win32\x86swt32.dll
94 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
In Example 3-1 on page 94, the shell serves as the SWT canvas that is needed
to host Draw2D. The LightweightSystem class provides the bridge between SWT
and Draw2D by routing mouse and paint events to the figures it contains. A root
figure is then added to the LightweightSystem. The root figure is configured with
a layout manager which controls the layout of any child figures that are
subsequently added to it.
3.2.3 Figures
In this section we go into more detail on the capabilities of figures in general, and
introduce some of the specialized figures that Draw2D provides.
Methods
Everything that is visible in a Draw2D window is drawn on a figure. The figure
class contains a number of methods that provide the following functionality:
Registering or deregistering listeners on a figure; the figure will notify listeners
of mouse events within the figure
Structural events, for structural changes in the figures hierarchy, and for
movement or resizing of the figure
Specifying the cursor to display when the mouse passes over it
Operations to manage the figure's place in the figure hierarchy, including
adding and removing children and accessing them or its parent figure
Accessors for:
– The figure's layout manager
– The figure’s size and location
– The tooltips
Setting and getting focus
Specifying the figure's transparency and visibility
Performing coordinate conversion, intersection, and hit testing
Painting
Validating
Subclasses
Draw2D provides many subclasses of figure that provide useful additional
functionality. We describe some of these in the following sections.
Widgets
Draw2D includes figures which allow you to create lightweight widgets that can
be used when you need an input control within your Draw2D application. These
include various buttons, Checkbox, and the text entry figure, Label.
3.2.4 Mechanism
This section introduces the core classes of the Draw2D architecture.
LightweightSystem
The LightweightSystem class is the heart of Draw2D. It performs the mapping
between an SWT canvas and the Draw2D system that is hosted within it. It
contains three main components:
The root figure: This is an instance of the LightweightSystem$RootFigure
class; this top level figure is the parent of your application's root figure. It
inherits some of the graphical environment of the hosting SWT Canvas, such
as font, background, and foreground colors.
The event dispatcher: The SWTEventDispatcher class translates SWT
events to the corresponding Draw2D events in the root figure. It tracks which
figure has focus, which figure is being targeted by mouse events, and handles
tooltip activation. It provides support for figures that want to capture the
mouse.
96 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The update manager: The update manager is responsible for painting and
updating Draw2D figures. The LightweightSystem calls the update manager's
performUpdate() method when a paint request is received from the underlying
SWT canvas. The update manager typically maintains a worklist of figures
that are invalid or need repainting. The update manager tries to coalesce its
work lists so that it can be as efficient as possible. The default update
manager, DeferredUpdateManager, allows updates to be performed
asynchronously by queuing work on the Display's user interface thread.
The main processes in a figure's life cycle are painting and validating. Draw2D
asks a figure to render itself by calling the figures paint methods. The paint()
method invokes three more specific paint methods:
paintfigure() — The figure should render itself.
paintclientarea() — The figure should render its children.
paintborder() — The figure should render its border, if any. Clipping is used to
protect the border.
Borders
It is frequently necessary to provide a visual border to figures. The Draw2D
package contains several classes, derived from the Border class, which provide
a variety of border effects:
GroupBoxBorder — Creates a labeled border similar to group boxes in native
window systems
TitleBarBorder — Creates a titled border that resembles a titled window
CompoundBorder — A border composed of two borders
FrameBorder — Similar to TitleBarBorder; can be used to create figures with
titles
FocusBorder — Surrounds a figure with a focus rectangle
LineBorder — Creates an outline around a figure of the width you specify
The Insets class is used to represent the space within a figure that is allocated to
the border. Notice that the border does not have to be symmetrical. It can be
occupy any combination of a figure's edges, and can be a different size on any
edge. The paint method clips the client area so that painting is constrained to the
area of the figure inside the inset. The border, when present, is the last part of
the figure to be painted.
Layouts
LayoutManagers are used to manage the position and size of a figure's child
figures. They interrogate each child figure to obtain its preferred size, and then
apply some layout algorithm to calculate the final size and placement of the child
figures. LayoutManagers also support constraints, which are data attached to
each figure that gives additional guidance to the layout manager. The figure has
accessor methods for its constraints, and the layout manager maintains a map of
constraints for the figures it is managing.
The constraint accessors use the Object type for constraints since the type of the
constraint depends on the layout manager being used. For instance, the
XYLayout layout manager requires that the figures it manages have a constraint
of type Rectangle, and the DelegatingLayout manager expects its figures to have
a constraint which implements the Locator interface. All Draw2D layout
managers derive from the AbstractLayout abstract class.
98 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
These are some of the provided layout managers:
FlowLayout — Lays out its children into either rows or columns, which is
configurable either by using the constructor:
public FlowLayout(boolean isHorizontal)
or by calling the method:
setHorizontal(isHorizontal)
The manager causes its children to wrap when the current row or column is
full. There are also methods to control the alignment and spacing of rows in
both the major and minor axes.
DelegatingLayout — Delegates the layout of its child figures to the child
figures' locators. The children must provide a Locator subclass as their
constraint.
XYLayout — Places its children at the location and dimensions specified for
the child. The child’s constraint must be a Rectangle object that specifies this
information.
Draw2D provides a scrollable pane via the ScrollPane class. To implement this
functionality it uses the ScrollPaneLayout, which manages the layout of the scroll
bars and Viewport that comprise the ScrollPane. In addition the Viewport uses
the ViewportLayout manager to manage the viewport's visible region and
maintain the scroll position state.
Layers
Layers are transparent figures intended specifically for use in LayerPanes. They
override the figure's containsPoint() and findFigureAt() methods so that hit
testing will “pass through” the layer. The FreeformLayer class adds additional
specialization to Layer to provide a layer that can extend indefinitely in all four
directions.
Note: The term “Freeform”, when used in Draw2D class names, indicates that
the class supports figures that can expand in all directions — that is, they do
not have a fixed size or origin, which also implies that the child figures can
have negative coordinates. Some examples are the FreeformLayer,
FreeformLayeredPane, and ScalableFreeformLayeredPane classes.
Locators
Implementors of the Locator interface are used in Draw2D to position figures.
The interface consists of a single method:
void relocate(IFigure target)
Subclasses of ConnectionLocator are used for locating figures that are attached
to a Connection. These can be used for placing arrowheads on the ends of
connections or placing labels or other decorations or annotations on a
Connection. The locator ensures that the figure stays “attached” to the
Connection in the designated location as the Connection is moved.
100 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Connection anchors
Draw2D provides classes that provide various styles of anchor points, which are
used to represent the ends of a connection. The basic function of these classes
is to contain the location of a Connection's endpoints and to register listeners that
will be notified if the end of a connection is moved.
Connection routers
Connection routers are used to calculate the path that a connection takes in
getting from one anchor to the other. AbstractRouter is the base class for
connection routers that implement the ConnectionRouter interface. Available
connection routers include:
NullConnectionRouter — By default this simply draws a straight line between
the anchors of a connection. This is shown in Figure 3-6 using a diagram
created using the logic sample application.However Draw2D also provides
more sophisticated routers that use different criteria to determine the path that
a connection will take.
102 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Rotatable decorations to place decorations on connections that can realign
themselves as the angle of the connection changes.
3.3.1 Prerequisites
We assume that you already have good knowledge and experience in Eclipse
plug-in development. You should have understand the concepts of Eclipse views
and editors.
The following articles from eclipse.org are very useful for understanding terms
and concepts mentioned in this chapter:
Eclipse Platform Technical Overview
Notes on the Eclipse Plug-in Architecture by Azad Bolour
How to Use the Eclipse API by Jim des Rivieres
Creating an Eclipse View by Dave Springgay
Getting Started with the Graphical Editing Framework by Randy Hudson
You should have installed Eclipse SDK 2.1.1 including GEF SDK 2.1.1, and you
should be familiar with Draw2D concepts and terms provided by the developers
guides, which are available in the Eclipse online help.
3.3.2 EditParts
EditParts are the central elements in GEF applications. They are the controllers
that specify how model elements are mapped to visual figures and how these
figures behave in different situations.
Usually you will have to create an EditPart class for every model element class
so you will have likely the same class hierarchy for the EditParts as you have for
your model. The process of creating EditPart instances is not covered here. It will
be explained in a later section.
Actually there are three different types of EditParts. For now, in this section, we
will focus on only two of them: GraphicalEditParts and ConnectionEditParts.
GraphicalEditParts are those EditParts that provide a graphical representation
for their model. These graphical representations are figures.
ConnectionEditParts represent connections between GraphicalEditParts.
The third type, TreeEditParts, is only interesting for building trees of your model
with SWT tree widgets. This is not the primary intention of a graphical editor, but
is probably useful for the outline view. Our redbook sample application will show
you an introduction into this.
The EditPart interfaces provide a lot of methods. Your are not expected to call
them, except for get/setModel, when necessary. Nearly all methods are used by
the Graphical Editing Framework to handle the edit parts. But you can add your
own methods, and we encourage you to add your EditPart implementations to
ease your access to model elements and properties.
Life-cycle of EditParts
We already know that EditParts will be created somewhere and somehow by a
factory (details about the factory will be explained later in 3.5.3, “Creating
EditParts” on page 137). We will now focus on the methods involved in EditPart
life-cycles.
When an EditPart was created, it is not yet visible or active. It becomes active
when the Graphical Editing Framework gets informed about it. If an EditPart
becomes obsolete for some reason (either the editor is closed or the model
object represented by the EditPart was deleted) and the framework no longer
needs it, it will be deactivated. There are two methods, EditPart#activate and
EditPart#deactivate, which will be called by the framework when the state of an
EditPart changes. A third method, EditPart#isActive, always returns the current
activation state.
Note: Although the JavaDoc of these methods indicates that an EditPart may
be reactivated after it was obsolete, we have not experienced such situations
during our development.
104 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
We suggest not to develop with the reuse of EditParts in mind. You should not try
to keep track of EditPart instances across editor sessions. If an EditPart gets
deactivated, throw it away. Allow it to get garbage collected by the virtual
machine. By handling EditPart instances this way, you do not need to worry
about the memory overhead. It will be solved for you and you can enjoy the
advantages of Java.
Figures
GraphicalEditParts have a figure that is the visual part of the model. The
GraphicalEditPart need to create the figure, update it on model changes, and
dispose it (if necessary) if the EditPart is deactivated.
More about figures can be found in the Draw2D developers guide (see the
Eclipse online help) and in 3.2.3, “Figures” on page 95 of this book.
Connections
A ConnectionEditPart, which represents a connection between two EditParts, is
nothing more than a GraphicalEditPart, which has a source and a target EditPart.
Connections are connected to ConnectionAnchors. These anchors should be
provided by the EditParts the ConnectionEditPart points to/comes from.
Request Creators
Forward Forward
Request Command
EditPart
Forward Forward
Request Command
EditPolicies
Create Command
for Request
Command
Figure 3-9 shows the typical communication chain of a request and the objects
involved. As you can see, someone (typically a tool, an action or some drag or
drop handler) creates a request. This request is forwarded to an EditPart. The
EditPart doesn’t process the request itself. Instead it delegates it to an EditPolicy,
which understands the request. The EditPolicy itself creates a command for the
request, which will be executed to fulfill the request.
CreateRequests
CreateRequests are used everywhere a new model object should be created.
For connections the subclass CreateConnectionRequest is used. A
CreateRequest has a CreationFactory, which you have to provide. This
CreationFactory is responsible for creating the new model objects.
106 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Tip: Actually, your CreationFactory implementation does not need to create
new model objects. We suggest only submitting the type of the new model
object and creating it later in a Command.
GroupRequests
GroupRequests are Requests that can span more than one EditPart into one
single request. A typical GroupRequest is the ChangeBoundsRequests, which is
responsible for moving and/or resizing EditParts.
LocationRequests
LocationRequests are requests that keep track of a location — for example, the
SelectionRequest, which is responsible for selecting an EditPart. You can always
determine where the user clicked into an EditPart to select it. This allows you to
provide special behavior for different locations inside your EditPart.
3.3.4 EditPolicies
We already know that the communication inside the Graphical Editing
Framework is done via requests and that these requests are forwarded to
EditPolicies. What are EditPolicies, and why is this done in this way?
Actually, EditPolicies are those parts in the Graphical Editing Framework, which
bring the editing functionallity into EditParts, This is done because it is a good
object-oriented design.
Component role
The component role is defined as EditPolicy#COMPONENT_ROLE, and the
base class for these kind of EditPolicies is ComponentEditPolicy.
Connection role
The connection role is defined as EditPolicy#CONNECTION_ROLE, and the
base class is ConnectionEditPolicy. It is the corresponding component role for
ConnectionEditParts.
Container role
The container role (EditPolicy#CONTAINER_ROLE, ContainerEditPolicy) is
responsible for operations typically performed on containers (for example, the
creation of children). Each EditPart with children would have a
ContainerEditPolicy.
Layout role
The layout role (EditPolicy#LAYOUT_ROLE, LayoutEditPolicy) is responsible for
containers that have a layout associated to them. It can calculate proper
locations for requests and define where children should be placed.
108 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Additional roles
More documentation about additional roles is available in the GEF developers
guide available in the Eclipse online help.
3.3.5 Commands
A command is the part that actually modifies your model. Commands simplify the
way of modifying your model because they provide support for:
Execution limitations
Undo and redo
Combining and chaining
There is nothing more to say about commands than what can be found in the
JavaDoc. You need to implement them and you need to instantiate them. The
abstract base class is org.eclipse.gef.commands.Command.
3.3.6 GraphicalViewers
From the Draw2D developers guide, we know that figures are drawn by a
LightweightSystem. But that is not all. There are some more components
involved, which we do not want to take care of when we are developing our
editor. That is why the Graphical Editing Framework provides the
GraphicalViewer.
Typically, a JFace viewer only needs some content, a factory, and some
configuration, and it is done. It already provides all necessary implementation for
drag and drop support, event and update handling, and other complicated tasks.
A GEF GraphicalViewer does exactly the same.
There are two GraphicalViewer implementations available: one that does support
native scrolling (ScrollingGraphicalViewer) and one that does not
(GraphicalViewerImpl). The most common case is to use a viewer that supports
native scrolling. It is even possible to have a ScrollingGraphicalViewer never
showing its scrollbars. Thus, we will focus on this implementation.
Note: Please do not be confused by RootEditPart and the root of your model.
Both are completely different and have no relation to each other.
3.3.7 RootEditParts
A RootEditPart is a special kind of an EditPart. It has absolutely no relation to
your model and should not be understood as a typical EditPart.
There are several implementations available, but actually only two of them
should be used — ScalableRootEditPart and ScalableFreeformRootEditPart. All
other implementations are either deprecated or provide only a subset of
functionality of the implementations mentioned above.
110 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Layers
Figure 3-10 gives an overview of the layers introduced by ScalableRootEditPart
and ScalableFreeformRootEditPart.
Feedback Layer
Handle Layer
Scalable Layers
Printable Layers
Connection Layer
Primary Layer
Layers are used to separate and/or group figures of EditParts to better control
their overlapping. Actually, all figures are placed into the primary layer. Figures
representing connections are placed on the connection layer and so they are
always painted above the other figures. Special figures (like drag or drop
feedback or handles) are painted into special layers above the scalable layers.
This is important because if you ever want to paint something in the feedback or
handle layers, you must be aware that you need to scale this manually before
painting.
Freeform or not
When using the ScalableFreeformRootEditPart, your editor can extend in all
directions. Thus, it is even possible to have negative coordinates. The
(non-freeform) ScalableRootEditPart only allows extension in positive directions.
Typically you will already have a save option in your model, so we do not discuss
the implementation of the methods save, isSaveAsAllowed, and saveAs here.
112 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
public void doSave(IProgressMonitor monitor)
{
// your save implementation here
}
3.4.2 EditDomain
Next we need an EditDomain. An EditDomain is an interface that logically
bundles an editor, viewers, and tools. Therefore, it defines the real editor
application.
Usually you will have one EditDomain per editor, but it is also possible to share
an EditDomain across several editors in a multi-page editor.
/**
* Returns the <code>EditDomain</code> used by this editor.
* @return the <code>EditDomain</code> used by this editor
*/
public EditDomain getEditDomain()
{
if (editDomain == null)
editDomain = new DefaultEditDomain(this);
return editDomain;
}
3.4.3 CommandStack
After adding the EditDomain, we have access to the CommandStack. We will
use the CommandStack to indicate when an editor is dirty.
Note: If you ever execute a command yourself, please ensure that you
execute it through the CommandStack.
This is not done by simply delegating the editors isDirty method to the
CommandStack; instead, we need a listener that listens to CommandStack
changes and updates the dirty state of our editor. Whenever this state changes,
we need to inform the Eclipse workbench. But you need not be concerned about
this. The superclass EditorPart provides methods for the last part.
We start with the last part, as it is the easiest task. We simply add a flag for the
dirty state and a setter that automatically fires an event, as shown in
Example 3-4.
/**
* Indicates if the editor has unsaved changes.
114 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
* @see EditorPart#isDirty
*/
public boolean isDirty()
{
return isDirty;
}
/**
* Sets the dirty state of this editor.
*
* <p>An event will be fired immediately if the new
* state is different than the current one.
*
* @param dirty the new dirty state to set
*/
protected void setDirty(boolean dirty)
{
if(isDirty != dirty)
{
isDirty = dirty;
firePropertyChange(IEditorPart.PROP_DIRTY);
}
}
/**
* Returns the <code>CommandStack</code> of this editor's
* <code>EditDomain</code>.
*
* @return the <code>CommandStack</code>
*/
public CommandStack getCommandStack()
{
/**
* Returns the <code>CommandStackListener</code>.
* @return the <code>CommandStackListener</code>
*/
protected CommandStackListener getCommandStackListener()
{
return commandStackListener;
}
Attaching the listener should be done when the editor gets it input, and removing
it should be done in the editor’s dispose method. See Example 3-6.
// add CommandStackListener
getCommandStack().addCommandStackListener(getCommandStackListener());
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose()
{
// remove CommandStackListener
getCommandStack().removeCommandStackListener(getCommandStackListener());
Do not forget to update the CommandStack when the editor content is saved.
See Example 3-7.
116 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 3-7 Update CommandStack on editor save
/**
* TODO: Implement "doSave".
* @see EditorPart#doSave
*/
public void doSave(IProgressMonitor monitor)
{
// your implementation here
// update CommandStack
getCommandStack().markSaveLocation();
}
/**
* TODO: Implement "doSaveAs".
* @see EditorPart#doSaveAs
*/
public void doSaveAs()
{
// your implementation here
// update CommandStack
getCommandStack().markSaveLocation();
}
/**
* Creates the controls of the editor.
* @see EditorPart#createPartControl
*/
public void createPartControl(Composite parent)
{
/**
* Creates a new <code>GraphicalViewer</code>, configures, registers
* and initializes it. *
* @param parent the parent composite
* @return a new <code>GraphicalViewer</code>
*/
private GraphicalViewer createGraphicalViewer(Composite parent)
{
// create graphical viewer
GraphicalViewer viewer = new ScrollingGraphicalViewer();
viewer.createControl(parent);
return viewer;
}
/**
* Returns the <code>GraphicalViewer</code> of this editor.
* @return the <code>GraphicalViewer</code>
*/
public GraphicalViewer getGraphicalViewer()
{
return graphicalViewer;
}
/**
* Returns the content of this editor
* @return the model object
*/
protected Object getContent()
{
// todo return your model here
return null;
118 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
}
/**
* Returns the <code>EditPartFactory</code> that the
* <code>GraphicalViewer</code> will use.
* @return the <code>EditPartFactory</code>
*/
protected EditPartFactory getEditPartFactory()
{
// todo return your EditPartFactory here
return null;
}
// hook the viewer into the EditDomain (only one palette per EditDomain)
getEditDomain().setPaletteViewer(viewer);
return viewer;
}
120 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
/**
* Returns the <code>PaletteRoot</code> this editor's palette uses.
* @return the <code>PaletteRoot</code>
*/
protected PaletteRoot getPaletteRoot()
{
// todo add your palette entries here
return null;
}
Next, we need to add this viewer to the editor’s composite. The SWT SashForm
is used to have the palette’s width modifiable, as shown in Example 3-11.
/**
* Returns the <code>PaletteViewer</code> of this editor.
* @return the <code>PaletteViewer</code>
*/
public PaletteViewer getPaletteViewer()
{
return paletteViewer;
}
There are several default tools available, and we need to add them so that we
have an initial PaletteRoot. See Example 3-12.
/**
// a separator
PaletteSeparator separator =
new PaletteSeparator(
EditorExamplePlugin.PLUGIN_ID + ".palette.seperator");
separator.setUserModificationPermission(
PaletteEntry.PERMISSION_NO_MODIFICATION);
controls.add(separator);
122 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
paletteRoot.addAll(categories);
}
return paletteRoot;
}
Palette customizer
It is possible to attach a palette customizer to the palette. This will enable the
users of your editor to modify the palette to work in the way they prefer. For
implementation details, please see our completed redbook sample application as
described in Chapter 7, “Implementing the sample” on page 203, or the Logic
example application provided from the GEF development team.
3.4.7 Actions
Actions are common objects in the Eclipse workbench to do something when
user requests are initiated through menu items, toolbar buttons or context menu
items. The Graphical Editing Framework provides a set of standard actions and
an infrastructure for using these actions within the Graphical Editing Framework.
ActionRegistry
The class org.eclipse.gef.actions.ActionRegistry serves as a container for editor
actions. The editor is responsible for providing and maintaining an
ActionRegistry. See Example 3-13.
/**
* Returns the action registry of this editor.
* @return the action registry
*/
public ActionRegistry getActionRegistry()
{
if (actionRegistry == null)
actionRegistry = new ActionRegistry();
return actionRegistry;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class adapter)
{
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose()
{
// remove CommandStackListener
getCommandStack().removeCommandStackListener(getCommandStackListener());
Managing actions
As soon as we have the ActionRegistry, we are able to create actions and to
register them.
The Graphical Editing Framework provides a set of default actions (redo, undo,
delete, print, and save). These actions need some special handling to stay
up-to-date with the editor, the CommandStack or the EditParts. The GEF default
actions are not implemented as listeners to some events. Instead, they have to
be updated manually. This can be done from within the editor as shown in
Example 3-14.
124 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
private List stackActionIDs = new ArrayList();
/**
* Adds an <code>EditPart</code> action to this editor.
*
* <p><code>EditPart</code> actions are actions that depend
* and work on the selected <code>EditPart</code>s.
*
* @param action the <code>EditPart</code> action
*/
protected void addEditPartAction(SelectionAction action)
{
getActionRegistry().registerAction(action);
editPartActionIDs.add(action.getId());
}
/**
* Adds an <code>CommandStack</code> action to this editor.
*
* <p><code>CommandStack</code> actions are actions that depend
* and work on the <code>CommandStack</code>.
*
* @param action the <code>CommandStack</code> action
*/
protected void addStackAction(StackAction action)
{
getActionRegistry().registerAction(action);
stackActionIDs.add(action.getId());
}
/**
* Adds an editor action to this editor.
*
* <p><Editor actions are actions that depend
* and work on the editor.
*
* @param action the editor action
*/
protected void addEditorAction(EditorPartAction action)
{
getActionRegistry().registerAction(action);
editorActionIDs.add(action.getId());
}
/**
* Adds an action to this editor's <code>ActionRegistry</code>.
Now that we have the action infrastructure, we must implement the automatic
updating of the different actions. Editor actions must be updated when the editor
changes, CommandStack actions when the CommandStack changes, and
EditPart actions when the selection changes. Example 3-15 shows how to add
update support for actions to our sample editor.
}
}
/**
* The <code>CommandStackListener</code> that listens for
* <code>CommandStack </code>changes.
*/
private CommandStackListener commandStackListener =
new CommandStackListener()
{
public void commandStackChanged(EventObject event)
{
updateActions(stackActionIDs);
setDirty(getCommandStack().isDirty());
}
};
126 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
private ISelectionListener selectionListener = new ISelectionListener()
{
public void selectionChanged(IWorkbenchPart part, ISelection selection)
{
updateActions(editPartActionIDs);
}
};
/**
* Returns the selection listener.
*
* @return the <code>ISelectionListener</code>
*/
protected ISelectionListener getSelectionListener()
{
return selectionListener;
}
/**
* Initializes the editor.
* @see EditorPart#init
*/
public void init(IEditorSite site, IEditorInput input)
throws PartInitException
{
// store site and input
setSite(site);
setInput(input);
// add CommandStackListener
getCommandStack().addCommandStackListener(getCommandStackListener());
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose()
{
// remove CommandStackListener
getCommandStack().removeCommandStackListener(getCommandStackListener());
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#firePropertyChange(int)
*/
protected void firePropertyChange(int propertyId)
{
super.firePropertyChange(propertyId);
updateActions(editorActionIDs);
}
Now, when all the infrastructure is ready, we are able to create and add our
actions as shown in Example 3-16.
// add CommandStackListener
getCommandStack().addCommandStackListener(getCommandStackListener());
// initialize actions
128 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
createActions();
}
/**
* Creates actions and registers them to the ActionRegistry.
*/
protected void createActions()
{
addStackAction(new UndoAction(this));
addStackAction(new RedoAction(this));
addEditorAction(new SaveAction(this));
addEditorAction(new PrintAction(this));
}
To enable this, the editor must deliver its own IPropertySheetPage. This
IPropertySheetPage is a default PropertySheetPage customized with an
undoable root entry provided by GEF. See Example 3-17.
/**
* Returns the undoable <code>PropertySheetPage</code> for
* this editor.
*
* @return the undoable <code>PropertySheetPage</code>
*/
protected PropertySheetPage getPropertySheetPage()
{
if (null == undoablePropertySheetPage)
{
undoablePropertySheetPage = new PropertySheetPage();
undoablePropertySheetPage.setRootEntry(
GEFPlugin.createUndoablePropertySheetEntry(getCommandStack()));
}
return undoablePropertySheetPage;
}
One way is to create a complete SWT based outline view without using the
Graphical Editing Framework. In many cases this is suitable and can be easily
done, because many components, such as content and label providers or even
tree viewers, can be reused to show a tree of your model objects.
If you do not have these reusable components available, then a second way is to
build a tree with GEF components. The Graphical Editing Framework provides a
TreeViewer and TreeEditParts for this case. You can also reuse actions created
for your graphical editor. For details about implementing a model tree with the
GEF TreeViewer and TreeEditParts, please see our redbook sample application.
A third way is to provide an overview of your graphical editor. Example 3-18 and
Example 3-19 show a possible implementation of this.
130 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 3-18 An overview outline page
/**
* This is a sample implementation of an outline page showing an
* overview of a graphical editor.
*
* @author Gunnar Wagenknecht
*/
public class OverviewOutlinePage extends Page implements IContentOutlinePage
{
/ **
* Creates a new OverviewOutlinePage instance.
* @param rootEditPart the root edit part to show the overview from
*/
public OverviewOutlinePage(ScalableFreeformRootEditPart rootEditPart)
{
super();
this.rootEditPart = rootEditPart;
}
/* (non-Javadoc)
* @see ISelectionProvider#addSelectionChangedListener
* (ISelectionChangedListener)
*/
public void addSelectionChangedListener(ISelectionChangedListener listener)
{}
/* (non-Javadoc)
* @see IPage#createControl(Composite)
*/
public void createControl(Composite parent)
{
// create canvas and lws
overview = new Canvas(parent, SWT.NONE);
LightweightSystem lws = new LightweightSystem(overview);
// create thumbnail
thumbnail =
new ScrollableThumbnail((Viewport) rootEditPart.getFigure());
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#dispose()
*/
public void dispose()
{
if (null != thumbnail)
thumbnail.deactivate();
super.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#getControl()
*/
public Control getControl()
{
return overview;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
*/
public ISelection getSelection()
{
return StructuredSelection.EMPTY;
}
/* (non-Javadoc)
* @see ISelectionProvider#removeSelectionChangedListener
* (ISelectionChangedListener)
*/
public void removeSelectionChangedListener(
ISelectionChangedListener listener)
{}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IPage#setFocus()
*/
public void setFocus()
{
if (getControl() != null)
getControl().setFocus();
}
132 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
/* (non-Javadoc)
* @see ISelectionProvider#setSelection(ISelection)
*/
public void setSelection(ISelection selection)
{}
}
Now this page can be used in the editor, as shown in Example 3-19.
/**
* Returns the overview for the outline view.
*
* @return the overview
*/
protected OverviewOutlinePage getOverviewOutlinePage()
{
if (null == overviewOutlinePage && null != getGraphicalViewer())
{
RootEditPart rootEditPart = getGraphicalViewer().getRootEditPart();
return overviewOutlinePage;
}
This is not a difficult task, because as with all other GEF concepts, there is a
default implementation available, which provides a very feature-rich set of keys
for interacting with a GraphicalViewer. The default implementation is the class
org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler. Example 3-20 shows how
to use this key handler with our editor sample.
134 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
// initialize the viewer with input
viewer.setEditPartFactory(getEditPartFactory());
viewer.setContents(getContent());
return viewer;
}
Tip: If you like to attach actions to your own key strokes, you do not need to
overwrite the GraphicalViewerKeyHandler. It is simply possible to attach a
parent to KeyHandlers. Thus, you simply create your own KeyHandler
instance (not GraphicalViewerKeyHandler), configure this KeyHandler
instance, and set it as the parent of the GraphicalViewerKeyHandler you
created for the GraphicalViewer.
Whether you want a simple one-to-one representation or not, you need to have
one main EditPart. This main EditPart is also called content EditPart and serves
as the main entry point for your representation. It is important to understand this
because each EditPartViewer can only have one content EditPart.
The best way to do this depends on your preference and what fits best with your
model. Mostly it is possible to store constraints together with the model element,
either as a model property or as some kind of element annotation. If you do not
like doing this, it is also possible to store them separately from your model.
3.5.2 Communication
If a model element is changed somehow or somewhere; a new issue arises —
we have to ensure that all graphical representations of the model are updated
accordingly. This requires communication between the model and the controller,
which represents it.
It is not acceptable practice for your model to know about its controller, but a
model can call attention to itself and to the change which has affected it.
It is up to you how you want to implement this, but a common way is to create
some kind of event object, which is fired every time a change is done in the
model. Then every controller can register with the model element itself or with an
event manager and listen for such events.
136 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
3.5.3 Creating EditParts
As mentioned in 3.3.2, “EditParts” on page 103, creating EditParts is best done
through a factory. The EditPartFactory interface is simple to implement. It has
only one method. In that method, you need to create an EditPart for a given
model element in a specified context. The context might be useful if you consider
a different UI representation than your model actually shows. Of course it is also
necessary that you associate the specified model element with the created
EditPart before you return the new EditPart.
You may consider building a map between the created EditParts and the model
elements, but we do not recommend this. The EditPartViewer maintains an
EditPart registry. EditParts register themselves to that registry. The default
EditPart implementation already does this for you by using the model element as
the key. Using the EditPart registry is a safe way to map between EditParts and
the model elements.
One possible need for an EditPart registry is a domain based (global) listener
model where there is only one listener, which does not belong to the model. This
listener receives all the events from all model objects, and therefore it needs to
find EditParts.
Note: If your model elements need to locate EditParts, you are probably not
using the model-view-controller paradigm and should consider another
solution.
4.1.1 RootEditParts
The RootEditPart is at the root of the EditPart hierarchy. It is the link between
your application’s root edit part and the EditPartViewer. GEF provides a few
RootEditPart implementations that you can use. In order to clear up any potential
confusion about which RootEditPart is appropriate for your application, we
summarize the features of the various implementations below.
The code snippet in Example 4-1 illustrates the essential steps in initializing a
GEF application. Notice that in an actual application, these steps may be
distributed across more than one method. It shows an instance of the root
EditPart being created and used to initialize the GraphicalViewer.
viewer.setRootEditPart(root);
viewer.setEditPartFactory(new EditPartFactory());
140 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Remember that a freeform diagram expands automatically in all directions as the
user drags figures beyond the current bounds of the diagram. This feature is
generally desirable whenever you want the user to control the placement of the
figures in your application. On the other hand, if your application constrains the
placement of graphical objects, for example, into cells of a grid, then the freeform
feature might not be desirable. Table 4-1 summarizes the main characteristics of
the three root EditParts.
A figure’s getClientArea returns the rectangle in which child figures are visible. It
is cropped by any border/insets that are in effect for the figure, and the origin of
the rectangle is set to (0,0) if the figure is using local coordinates.
The figure class includes four methods for translating coordinates between
relative and absolute coordinates:
translateToParent() — translates a point in the figure’s coordinates to its value
in the parent’s coordinates
translateFromParent() — translates a point in the parent’s coordinates to its
coordinates in this figure
translateToRelative(), translates an absolute coordinate to a coordinate that is
relative to this figure, that is, recursively translates from parent
translateToAbsolute() — translates a coordinate that is relative to this figure to
an absolute coordinate, that is, recursively translates to parent
Anchors and locator reference points work with absolute coordinates. Hit testing
uses local coordinates.
/**
* Creates a layered pane and the layers that should be printed.
* @see org.eclipse.gef.print.PrintGraphicalViewerOperation
* @return a new LayeredPane containing the printable layers
*/
protected LayeredPane createPrintableLayers() {
FreeformLayeredPane layeredPane = new FreeformLayeredPane();
layeredPane.add(new FreeformLayer(), PRIMARY_LAYER);
layeredPane.add(new ConnectionLayer(), CONNECTION_LAYER);
return layeredPane;
}
142 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
By subclassing these root EditPart classes, you can gain control over the
ordering of layers, or customize which layers are printable or scalable. There are
probably few cases where it would be useful to modify the configuration of the
“stock” layers. One application that has been discussed is to place connections
under rather than over the primary figure layer. Although this can have some
aesthetic advantages, if you are considering this, keep in mind that it will be
possible for your figures to completely cover connections, making them difficult to
access. It becomes more problematic when your application includes container
nodes, because connections between nodes in a container will be occluded.
4.2 Techniques
In this section we discuss some GEF and Draw2d techniques that are useful
when developing a GEF application.
As you might know, drag and drop is organized in SWT around Transfers. They
are the base representation of something that is transferred between the SWT
controls in an drag and drop operation. Basically, there is no difference
compared with GEF.
The Graphical Editing Framework provides some classes and concepts to ease
the development of drag and drop in GEF applications. For example, you won’t
have to deal with SWT DragSource objects and other lower level classes.
When implementing drag and drop listeners for the viewers in your GEF
application, be sure to inherit from the abstract base classes. A good point to
look for further implementation information is the template drag and drop palette
demonstrated in the Logic example application provided by GEF. Our sample
application will also provide an introduction into implementing drag and drop.
The latter technique can be used in applications that use the GEF palette and
whose editor class is derived from GraphicalEditorWithPalette.
1. Create a class that implements the org.eclipse.gef.palette.PaletteListener
interface, which contains the single method:
void activeToolChanged(PaletteViewer palette, ToolEntry tool)
Add your listener to the palette by calling the palette’s method:
public void addPaletteListener(PaletteListener paletteListener);
The code inside your listener then needs to get the actual active tool from the
EditDomain’s getActiveTool() method. Then you can set the active tool’s
setUnloadWhenFinished() method to set this behavior based on whatever
criteria your application wants to use, such as a user setting or preference, or
based on what tool is active.
4.2.3 Printing
Starting with GEF version 2.0, there is built-in support for printing from GEF
applications. You simply need to add a PrintAction to your editor’s action registry
as shown in Example 4-3.
144 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
In your application’s action bar contributor class, you can provide keyboard or
menu access to the PrintAction by calling:
addGlobalActionKey(IWorkbenchActionConstants.PRINT);
4.2.4 Zooming
Support for zooming was added to GEF in version 2.0. The scaling functionality
is built into ScalableLayeredPane and ScalableFreeformLayeredPane. These
classes support scaling by maintaining the current scaling level, and by taking
the scaling factor into account when doing point translations and calculating their
client area and preferred size. They use the ScaledGraphics subclass of
Graphics as the their graphics context for painting their child figures.
The ScaledGraphics class applies the current scale factor to the normal graphics
operations, performing transformations on point lists and rectangles before
painting them. It also scales fonts, stretches or shrinks bitmaps, and scales the
line width for line drawing operations.
You can do this if you need to modify the ZoomManager's configuration, such as
to set the supported zoom levels.
When you want to add zoom controls to your editor's user interface, GEF
includes classes for zoom actions and a ContributionItem, named
ZoomComboContributionItem. These are shown in Figure 4-1.
146 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 4-4 Returning the ZoomManager via the IAdapter interface
public Object getAdapter(Class type) {
if (type == ZoomManager.class)
return ((ScalableFreeformRootEditPart)getGraphicalViewer()
.getRootEditPart()).getZoomManager();
return null;
}
To add the zoom combo box, add it to the tool bar manager in the class that
implements your application's ActionBarContributor, as shown in Example 4-5:
toolBarManager.add(new Separator());
toolBarManager.add(new ZoomComboContributionItem(getPage()));
}
To add the zoom actions, add code in your EditorPart-derived editor class that
registers the actions with the action registry. See Example 4-6.
Finally, to add menu items for the zoom actions to the action bar menu, you add
them to the MenuManager as shown in Example 4-7.
If you want a more customized decoration, you can call either classes’
setTemplate method, passing it a list of points for your custom polyline or
polygon figure. You can also control the size of your decoration by calling its
setScale method.
148 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
PolylineConnections use a DelegatingLayout for their layout manager, meaning
that its child figures must provide a constraint that is a subclass of Locator.
When you place a decoration on a connection using setSourceDecoration or
setTargetDecoration the these methods will automatically create an
ArrowHeadLocator and set it as the constraint on the decoration. The arrowhead
locator will ensure that the decoration is placed correctly on the ends of the
connection.
DirectEditPolicy
Direct editing allows for visual editing of graphical elements by launching a cell
editor in response to mouse clicks on the target EditPart. Creating a direct edit
implementation is discussed in detail in “4.2.9, “Using direct edit” on page 158”,
and is demonstrated in our redbook sample application.
Customizing:
You can expose different properties of your model to editing based on where
the user clicked.
You can respond differently to double-clicks.
You can create custom cell editors
150 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
GraphicalNodeEditPolicy
This class provides visual feedback while creating or reconnecting connections.
It works in conjunction with NodeEditPart-derived EditParts to provide a
simulated connection while the connection is being dragged to a target. The
NodeEditPart returns the best potential anchor point given the current mouse
position. The simulated connection, drawn on the feedback layer, will snap to
anchor proposed by the NodeEditPart. In typical implementations this will be the
source anchor that is closest to the current mouse position.
Customizing:
Override createDummyConnection() to return a customized figure to show
the creation feedback, possibly by changing the color, or line style or weight.
In your NodeEditPart-derived EditPart’s implementation of
getTargetConnectionAnchor or getSourceConnectionAnchor, apply additional
filtering criteria to hide anchors that are not appropriate sources or targets for
the current request. These anchors will then be ignored.
Add target connection feedback. The default implementation has no visual
effect for highlighting the target connection.
LayoutEditPolicy
This is a base class for EditPolicies that place their child EditParts using some
type of LayoutManager. Subclasses should provide visual feedback that shows
how the layout constraints will determine where a new element can be inserted.
Key methods include:
showLayoutTargetFeedback — This method gives visual feedback showing
where the current operation will place the resulting figure. Subclasses will
typically be constraining the placement of new figures to certain locations,
and this feedback should make those constraints clear to the user. The figure
returned by this method is effectively a type of cursor showing where the
insertion point for the operation is located.
getSizeOnDropFeedback — Shows the size that the new figure will assume if
the drag operation is completed.
Customizing:
The default implementation of showLayoutTargetFeedback does nothing.
Implement this in a subclass to show the insertion point for new objects.
The default implementation of getSizeOnDropFeedback() can be changed to
use a different shape or color, and so on.
Override getSizeOnDropFeedback() can be used if you want to provide visual
feedback indicating the size of the new figure is constrained to some
minimum and/or maximum size.
Customizing:
The getLineFeedback() method can be called to get the default line figure,
and then some of its attributes can be modified, such as thickness, line style,
or thickness. If you want to use a different figure altogether, then you will
probably need to override the showLayoutTargetFeedback() to do the math
required to locate and size your figure correctly.
SelectionEditPolicy
This is an abstract base for EditPolicies that provide visual feedback for the focus
selection state of EditParts. Note that the feedback figures are drawn on the
feedback layer (LayerConstants.FEEDBACK_LAYER). The methods in this class
include:
protected void showFocus()
protected abstract void showSelection()
showPrimarySelection()
hideFocus()
hideSelection()
The purpose of these methods is clear from the method names. Custom
subclasses could be used to:
Provide a non-standard focus or selection indicator, perhaps to conform to a
non-rectangular figure.
Provide an implementation for figures that can be selected but not moved.
Render the selection indicator of the primary selection in a way that
distinguishes it from the other selections.
SelectionHandlesEditPolicy
This EditPolicy supplies a specialization of the SelectionEditPolicy that supports
selections with handles. Subclasses provide the list of Handles that should
decorate the selected EditPart. For instance, GEF includes the following
SelectionHandlesEditPolicy-derived subclasses:
BendpointEditPolicy: This SelectionEditPolicy displays bendpoint handles
when a Connection is selected.
ConnectionEndpointEditPolicy: This EditPolicy displays handles on the
ends of a Connection when it is selected to support disconnecting and
reconnecting connections.
152 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
NonResizableEditPolicy: This EditPolicy, which prevents resizing,
surrounds the selected EditPart with a simple outline and places a small
square in each corner that allows for dragging.
ResizableEditPolicy: This class extends the NonResizableEditPolicy class
by adding handles on each side of the selection rectangle to allow for
resizing. Customize these classes to:
– Indicate that resizing is limited to one dimension, or may be constrained to
maintain the part’s aspect ration. Implementing this would also require
customizing the DragTracker to enforce these constraints.
– Provide a selection effect that is more visually harmonious with unusually
shaped parts.
– Create a “lighter weight” visual for selection that may work better for
showing the selection on small parts.
– Use custom handles.
The EditDomain class is designed to integrate with the palette when it is present.
There are two methods that are used to establish its connection to the palette:
When the EditDomain’s PaletteRoot is set by calling
setPaletteRoot(PaletteRoot root), the EditDomain will then obtain its default
Tool from the PaletteRoot
Setting the EditDomain’s PaletteViewer by calling its
setPaletteViewer(PaletteViewer palette) method will cause it to register itself
as a listener for tool selection changes in the palette.
Therefore, the first step in creating a palette-less application is to omit setting the
EditDomain’s PaletteRoot and PaletteViewer. This EditDomain will then return
the SelectionTool when its getActiveTool() method is called. This can be
achieved simply by constructing your editor as a subclass of
org.eclipse.ui.parts.GraphicalEditor.
In this section we demonstrate how to add a toolbar button that sets the
EditDomain’s active tool. The action we create could also be used to make the
tool available on the editor’s context menu if desired. For the purposes of this
example we use the Graphical Editing Framework logic editor example program.
We modify it so that the tool to create a “Flow Container” is available as a button
on the tool bar, as show in Figure 4-2.
154 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Create the new class AddFlowContainerAction in the package
org.eclipse.gef.examples.logicdesigner.actions. An abbreviated version of the
Java code appears below in Example 4-9. Notice that this is a fairly
generic-looking action implementation. The constructor includes calls to set
the action’s image, description, and title to the same values that were formally
set in the palette entry. The run() method creates a new tool instance and
uses it to call the editor’s setActiveTool method, which in turn sets the
EditDomain’s active tool via its own setActiveTool method.
import org.eclipse.gef.Tool;
import org.eclipse.gef.examples.logicdesigner.LogicEditor;
import org.eclipse.gef.examples.logicdesigner.LogicMessages;
import org.eclipse.gef.examples.logicdesigner.ToolActivationListener;
import org.eclipse.gef.examples.logicdesigner.model.Circuit;
import org.eclipse.gef.examples.logicdesigner.model.LogicFlowContainer;
import org.eclipse.gef.requests.CreationFactory;
import org.eclipse.gef.requests.SimpleFactory;
import org.eclipse.gef.tools.CreationTool;
import org.eclipse.gef.ui.actions.EditorPartAction;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IEditorPart;
/**
* @param editor
*/
public AddFlowContainerAction(IEditorPart editor) {
super(editor);
setDescription(
LogicMessages.LogicPlugin_Tool_CreationTool_FlowContainer_Description );
setImageDescriptor( ImageDescriptor.createFromFile(Circuit.class,
“icons/logicflow16.gif”) );
setText( LogicMessages.LogicPlugin_Tool_CreationTool_FlowContainer_Label
);
((LogicEditor)getEditorPart()).setActiveTool( tool );
}
setId( ADD_CONTAINER );
}
/**
* @see org.eclipse.gef.ui.actions.ActionBarContributor#createActions()
*/
protected void buildActions() {
addRetargetAction(...);
156 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
addFlowContainerAction = new AddFlowContainerAction( null );
addAction( addFlowContainerAction );
}
tbm.add(new Separator());
tbm.add( getAction( AddFlowContainerAction.ADD_CONTAINER ) );
}
addFlowContainerAction.setEditorPart( editor );
}
At this point you should have a functioning tool bar button whose function is
identical to the palette entry that adds a flow container.
Another user interface option for a palette-less application is to add
commands to a menu. The AddFlowConterAction that we developed can also
be used for this purpose. As shown in Figure 4-3, a new Tools menu item has
been added which contains a submenu item that invokes our
AddFlowConterAction action.
The code fragment in Example 4-11 shows the changes you must make to
the contributeToMenu method of the LogicActionBarContributor class.
Figure 4-4 shows a simple Label figure when its cell editor is activated. This
simple example can be seen in the GEF logic example application. However,
direct edit can be used for much more visually complex EditParts in which
clicking in areas of your EditPart invokes different cell editors, including dialogs.
this is a label
a selected label
Figure 4-4 A Label figure showing the selected and cell editing states
The behavior of direct edit in your application is customizable. You specify what
cell editor to open, and your application can determine this dynamically
depending on criteria such as where the user clicked on your EditPart as well as
the current state of your model, and so on. Direct Edit supports activating cell
editors derived from org.eclipse.jface.viewers.CellEditor. Eclipse includes
several useful subclasses that provide cell editors for booleans, combo boxes,
text, and dialogs. By subclassing the DialogCellEditor, you have full flexibility to
create dialogs that allow for the display or editing of your EditPart’s properties.
This section outlines the steps you take to implement Direct Edit for one of your
application’s EditParts:
158 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
1. First modify your EditPart’s createEditPolicies to install a new edit policy with
the key EditPolicy.DIRECT_EDIT_ROLE, as shown in Example 4-12.
installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new
LabelDirectEditPolicy());
}
this is a label
a selected label
Tip: Normally during direct edit, your EditPart’s selection handles will be
shown, because the EditPart had to be selected in order to enter direct edit
mode. This may not be aesthetically desirable. There are a couple of
approaches to address this.
In the show() method of your DirectEditManager subclass you can save the
current selection state:
List savedSelection = source getSelectedEditParts();
Then override the bringDown() so that the saved selection state is restored
when the cell editor is closed.
160 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
4.2.10 Accessibility
Designing an accessible application is fundamentally about allowing for choice
and flexibility in both input and output methods. An accessible application may
receive input from the keyboard or serial port rather than the mouse. It needs to
support accessibility clients that use sound, speech synthesis, or screen
magnification to convey the output to the user.
GEF provides built-in support for accessibility, allowing you to create visual
editors that can be controlled with little or no mouse interaction. The editor and
palette are preconfigured to understand several keyboard navigation commands.
Examples are the ability to select objects and palette entries, cycle through an
object’s selection handles, and drag or resize an object using the arrow keys:
It supports the use of keyboard commands for users with limited dexterity.
It provides annotations for the selected part, such as name, description, help
text, and so on. that can be used by accessibility clients which may magnify or
speak these strings for the user.
It maps between the EditPart in focus and the accessibility client’s view of the
screen. This allows applications such as the Windows magnifier to track the
user’s actions within a GEF editor. This assists sight-impaired users.
GEF supports autoscrolling, which allows the editor to scroll automatically to
expose parts of a diagram that may be outside of the viewable area as the
user drags their mouse to the edge of the view.
Tip: WIndows users can experiment with the accessibility features in the logic
example by launching the Windows Magnifier application. Launch the
Windows Magnifier by selecting Programs -> Accessories -> Accessibility
->Magnifier
Accessible EditParts
Accessible EditParts are able to participate in the accessibility support that is
included in SWT and ultimately in the underlying operating system on which that
your GEF application is running. Accessibility client applications can listen for
selection changes in your GEF application and then obtain accessibility
information about the selected EditPart via the EditPartViewer.
AccessibleGraphicalEditPart
AccessibleGraphicalEditPart is an inner class of AbstractGraphicalEditPart that
provides GEF’s implementation for the underlying SWT accessibility API, defined
in org.eclipse.swt.accessibility package. This an abstract class, so EditParts
supporting accessibility must provide a concrete subclass that is returned when
the AbstractEditPart.getAccessibleEditPart() is called.
Accessible handles
Making a handle accessible requires that the handle provide a single point, in
absolute coordinates, at which it can be selected. Keyboard navigation can then
use this coordinate when selecting the handle, effectively simulating a mouse
click at that location. Accessible handles are obtained from the EditPolicies that
are responsible for handle management, such as subclasses of
SelectionEditPolicy.
162 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Accessible anchors
Accessible anchors work similarly to accessible handles. An EditPart provides an
implementation of the AccessibleAnchorProvider interface by implementing the
IAdaptable interface. The AccessibleAnchorProvider interface contains methods
to return a list of source and target anchor locations. These points will be used to
programmatically simulate a mouse event at that location. The targeting tool will
then provide the same targeting behavior and feedback as if a mouse was used.
To implement this capability in your own EditParts, you will need to traverse all
the ConnectionAnchor-derived children of your EditPart’s parent figure, and
return an appropriate point for each one.
AbstractTool
The AbstractTool class serves as the base class for contains the state machine
which interprets accessible actions such as translating arrow keys into drags,
and so on. Pressing the Enter key commits a drag
SelectionTool
When an edit part is selected, the SelectionTool’s accessibility support enables
the user to traverse the EditPart’s available selection handles, select one, and
perform drag operations all by using the keyboard. The keyboard commands
supported by this class are summarized in Table 4-2.
Up Arrow Drag up
ConnectionCreationTool
The keyboard handling in this class allows the user to indicate the start and end
of connections using the Enter key. The user can cycle through the available
anchor points of accessible EditParts by using the arrow keys. The tool will snap
the connection to the next available anchor.
SPACE Selects
PaletteViewerKeyHandler
This class, the keyhandler for the palette, supports keyboard commands in the
palette.It supports moving between palette entries and moving into and out of
palette drawers. The commands are summarized in Table 4-4.
Table 4-4 Arrow key bindings to palette navigation
Key Action
164 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
5
Note: The sample code we describe in this chapter is available as part of the
redbook additional material. See Appendix A, “Additional material” on
page 225 for details on how to obtain and work with the additional material.
The sample code for this chapter is provided as Eclipse projects that can be
imported into your Eclipse workbench.
Each major section of this chapter has a matching Eclipse project in the
additional material. Also, be sure to import the appropriate model project for
the editor project you want to work with. For example, to work with the
NetworkEditor project, you need to also import the NetworkEditorModel
project. Some of the sample projects in this chapter also expect that you have
the SAL330RWorkflowModel project in your workspace. You may have
created this project by working through the examples described in Chapter 1,
“Introduction to EMF” on page 3, or you can import this from our redbook
sample material.
Although we can generate EMF.Edit-based editors from EMF models using the
org.eclipse.emf.codegen.ecore plug-in, these editors use JFace viewers, such as
the TreeViewer to display model instances, and typically provide a view that has
a one-to-one correspondence with the model. Sometimes we may wish to create
editors where the view is more loosely coupled with the model. This is often the
case when we want to use a graphical notation that may hide some of the detail
of the underlying model objects, or may impose additional or a different structure
to the model, for visualization purposes.
We can think about using GEF and EMF together from two different
perspectives; using an EMF model within a GEF application, and augmenting
EMF.Edit-based editors using GEF. In this book, we focus on the first
perspective only, due to time constraints. The second approach deserves a book
of its own, as integrating an EMF.Edit-based editor with GEF provides its own
challenges. For an example of an application that uses GEF and EMF.Edit
together, take a look at the Jeez report designer, available from:
http://jeez.sourceforge.net
166 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
5.2 Using an EMF model within a GEF-based application
This section describes how to use model interfaces and implementations
generated from an EMF model as the model within a GEF-based application.
This is the approach that we have used for our sample application, described in
Chapter 7, “Implementing the sample” on page 203. We assume that you have
read Chapter 3, “Introduction to GEF” on page 87and that you have a basic
understanding of how an arbitrary (not necessarily EMF-based) model is usually
integrated into a GEF-based application. Because GEF can use almost any type
of model, integrating an EMF model into an editor is much the same as
integrating any other sort of model into a GEF-based application. When tying our
model into our editor, we can take advantage of mechanisms provided by EMF
for notification, reflection and serialization.
We use a simple application to illustrate our approach for using GEF and EMF
together. The application is an editor that allows us to define networks consisting
of nodes that may be linked together. We discuss how to implement an example
application based on the model shown in Figure 5-1.
name: EString
1 1
0..*
Nodes Node
Links 0..* Links
x: EInt
0..* y: EInt
Link
upstreamLinks
downstreamLinks 0..*
In general, if a model has a top-level element that contains all other model
objects, as is the case with the NetworkModel, and the WorkflowModel used for
our sample application, then the contents EditPart corresponds directly to that
container. For models that do not have a top-level container, you can think of the
contents EditPart as corresponding to the contents of a ResourceSet that
contains model objects, rather than corresponding directly to an EObject from
the model.
GEF provides two base implementations of EditPart that are used in graphical
viewers; AbstractGraphicalEditPart and AbstractConnectionEditPart. We can
subclass either of these classes for the EditParts that correspond to the objects
from our model. For the NetworkEditor example, we subclass
AbstractGraphicalEditPart as NetworkEditPart, our contents EditPart. As an
AbstractEditPart, NetworkEditPart has methods getModel() and setModel() for
getting and setting the corresponding model object with the EditPart. We
implement NetworkEditPart so that the Network associated with the part is
supplied to the constructor, as shown in Example 5-1.
Tip: When basing an editor on an EMF model, most of the objects returned by
the getModel() methods of the EditParts will be EObjects, however, you can
use any object as the model for an EditPart. This is one way to provide
EditParts that do not correspond directly to EObjects from the EMF model.
168 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Example 5-2 NetworkEditPart’s getModelChildren() method
How you choose to map the other objects from your model to EditParts will
depend on how each object is to be represented graphically. The graphical
representation for some objects may be simple, but for others, it may be
composed of multiple graphical components. These components will either be
implemented as child EditParts, or as children of the figure that represents the
model object in the view. An example of an appropriate use of child figures is to
represent object attributes with simple string or number values. An EditPart is
typically used to represent something with which the user interacts, which can be
selected and manipulated in its own right.
One approach for mapping from the model is to provide an EditPart for each
class, and then decide if you need any extra EditParts to represent its features.
For the NetworkEditor, we use an AbstractGraphicalEditPart for both the
Network and the Node class. Objects referenced by a containment reference are
represented as child EditParts, that is, NetworkNodeEditParts are children of
NetworkEditPart, and Links between Nodes are represented as LinkEditParts,
which subclass AbstractConnectionEditPart.
Figures
Each EditPart has a corresponding figure which is created and returned by the
EditPart’s createFigure() method. For each EditPart that you implement, you will
need to decide if you also need to provide a specialized figure to represent that
EditPart. EditParts that have simple graphical representations can often be
represented using one of the figures provided by Draw2D, such as a label or a
shape. We use a layer as the figure for NetworkEditPart in the NetworkEditor, as
Example 5-4 shows.
170 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
setBackgroundColor( ColorConstants.white );
setOpaque( false);
incomingConnectionAnchor = new EllipseAnchor(this);
outgoingConnectionAnchor = new EllipseAnchor(this);
label = new Label(" ");
add(label);
}
public void setId(String id){
label.setText(id);
}
...
}
Indirect mappings
There are few restrictions on how you may map your model objects to EditParts;
however, if you decide to map a single model object into multiple EditParts, you
will need to contain those parts by a (possibly invisible) parent EditPart that
corresponds to the model object. The reason why your model object can only
correspond to a single EditPart is because the viewer uses a java.util.Map to
map model objects to their corresponding EditParts, using the model object as
the key. If grouping the parts is not appropriate for the graphical representation
that you have chosen to use, this usually indicates a mismatch between the
model and the graphical representation, and you may need to reconsider the
representation or refactor your model.
Network
name: EString
1 Network
0..* Nodes
Node upstreamLinks
x: EInt
y: EInt 0..*
id: EString
downstreamLinks 0..*
172 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Modify GraphicalEditPartFactory so that it provides a String to the
LinkEditPart constructor when creating a new LinkEditPart.
Remove references to Link from the ModelCreationFactory, and use null
instead of a ModelCreationFactory in the NetworkPaletteRoot when creating
the tool entry for link creation.
Provide new implementations of getModelSourceConnections() and
getModelTargetConnections() in NetworkNodeEditPart, to return the Strings
that we use to identify the links. The String that we use to identify a Link is
constructed using toString() on the source and target Nodes. An example of
how we construct the identifying String is shown in Example 5-6.
Remember that you can use any Java object as the model for your EditParts.
You can create parts with more complex derivations from the model by providing
your own objects to represent those values. You should only use this technique
for transient or derived values, as any data that is not stored in the model will not
be persisted by default.
As we have seen in Example 5-6, you can then associate your custom objects
with their corresponding EditParts from within getModelChildren(),
getModelSourceConnections() or getModelTargetConnections(), depending on
whether you are using child or connection EditParts to represent those objects.
GEF assumes that all of the information that you need to store about the
diagrams that you are editing is represented in the model. For this reason, you
may also need to augment your model to include information such as
co-ordinates or dimensions.
For example, if you remove a reference to an object, the reference back from the
opposite will also be removed. If you delete an object that contains others, they
will also be deleted. This is because the EMF types are implemented to ensure
that the model remains consistent. When you are using EMF for the first time,
these behind the scenes changes are convenient, as they save you from having
to enforce these constraints manually; however, they can come as a surprise if
you are not aware of how the underlying objects behave.
174 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
If you are not expecting such changes, you can run into problems, for example
when undoing multiple changes in the sample application, if you delete an edge
and then the node it was connected to, the port that the edge was connected to
will be deleted with the node, so you need to store enough information about the
ports and the edge, so that the edge can find the right ports to reconnect to if
those deletions are undone. When you are working with a known model it is not
usually a problem to know how much information you need to store to facilitate
undo, however, if you are working with models generically using the reflective
API, the safest way to ensure that undo restores the model exactly how it was
before the change, is to snapshot the model each time a command executes.
You can either represent each snapshot as a separate serialization, or use diffs
to reconstruct model state.
Each EditPart adds itself to the adapters of any objects that it represents in its
activate() method, and removes itself from the adapters of those objects in its
deactivate() method. Example 5-7 shows how NetworkEditPart adds itself as an
adapter of the Network it represents in its activate() method.
176 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
If an EditPart does not use all of the features of the model object in its visual
representation, additional code could be added so that refreshVisuals() is only
called when features that are visualized change. If the visualization is made up of
many parts, you may want to provide methods that will only refresh specific parts
of the view, and use them from notifyChanged() instead of refreshVisuals().
The NetworkEditor class uses NetworkModelManager, creating one per file that
is open in the multi-page editor, and provides methods to get and save the
Network instance currently being edited via the NetworkModelManager.
Example 5-10 shows how the editor uses the NetworkModelManager instance to
get a network from a file opened in the editor. This method is called when the
editor is initialized from its init() method.
178 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
IStatus.ERROR,
NetworkEditorPlugin.PLUGIN_ID,
0,
"Error loading the worklow.",
null));
}
}
return network;
}
When the save() method is called, the NetworkModelManager calls the save()
method on the Resource containing the Network, and it is serialized into an XMI
document and saved to the path supplied.
Finally we hook our model and corresponding EditParts into the viewer when we
create the GraphicalViewer within the NetworkPage class, as shown in
Example 5-11.
We provide an example that generates skeletons for some classes that are used
in a GEF editor, from a model. We can use the technique described in this
section regardless of whether we take the approach described in 5.2, “Using an
EMF model within a GEF-based application” on page 167, or whether we are
using GEF to augment an EMF.Edit-based editor. You can flesh out the
generated code into an application as described in Chapter 3, “Introduction to
GEF” on page 87.
180 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
When developing your GEF-based application based on an EMF model, you will
notice that you are usually creating many similar classes, for example, often you
will create NodeEditParts for most of the classes in your model, perhaps using
ConnectionEditParts for some of them. Often you will use a custom figure for
your NodeEditParts. In the following example, we use JET templates to generate
EditParts and Figures from classes in our model. This is a very basic example, to
illustrate concepts. We do not provide a complete example due to time
constraints, as the templates required to generate more complete
implementations would be non-trivial. You would probably want to provide more
detail in the templates if you wanted to generate EditParts specific to your
application.
Refer to the JET Tutorial, Part one for an introduction to using JET. We use a
similar process to the example described to generate our skeleton EditParts and
Figures from the WorkflowModel. We take the following steps:
1. To begin with, we create a project, and add a JET Nature to the project from
the right-click context menu. This sets up the template directory.
2. In the template directory, we create a new file NodeEditPart.javajet.
3. We edit the NodeEditPart to create all of the required methods. We base the
content of the template on the NetworkNodeEditPart from the NetworkEditor
described in 5.2, “Using an EMF model within a GEF-based application” on
page 167. Example 5-12 shows an excerpt from the template. Our example
only really uses the name of the class so far to generate the skeleton,
however you could use methods on the EClass to get more detail. For
example, you might want to generate a skeleton notifyChanged() method with
a switch that selected from all of the features of the class.
public <%=name%>EditPart(<%=name%> o)
{
setModel(o);
}
public <%=name%> get<%=name%>() {
182 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
9. The result of calling generate() on the template is a string containing the text
generated from the template. In our simple example, we print this to
System.out, however if you were really generating code, you would want to
create a resource containing the contents of the String.
10.If you run the EditPartGenerator as a Java application, you will see the
resulting code printed to the console.
Using a similar approach to the EMF codegen for the model, edit, and editor
plug-ins, you could generate a generic graphical editor for any model using JET.
You would probably want to use your own GenModel to represent options such
as whether a class maps to a Node or Connection EditPart, whether it can
contain other nodes, and possibly also to specify the type of Figure used to
represent the class. You could then generate from instances of that model rather
than from the application model directly.
A workflow is a collection of tasks. Two types of task have been defined: simple
and complex.
Simple tasks
A simple task, as represented in Figure 6-1, has one input, one output, and one
fault output. A simple task does some sort of processing on the data given to it.
Two tasks are linked together with an edge. Data on the input is processed by
the task and made available on the output.
Label T
Task
In Out
Fault
Complex tasks
These are the complex tasks that we use in our sample application:
Compound task: A compound task is a kind of container. It follows the
composite pattern. It contains a containment reference to a workflow, which
can contain other simple or compound tasks.
Loop task: The loop task gives us the ability to iterate, as long the condition,
a predicate, is true.
Choice task: The choice task implements branching.
Transformation: Transformation has been introduced in order to enable a
task to do a combination of its multiple inputs.
188 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 6-2 shows the representation we use for complex tasks.
Task Task
Task
Choice
2
X=
Task
X=3
True Task
LoopTask
While Condition
T T
Label B A+B
Edge
An edge is used to link two tasks together. The output of the first task is
redirected to the input of the second task. An edge represents both control and
data flow. This means that once the first task has completed, the data made
available to and the control flow is transferred to the second task.
Concurrency Task
Task Task
Task
Variables are used to store data, usually coming from the output of a task, and to
hold the data until another task in the workflow makes use of it. Variables can be
seen as a way to separate the control flow from the data flow. Control goes to the
next task, while the data is held in the variable.
Task Task
Task Task
Figure 6-4 Data flow, variable, start, and stop tasks representation
190 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
6.2 Sample application design
The EMF and GEF sample application is an Eclipse plug-in. It is an editor, which
uses a property view to capture user input, and provides an outline view to help
the user to navigate more easily through the model. It also has multiple levels of
undo and redo, and provides several Eclipse plug-in extension points.
At the editor level, additional context dependent features have been defined:
Inplace editing for compound tasks:
– A task can be added to a compound task by means of drag and drop.
– A sub-workflow can be accessed through the compound task itself.
A separate editor tab for compound task editing:
– This provides an extended work space to work on a sub-workflow defined
in a compound task.
During edge creation, which is a link between two nodes, the link creation tool
is smart enough to recognize where the link can be connected:
– No loop on a single task is allowed.
– No link from a task of a sub-workflow to a task in the main workflow is
allowed.
– Link cursor is dynamically updated to represent the ability to connect to a
given endpoint.
Dynamic update of the main and properties view, to reflect the user action on
the outline view.
Drag and drop from the palette into the viewer.
Right-click contextual menu:
– For example, the Choice right-click menu contains undo, delete, add
condition to choice and save actions.
WorkflowElement
comment: EString
height: EInt
id [1..1]: EString
name: EString
width: EInt
x: EInt
y:EInt
WorkflowElement
The WorkflowElement class provides features common to all elements present in
a workflow. It is the common abstract supertype of the Workflow, WorkflowNode
Port, Edge and Comment classes. Table 6-1 provides a summary of the
WorkflowElement class.
192 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Table 6-1 WorkflowElement summary
Owned By
Inheritance
Constraints
Workflow
The Workflow class represents the description of a process. A Workflow contains
WorkflowNodes representing the steps in the process and Edges that represent
data and control flow between the nodes. A Workflow may also contain
comments that annotate the process described by the Workflow. Table 6-2
provides a summary of the Workflow class.
Inheritance WorkflowElement
Constraints
WorkflowNode
The class WorkflowNode represents a step in a Workflow. WorkflowNodes have
ports and may be connected to other WorkflowNodes via those ports. Table 6-3
provides a summary of the WorkflowNode class.
Inheritance WorkflowElement
Task
The class Task represents an action or unit of work within the Workflow.
The start and end icons in the documentation are different from the ones
currently implemented. In the application, the start task’s InputPort is replaced by
a green square and the end task’s OutputPort is replaced by a red square as
shown in Figure 6-6.
Inheritance WorkflowNode
Features
Constraints A Task has exactly one input port and one (non-fault) output port.
CompoundTask
The class CompoundTask is a Task that is defined by a sub-workflow. The
CompoundTask is complete when the sub-workflow that composes it is
complete. As CompoundTask inherits from Task, it has a single input port, output
port and fault port. When a CompoundTask begins, the inputs to the start nodes
of the sub-workflow are the inputs that are received at the input port of the
CompoundTask. Similarly, when the sub-workflow completes, the output data
from the finishing nodes of the sub-workflow provide the data that is output from
the output port of the CompoundTask. Figure 6-7 shows the visual
representation of a CompoundTask.
194 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Compound Task (= Nested Task)
Task Task
Inheritance Task
Constraints
LoopTask
The LoopTask represents actions that are repeated while a condition is true. The
actions that are repeated are contained by the sub-workflow of the LoopTask.
The data received at the Input of the LoopTask is provided as the input to the first
execution of the start node in the LoopTask’s sub-workflow. For each repetition
of the sub-workflow, the output from the previous execution becomes the input to
the current one. The output from the finishing nodes from the final execution of
the loop becomes the output of the LoopTask. Figure 6-8 shows the visual
representation of the LoopTask
LoopTask
While Condition
T T
Inheritance CompoundTask
Constraints
Choice
The class Choice represents a switch between alternative execution and data
flow paths. Data and control flow is only activated for Edges that source from
output ports of the Choice where the condition of the OutputPort evaluates to
true. Conditions must be unique in a Choice. The default name for a condition is
false.
The way conditions are represented in the workflow editor differ a little bit from
the present documentation, where a condition is drawn close to the
corresponding edge. In the editor, they are drawn inside the Choice visual itself.
A condition is placed in front of the corresponding ConditionalOutputPort port,
see Figure 6-9.
Task
Choice
2
X=
Task
X=3
True Task
Note: The little icon on the upper right corner of the Choice is used for adding
a condition to it. If you click it and nothing happens, check if the default false
condition is not already defined in the Choice.
196 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Table 6-7 Choice summary
Owned By
Inheritance WorkflowNode
Features
Constraints Choice has only one input port, but may have multiple output
ports. Non-fault OutputPorts owned by a Choice must be
ConditionalOutputPorts.
Transformation
The class Transformation takes multiple inputs and performs a transformation on
that data to produce a single result. Figure 6-10 shows the visual representation
of a Transformation.
Label B A+B
Inheritance WorkflowNode
Constraints Transformation has only one (non-fault) output port, but may
have multiple input ports
Edge
The class Edge represents a connection between an output port and an input
port, that is, a flow of data from the output of one WorkflowNode to the input of
another. Table 6-9 provides a summary of the Edge class.
Inheritance WorkflowElement
Constraints
Port
The abstract class Port is the common supertype for InputPort and
OutputPort.Table 6-10 provides a summary of the Port class
Inheritance WorkflowElement
Features
Constraints
InputPort
The class InputPort represents the Port at which data and control is received by
a node in the Workflow. Table 6-11 provides a summary of the InputPort class
Inheritance Port
Constraints
OutputPort
The class OutputPort represents the Port at which data and control is provided
by a WorkflowNode upon completion. Table 6-12 provides a summary of the
OutputPort class.
198 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Table 6-12 OutputPort summary
Owned By WorkflowNode
Inheritance Port
Constraints
FaultPort
FaultPort represents the output of a node that terminates under exceptional
conditions. The exception may be handled by another node if there is an Edge
linking the FaultPort with the InputPort of the handling WorkflowNode, otherwise
the Workflow containing the WorkflowNode that owns the FaultPort fails.
Table 6-13 provides a summary of the FaultPort class
Inheritance OutputPort
Features
Constraints
ConditionalOutputPort
The ConditionalOutputPort class represents an output of a Choice. For any
Choice, the conditions of its ConditionalOutputs determine the execution paths
that are taken upon evaluation of the Choice, as only Edges sourcing from a
ConditionalOutputPort where the condition evaluates to true will be activated
when the Choice completes. Table 6-14 provides a summary of the
ConditionalOutputPort class
Inheritance OutputPort
Constraints
Inheritance WorkflowElement
Features
Constraints
To run the workflow sample application, we need to first create a simple project,
than create a workflow file using the simple file creation wizard or the workflow
wizard. The workflow wizard provides workflow file extension handling and
control.
To create a workflow model with the simple file with the workflow wizard:
1. Click File -> New -> Other..., select Other -> Workflow, click Next.
2. Give the file name, for example My.workflow, click Finish.
In both cases, the workflow editor opens automatically on a new empty workflow.
Figure 6-11 shows a workflow model built using our redbook sample application.
200 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 6-11 Workflow sample application window
Notes:
1. If your starting point is the additional material, which contains the plug-in
code, you have to run the plug-in on a Run-time Workbench. Eclipse
automatically opens the editor on the workflow file, created during the
simple file creation process.
2. The Edge creation tool was considered as a composite of the model. It was
presented in a way similar to Tasks, with an Edges menu and an Edge
entry. Later on, it has been considered not as being a composite, but more
like a link between two composites. As such, it was moved to the top of the
menu, just after the Select and Marquee tools.
7.2 Architecture
In this section we describe the architecture of the sample application. We discuss
the techniques that we use to connect the EMF model with the editor framework.
In general, there will probably be fewer EditParts than there are object classes in
your model. For instance, in our sample application, we created the
WorkflowNodeEditPart to be the base class for model elements that have
connections. In the model, the ports are separate objects; but in the editor, we
chose to have the WorkflowNodeEditPart represent both the node and all its
ports. One criterion for making a determination about this mapping is to consider
how dynamic the visual behavior of a component needs to be.
For instance, if a model object needs a visual representation that can be moved,
resized, or can be individually added or deleted, then it may be a good candidate
for mapping it to its own EditPart. In our sample application, we designed the
EditPart class hierarchy shown in Figure 7-1.
204 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Figure 7-1 The sample application’s EditPart class hierarchy
EditPart functionality
The base class for our EditParts is the WorkflowElementEditPart class, which
provides the following three main functions needed by all its subclasses:
Register the EditPart a listener of its model:
The WorkflowElementEditPart class implements the interface that is used by
listeners of EMF's notification mechanism:
org.eclipse.emf.common.notify.Adapter
Tracking changes in the model is crucial to the EditPart's function (discussed
in detail in 7.2.2, “Tracking model events in the editor” on page 207). We
override the EditPart's life cycle methods activate() and deactivate() to
manage registration of the EditPart as an Adapter on its model.
Register the EditPart as a property source:
All EditParts inherit the IAdaptable interface from their AbstractEditPart base
class. This Eclipse interface supports a kind of multiple inheritance in which a
class can offer a proxy object to implement an interface requested by the
Eclipse framework. In our case we want all of our EditParts to provide an
implementation of the IPropertySource interface. By doing so the Eclipse
property page viewer will display the properties of our EditParts as they are
selected, and also allow them to be edited.
The implementation of the IPropertySource interface requires adding
Eclipse-specific code. While we could have extended our model's objects to
implement this interface directly, we felt that it would be preferable to keep the
Eclipse properties handling out of the model classes. Fortunately, the
EMF-generated classes provide a lot of metadata. This made it simple to
create a proxy class that provides a generic IPropertySource implementor,
WorkflowElementPropertySource, that can provide the requisite
IPropertyDescriptor's for any of our model's classes.
it = cls.getEAllAttributes().iterator();
while( it.hasNext() ) {
EAttributeattr = (EAttribute)it.next();
EDataTypetype = attr.getEAttributeType();
if( attr.isID() ) {
// shouldn't be editable
descriptors.add( new PropertyDescriptor( Integer.toString(
attr.getFeatureID() ),
attr.getName() ) );
}
else if( type.getInstanceClass() == String.class ) {
descriptors.add( new TextPropertyDescriptor( Integer.toString(
attr.getFeatureID() ),
attr.getName() ) );
}
else if( type.getInstanceClass() == boolean.class ) {
descriptors.add( new CheckboxPropertyDescriptor( Integer.toString(
attr.getFeatureID() ),
attr.getName() ) );
}
}
206 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Provide a default implementation of the refreshVisuals method:
Our implementation of this method handles changes to an EditPart's size and
position. In our sample application all of our EditParts can be moved, and
most of them can be resized. Therefore we provide this functionality in our
EditPart base class. The refreshVisuals() method simply applies the changed
position and extent values in the model to the EditPart's figure by updating its
layout constraint accordingly.
The WorkflowNodeEditPart derives from WorkflowElementEditPart and its
purpose is to support EditParts that have connections. This is the base
EditPart for EditParts that map to the model classes derived from the
WorkflowNode. This class implements GEF’s NodeEditPart interface, which
supports the connection feedback mechanism in GraphicalNodeEditPolicy.
This gives user feedback when Connections are initially connected and also if
they are later disconnected and reconnected.
This is the approach taken in the logic example that the GEF project provides. In
our case we are fortunate to have the full-featured notification mechanism that is
generated automatically in EMF classes. Recall that all the EditParts in our
sample are registered as adapters on the EMF model class(es) that they
represent. Each EditPart then provides an override of the notification method:
public void notifyChanged(Notification notification)
This method is called when any attribute of a model class is changed, or when a
child object is added or removed. The Notification class provides extensive
context describing the model change that has occurred. It includes information
such as:
The notifier, that is, which object's property has changed, or had a child
added or removed
The new and previous values of the target attribute
The data type information for the affected attribute
An identifier for the attribute
7.2.3 Refreshing
Once we have ensured that our EditParts are receiving all the notifications they
require to track their model, we then need to add code in our EditParts that acts
on this information. The implications of a model event to an EditPart can be
distilled into three general operations. The EditPart must interpret the notification
to decide which of these operations are required:
Updating the visual representation:
Underlying attributes of the model are often represented visually using
colored indicators, text annotations, or other graphical effects. For example,
in the sample application the name of an element is drawn inside a task
rectangle or on the title bar of a compound task. The ports change color to
indicate when a task is a start or finish task. The EditPart class provides the
method refreshVisuals(). Its implementation should provide a full update of
every graphical feature that is mapped to a model attribute. This method will
be called once when the EditPart is first activated so that the model and figure
are synchronized. Subsequently it is the responsibility of the application to
decide when a model change event requires an update to the visualization. It
is not required, or always advisable, to update the entire visualization if only a
208 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
single attribute has changed. This is a judgement call depending on the
complexity of the figure. With a detailed notification mechanism such as the
one provided by EMF, it is easy to determine exactly what has changed in the
model and decide whether to update details of the figure vs. calling
refreshVisuals() to update the entire figure.
Updating children:
In our sample application, our model has containment relationships that
are mirrored in our EditPart hierarchy, which is a common situation in GEF
applications. In our case the Workflow object may contain Task,
CompoundTasks, Choice and LoopTask objects, and so on. Our model
supports nesting, so that there are sub-workflows within CompoundTasks
and LoopTasks. The EditParts that represent these objects maintain a
similar structure. When a container EditPart is notified that a child model
element has been added or removed from its model, it must interact with
the GEF framework to synchronize by either adding or removing the
EditParts that represent the affected model children. GEF provides the
EditPart method refreshChildren() for this purpose. GEF provides the
implementation of this method. Our notification just needs to call it when
appropriate, as we do in the WorkflowNodeEditPart, shown in
Example 7-2:
switch( type ) {
case Notification.ADD:
case Notification.ADD_MANY:
if( notification.getNewValue() instanceof Edge ) {
if( notification.getNotifier() instanceof InputPort ) {
refreshTargetConnections();
}
else {
refreshSourceConnections();
}
}
else {
// listen for connection changes on the port
if( notification.getNewValue() instanceof Port ) {
Port port = (Port)notification.getNewValue();
port.eAdapters().add( this );
}
refreshChildren();
}
break;
it = getCompoundTask().getSubworkflow().getNodes().iterator();
while( it.hasNext() ) {
result.add( it.next() );
}
it = getCompoundTask().getSubworkflow().getComments().iterator();
while( it.hasNext() ) {
result.add( it.next() );
}
210 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
}
return result;
}
Notice that the Comment objects are also added here because they are
owned by the containing workflow (but are not part of the node hierarchy).
Updating connections
EditParts must notify GEF when they detect model changes indicating the
making and breaking of connections. The mechanism for this is very similar to
the mechanism described above for child additions and deletions. In the case
of our sample application we do this processing in the example Example 7-2
above. GEF provides two methods for refreshing connections, depending on
whether the affected EditPart is the source or target of the connection. The
methods are named EditPart.refreshSourceConnections and
EditPart.refreshTargetConnections. As it does when refreshing children, GEF
then asks our EditPart to provide a list of the connections for which our
EditPart is the source or target. For our model we simply need to return the
result of the WorkflowNode class's getOutputEdges or getInputEdges, which
conveniently return a List as required by GEF (see Example 7-4)
7.2.4 Factories
We use two factories in order to integrate between GEF and our EMF-based
model. First we need to use the EMF-generated factory, WorkflowFactory,
whenever we are creating new model objects. Typically this happens when a
creation command is either initialized by a policy or when the creation command
is executed. The factory is made available to these functions by setting it as the
factory for the creation tools created in the palette, as we do in the
WorkflowPaletteRoot class. The factory is set in the constructor for the
CreationToolEntry class. The class ModelCreationFactory, which implements
CreationFactory, is where the EMF factory is invoked. The getNewObject()
method in this class is where objects are actually created, as show in the snippet
in Example 7-5.
212 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
CompoundTask. In that case the parent workflow is obtained by calling the
CompoundTask’s getSubworkflow() method.
The one-to-one association names are singular and are always easily chosen.
For the one-to-many association names, we have an extra level of freedom,
because we can choose the Modeling or the Java naming convention to give it a
name. The main difference between the two is that Modeling uses singular while
Java uses plural.
Java coding conventions are strong. By respecting them, code is generally more
readable and understandable. It is not that those conventions are the only way or
the best way to go, but when you follow them, code become more easily familiar
to developers. Modeling uses different conventions, because the interests are
not the same.
Knowing that the convention choice has some effect on the generated code, the
result is that you rapidly end up with some sort of decision like, do we privilegize
the modeling or Java standpoint? When implementing the sample application,
with Java code as the only mapping, we decide to use the Java standpoint.
214 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
To help you visualize the potential implications of the choice of one view, we use
the Workflow to WorkflowNode association, called node(s) from Workflow to
WorkflowNode.
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public EList getNodes() {
if (nodes == null) {
nodes = new EObjectContainmentWithInverseEList(
WorkflowNode.class,
this,
WorkflowPackage.WORKFLOW__NODES,
WorkflowPackage.WORKFLOW_NODE__WORKFLOW);
}
return nodes;
}
}
In XML, if you look at a file containing the result of a workflow serialization, you
will see an extra ‘s’ at the end of each node entity, which is unusual for an XML
entity. You can look at any Ecore file for more examples of eClassifiers or
eReferences XML entities. See Example 7-8.
216 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
The model, as designed, tell us that the association between tasks and ports are
inherited from the WorkflowNode, where two one-to-many associations are
defined between WorkflowNode and InputPort on one side and WorkflowNode
and OutputPort on the other side. The reference named outputs contains all the
OutputPort, all the ConditionalOutputPort, and the default FaultPort ports.
With those elements in mind, we can see that we have a problem to reduce the
visibility of the inherited methods for outputs and inputs relationships. The
model provides methods dealing with a collection, where methods dealing only
with one object should be defined.
<interface>
WorkflowNode
isIsStart()
isIsFinish()
getOutputs()
getInputs()
connectTo(in WorkflowNode)
setFault(in FaultPort)
getFault()
addFault()
addInput()
addInput(in InputPort)
addOutput()
addOutput(in OutputPort)
getInputEdges()
getOutputEdges()
getOutput(in String)
getInput(in String)
createDefaultPort()
Init()
getInput()
getOutput()
<interface>
CompoundTask
<interface>
LoopTask
218 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Note: At the moment, the design can be split into three simple implementation
lanes. The first is one input and one output; the second is with many inputs
and one output; and the third is the one input and many outputs. Once we
have more than one class in a lane, basically two classes with no direct
inheritance in between, it would be nice to create an abstract intermediate
class in order to provide one-one, many-one, or one-many behavior.
The Java code for the connectTo method is found in the WorkflowImpl class as
shown in Example 7-9:
if (edge == null) {
// No connection found
WorkflowFactory workflowFactory = WorkflowModelManager.getFactory();
// Create an edge
edge = workflowFactory.createEdge();
// Add the edge to the workflow, to benefit
The EMF eOpposite attribute of the eReferences entity is very helpful when
making a connection between two ports with an edge, because for all the
references with an eOpposite attribute, EMF keeps track of the changes on the
other side of the reference. This means, for example, that if you add an Edge to
an OutputPort:
outputPort.getEdges().add(edge);
Then EMF will do the opposite setup automatically and transparently for you:
edge.setSource(outputPort);
When creating an association in the EMF Class Diagram in the UML plug-in.
The Navigable checkbox (see Figure 1-12 on page 20) drives the access to the
association features. Once an association is navigable on both ends, a change
on one side is reflected on the other side, because the eReferences’ eOpposite
attributes are used.
Note: The source code of our sample application is available along with this
book. See Appendix A, “Additional material” on page 225 for details on
obtaining the sample code. We suggest that you study the code for
implementation details. We tried to document it as often as possible. Because
of that, we are not going to reproduce a lot of example code within this section.
Instead, we give you an overview of the implementation and explain what and
why implementation decisions were made.
220 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Our multi-page editor consists of only two pages One page is for editing a whole
workflow and the second page is for editing compound tasks of the same
workflow. This provides an alternative way of editing compound tasks because
in-place editing might not be suitable in all situations.
We decided not to share a single EditDomain within our editor pages. Our
reasons were clear, because our editor would only have two pages. The
functionality of each page might be similar, but the concept of each page is
different.
On one page you should be able to edit the workflow, and on the second page
you should edit the content of compound tasks. We thought that changes on one
page should not affect the other page except for updating the UI. Thus, we
wanted to have completely different undo/redo stacks for each page.
If you want all your pages to be using the same undo/redo stack
(CommandStack), you will have to share the EditDomain between your pages.
Because of some current limitations in GEF, you have to think about solutions for
the following issues:
If you share an EditDomain within several pages, you have to remember that
an EditDomain can have several EditPartViewers but only one palette.
Thus, you might consider a concept of sharing one palette or attaching a new
palette with every page switch.
We decided to implement this directly into the multi-page editor because we think
it might be less expensive to calculate this once for all pages rather than letting
each page calculate this itself and asking each page.
The concept is basically the same as we would have used for a simple editor.
The editor listens for CommandStack changes and updates its dirty state
according to the state of the CommandStack.
7.4.4 Actions
Our multi-page editor provides one ActionRegistry for the whole editor. Thus, all
actions are available on all pages. We don’t need to have different actions for
different pages. Again the concept is similar to a single editor. Actions are
registered to an ActionRegistry.
The ActionBarContributor
The GEF ActionBarContributor is not able to provide support for tracking page
changes in a multi-page editor. If you need this, you can either implement the
functionality from org.eclipse.ui.part.MultiPageEditorActionBarContributor or
inherit from this class. But if you inherit from this class, you don’t have the action
handling support provided by the GEF ActionBarContributor.
222 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
We are handling this with a workaround. Our undoable property sheet page root
entry gets a delegating CommandStack. The DelegatingCommandStack is a
CommandStack that delegates work to a current CommandStack, which can be
changed. Thus, we only need to update the DelegatingCommandStack when the
current page changes, and this can be easily done from within our multi-page
editor.
Select the Additional materials and open the directory that corresponds with
the redbook form number, SG246302.
226 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
the NetworkEditor project, you must also import the NetworkEditorModel
project.
Some of the sample projects in this chapter also expect that you have the
SAL330RWorkflowModel project in your workspace. You may have created
this project by working through the examples described in Chapter 1,
“Introduction to EMF” on page 3, or you can import this from our redbook
sample material. If you import the SAL330RWorkflowModel project, you will
need to set up the Eclipse environment as described in “Eclipse Classpath
settings for sample projects” on page 227. If you create the
SAL330RWorkflowModel project, you will need to make sure that you have
created the Java build path variables described in 1.3.9, “Compiling the code”
on page 27, otherwise you may get classpath errors when importing the
sample projects.
4. sample-application:
This folder contains code for the sample application described in Chapter 7,
“Implementing the sample” on page 203. We provide two zip files:
– workflow-sample-plugins-1.0.0.zip:
This is our redbook sample application packaged as a plug-in for install in
Eclipse. To us this plug-in, unzip the archive to the directory where you
installed Eclipse. You can then experiment with the functions of our
sample workflow editor by create a new file resource with a .workflow
extension.
– workflow-sample-src-1.0.0.zip:
This is the zipped source code for our redbook sample application. When
you unzip this archive, two Eclipse project folders are created with projects
that can be imported into Eclipse: SAL330RGEFDemoApplication, which
is the sample editor code; and SAL330RWorkflowModel, which is the
associated workflow model used by our redbook sample editor. If you
import the sample projects, you will need to set up the Eclipse
environment as described in “Eclipse Classpath settings for sample
projects” on page 227.
228 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Abbreviations and acronyms
ADL Architectural Description
Language
DTD Document Type Definition
EAI Enterprise Application
Integration
EJB Enterprise Java Bean
EMF Eclipse Modeling Framework
FAQ Frequently Asked Questions
GEF Graphical Editing Framework
HTML HyperText Markup Language
HTTP HyperText Transfer Protocol
IBM International Business
Machines Corporation
IDE Integrated Development
Environment
IDL Interface Definition Language
ITSO International Technical
Support Organization
JET Java Emitter Templates
MDA Model Driven Architecture
MDE Model Driven Environment
MOF Meta Object Facility
MVC model-view-controller
NLS National Language Support
NLS National Language Support
OMG Object Modelling Group
OVID Object View and Interaction
Diagram
SWT Standard Widget Toolkit
SWT Standard Widget Toolkit
URI Universal Resource Identifier
XSD XML Schema definition
The publications listed in this section are considered particularly suitable for a
more detailed discussion of the topics covered in this redbook.
Other publications
These publications are also relevant as further information sources:
The Java Developer’s Guide to Eclipse, Sherry Shavor et al, Addison Wesley,
ISBN: 0-321-15964-0
Eclipse Modeling Framework, Frank Budinsky et al, Addison Wesley, ISBN:
0131425420
Online resources
These Web sites and URLs are also relevant as further information sources:
eclipse.org main page:
http://www.eclipse.org
Eclipse Modeling Framework home page:
http://www.eclipse.org/emf
Graphical Editing Framework home page:
http://www.eclipse.org/gef
Omondo EclipseUML page:
http://www.eclipseuml.com
Object, view and interaction design:
http://www-3.ibm.com/ibm/easy/eou_ext.nsf/Publish/589
Eclipse XML Schema Infoset Model:
http://www.eclipse.org/xsd
XML Metadata Interchange:
http://www.omg.org/technology/documents/formal/xmi.htm
JET tutorial part 1:
http://eclipse.org/articles/Article-JET/jet_tutorial1.html
232 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling
Index
connection routers
A Draw2D 101
accessibility 161
connections 105
accessible 161
decorating 148
ActionRegistry 123
Draw2D 102
actions 123, 222
GEF 105
adapters 119, 130
constraints 39
anchor points
container role 108
Draw2D 101
control flow 189
ANT 90
controllers 166–167
architecture
coordinate system 94
sample 204
coordinate systems 141
association 19, 23, 32
CreateRequests 106
attribute 23, 44
cursor 94–95, 150
creation 14
attributes 39, 48
D
data flow 189
B dataflow 30
borders
datatypes 39
Draw2D 97
decorating connections 148
descriptors 47
C design 187
cache 46 sample 191
canvas 93, 95 DiagramConnection 36
Choice 196 DiagramModel 36
choice task 188 DiagramNode 36, 46
class 46, 48 direct edit 158
class diagram 12 direct edit role 108
code generation 4, 23, 27, 29, 31, 47, 51, 58 DirectEditPolicy 150
JET 79 Directory 80
commands 109, 113, 150, 174, 212 dirty state 222
keyboard 161 displaying properties 174
CommandStack 113–114, 222 documentation
Comment 67, 192, 200 EMF 6
complex task drag and drop 143
tasks Draw2D 87–88, 95, 136
complex 188 anchor points 101
complex tasks 188 borders 97
component role 107 connection routers 101
compound task 188 connections 102
CompoundTask 21, 77, 194 event dispatcher 96
ConditionalOutputPort 39, 199 figures 93, 95
connection role 108 introduction 93
234 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
feedback 150 getter 46, 61
field 46 Graphical Editing Framework See GEF
figures 93, 95, 105, 141, 170 graphical node role 108
GEF 105 GraphicalNodeEditPolicy 151
root 96 GraphicalViewer 109, 117
FlowLayoutEditPolicy 152 graphics 94
focus 94–95 graphics context 94, 96
framework 4, 45 GroupRequests 107
GEF 103
frameworks 165
Freeform 99
H
handles 152
freeform 141
help
EMF 6
G
GEF 9, 87–88, 103, 165
accessibility 161
I
IAdaptable 119
applications 89
IContentOutlinePage 130
commands 150
IDE 90
direct edit 158
IDL 4
drag and drop 143
implementation
EditParts 103
sample 203
examples 139
inheritance 18
feedback techniques 150
InputPort 21, 31, 198
GraphicalViewer 109
installation
introduction 87
EMF 6
printing 144
instances 178
requests 106
create 64
resource management 149
interface 13, 46, 51
techniques 143
interface design 13
tools 144
ItemProvider 47
using an EMF model 166–167
ItemProviderAdaptor 47
viewer 153
ItemProviderAdaptorFactory 47
zooming 145
ItemProviders 47
generalization 18
generation
XML Schema 53 J
GenModel 45, 47–48, 73 Java 4, 46, 173
editor 50 Java Emitter Templates See JET
properties 47, 49 Java interface annotation 22
class-level 55 Java project 11
DataType-level 57 Java Server Pages See JSP
feature-level 56 JavaDoc 204
operations 57 JET 29, 45, 79, 165, 180
package-level 54 code generation 79
parameters 57 templates 80
top-level 52 JFace 47, 109, 166
GenModel properties JSP 82
JET-related 79
Index 235
K WorkFlowElement 35
key strokes 134 Model Driven Architecture See MDA
keyboard commands 161 model plug-in 45, 61, 64
KeyHandlers 134 modeling
Java interface annotation 22
MOF 4
L mouse 93
labels 190
mouse events 94
layers 96, 111, 142
multi-page editor 220
Draw2D 99
MVC 166
layout 135
layout manager 95
layout role 108 N
LayoutEditPolicy 151 namespace 42
LayoutManagers naming convention 214
Draw2D 98 National Language Support See NLS
lightweight system 94 NLS 53
LightweightSystem class 95 Notification 175
listeners 95 notifications 47, 208
LocationRequests 107 Notifier 175
Locator
Draw2D 100
O
loop task 188 object 48
LoopTask 39, 195 Object Management Group See OMG
Object, View and Interaction Diagram See OVID
M OMG 4
mapping 5, 42, 168, 204 Omondo 10
mappings operations 39, 46, 48, 57
indirect 171 outline view 130, 223
MDA 4–5 OutputPort 21, 31, 198
menu 47 OVID 28
Meta Object Facility See MOF
meta-data 4
P
meta-model 4 packages 45
methods 46, 95 painting 93
model 4, 9, 47, 61, 135, 204, 214 palette 120, 153, 223
constraints 216 sticky tools 144
creation 10, 30 palette-less applications 153
Ecore 38 PaletteRoot 120
editing 174 PaletteViewer 120, 223
elements 35 panes 96
EMF 29 parameters 57
events 207 patterns 56
instances 64, 66, 178 Singleton 56
migrating 40 Stateful 56
properties 10 plug-in 11, 47, 49, 61
serialization 64 development 10
Workflow 30, 33 Draw2D 93
workflow 9, 192
236 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
edit 47, 63 setter 46, 61
editor 45, 47 Shape 96
installation 10 signatures 39, 46
model 45, 61, 64 simple task 188
XSD 43, 53, 70, 75 Singleton 56
polymorphism 78 skeletons 39
Port 31, 33, 192, 198 SKU 44
presentation 47 start task
printing 144 tasks
process 193 start 190
project 10 Stateful 56
creation 10 Struts 90
properties 15, 129 supertypes 46
display 174 SWT 92–93, 109, 130, 143
properties view 222
T
R Task 31, 33, 36, 67, 194
Rational Rose 10, 40 task 188
Redbooks Web site 232 tasks 30
Contact us xii choice 188
references 48, 214 compound 188
refresh 208 end 190
rendering 94 loop 188
requests 106 simple 188
requirements 187 techniques 143
Resource 55 tooltips 95
resource implementation 75 tracking 207
ResourceFactory 55 Transformation 197
Resources 69 transformation task
resources 149 tasks
ResourceSets 69 transformation 188
root figure 96 transparency 95
RootEditPart 110, 117, 140 tree container role 108
TreeViewer 47
type 43, 46
S types 36, 39
sample 30, 187
architecture 204
design 191 U
implementation 203 UML 9–10
requirements 188 update manager
ScaledGraphics 145 Draw2D 97
SDK 88 URI 42
SelectionEditPolicy 152
SelectionHandlesEditPolicy 152
SelectionTool 144
V
variables 190
serialization 4, 43, 64, 66, 74
viewers 47, 153
customize 70
attaching 117
examples 67
Index 237
visibility 95
W
Web services 5
widgets 96
wizard 47, 51
Workflow 9, 19, 32, 48, 192–193
instance 67
model 9, 17, 30, 33
serialization 67
workflow 188
workflow model 192
Workflow.genmodel 27
WorkflowDiagram 36, 46
WorkflowDiagramPackage 36
WorkflowDiagramTask 36, 46
WorkflowElement 13, 47, 192
model 35
WorkflowElementEditPart 205
WorkflowModel 12, 30, 36, 39, 45, 47–48, 192, 214
WorkflowNode 21, 192–193
WorkflowNodes 193
Worklow 21
X
XMI 10, 31–32, 34, 38, 48–49, 55, 76
XMI serialization 76
XMIResource 69, 75
XML 4, 44, 55, 70
XML Metadata Interchange See XMI
XML Schema 30, 40, 44, 53, 70
XML serialization 77–78
XMLHelper 77–78
XMLLoad 78
XMLMap 74
XMLResource 75
XMLSave 78
XSD 4, 43
plug-in 43, 53, 70, 75
Z
zooming 145
ZoomManager 145
238 Eclipse Development using the Graphical Editing Framework and the Eclipse Modeling Framework
Eclipse Development using the Graphical Editing Framework & the Eclipse Modeling Framework
(0.5” spine)
0.475”<->0.875”
250 <-> 459 pages
Back cover ®
Eclipse Development
using the Graphical Editing Framework
and the Eclipse Modeling Framework