The OCLinEcore tutorial shows how
to install OCL and the additional Editors and Examples
to use the OCLinEcore editor
to edit Ecore meta-models
to enrich Ecore meta-models with OCL invariants, bodies and values
to use embedded OCL for validation of models
to use the OCL Console to practice evaluation of OCL
to generate Java code for Ecore that uses the embedded OCL
The Working with Classic OCL tutorial shows how
the OCL Parser may be invoked from Java
the OCL evaluator may be invoked from Java
This tutorial has been refreshed for Eclipse 2023-12; Eclipse 4.30, EMF 2.36, OCL 6.19.0.
The graphical styling of some screenshots may be slightly out of date.
In this example you will
Create an Ecore model using the OCLinEcore text editor
Create a dynamic instance of that Ecore model
Enrich the Ecore model with OCL using the OCLinEcore text editor
Validate the model and observe the OCL enrichments
Use the Interactive OCL Console to execute the OCL enrichments
The above is all performed without generating any Java code; the models exploit EMF’s dynamic capabilities and the OCL integration.
You may then
Create an Ecore genmodel
Generate Java code for the Ecore model that invokes the OCL expressions.
See the OCL Debugger tutorial for debugging See the Code Generator tutorial for Java code generation
This tutorial assumes that the reader is familiar with generating models using EMF. The reader is referred to Generating an EMF Model.
Other references:
The Object Constraint Language: Getting Your Models Ready for MDA. Jos Warmer and Anneke Kleppe. (Addison-Wesley Object Technology)
Please see the Instructions for installing the OCL Editors.
The editor currently provides syntax and semantic validation. It does not yet apply all the well-formedness validation rules, so some problems may be unreported. This is work in progress. Sometimes spurious errors are displayed, which may go away with a Save, but may require an editor close and reopen.
There are many different (compatible) ways to create and edit Ecore models.
An Ecore Model may be created from an XSD schema file
An Ecore Model may be created from a Rose model file
An Ecore Model may be created from annotated Java file
The Sample Ecore Editor provides tree editing
The Xcore Editor provides text editing
The Ecore Tools project provides graphical editing
Papyrus provides UML editing that may be converted to Ecore
Here we introduce the OCLinEcore editor that provides text editing, which is appropriate when a non-trivial amount of OCL enrichment is required.
All the above approaches update a *.ecore file, so the user is free to choose whichever editing approach is best suited for the planned changes.
We will first create a new project for this example; so invoke File->New->Project... (left-click the File menu, then left-click New, then left-click Project...).
In the New Project dialog left-click to expand Eclipse Modeling Framework, then left-click to select Empty EMF Project.
Left-click on Next and in the New Empty EMF Project dialog type Tutorial as the project name.
Left-click on Finish.
An OCL project, analogously to many other Eclipse projects, has an OCL nature and an OCL builder that compile OCL resources so that any problems are shown as error/warning/info markers in the Problems View.
The OCL nature may be added to our project by right-clicking on the Tutorial project and then invoking Configure->Convert to OCL Project from the context menu.
The OCL nature may be removed by Configure->Unconfigure OCL.
Adding the OCL nature makes the project also-an-OCL-project. It does not displace any other natures that the project already has.
It is not necessary to add the OCL nature, but if you omit this step, every time you open an OCL editor, a pop-up will offer to add the OCL nature for you.
The EMF and UML2 projects do not provide corresponding natures to validate *.ecore or *.uml files. This would leave any OCL embedded in Ecore or UML unchecked and the user unaware of problems. The OCL nature therefore validates *.ecore or *.uml files as well as OCL files by default. This default can be adjusted by editing the value of the enabledExtensions in the .project file.
If you cannot see files such as .project, open the View menu by clicking the top right tool bar icon of the Package Explorer and enable visibility of .resources in the Filters.
We will now create a new model for this example.
First right-click on the model folder in the Tutorial project to define the target folder and pop-up the context-sensitive menu. Select New->Other... then select the OCLinEcore Ecore File from the OCL category. An almost empty file is created with a little example content.
The alternative OCLinEcore Text File creates a *.oclinecore text file which preserves whitespace and comments more faithfully but which must be converted to a *.ecore file for many modeling purposes.
Left-click Next and enter Tutorial.ecore as the file name.
Left-click Finish to open up an editor. The example content demonstrates
the nesting of attributes or operations or invariants within classes within packages
use of OCL to define the body of an operation
the syntax for mutually opposite properties
use of OCL to define an invariant and a custom error message
Close the editor by left-clicking the cross on the editor tab.
You can see the normal Ecore view of the file by right-clicking on the Tutorial.ecore file in the Package Explorer to pop-up the context-sensitive menu and select Open With->Sample Ecore Model Editor.
Close the editor by left-clicking the cross on the editor tab.
We will now open the Ecore model using the OCLinEcore text editor and provide some initial content.
Right-click on the Tutorial.ecore file to pop-up the context-sensitive menu and select Open With->OCLinEcore Editor.
Now follow the following procedure to cut and paste the following text into the editor.
select all existing content (e.g. Ctrl-A)
delete all (e.g. Ctrl-X)
select and copy the text (e.g Ctrl-A and Ctrl-C) from the browser
paste (e.g Ctrl-V) in the original editor
save the contents (e.g. Ctrl-S)
The syntax is defined in OCLinEcore. It emulates OMG specifications with ‘name : type[multiplicity] { properties }’.
import
associates an alias with an external EPackage.
package
introduces an EPackage with name, nsPrefix and nsURI.
class
introduces an EClass with name and optional superclasses.
attribute
introduces a property with a datatype type (an EAttribute).
property
introduces a property with a class type (an EReference).
#
introduces an opposite role name.
_'xxx'
escapes an awkward or reserved word identifier.
The import URI is the URI of a Package, so in the example the
http://www.eclipse.org/emf/2002/Ecore
is the URI of the model,
#
is the fragment separator and
/
is the path to
the Package at the root of the XMI document.
Completion assist (Ctrl Space) may be used for syntax assistance.
Format (Ctrl-Shift F) may be used to auto-format a selected range.
In order to discover a syntax for which completion assist is insufficient, you may use the Sample Ecore Editor on a test file to create the kind of Ecore element that you require, and then open the test file with the OCLinEcore editor to see the corresponding textual syntax.
The example meta-model models a library with members and books and loans of books to members. It may be viewed graphically using the Ecore Tools editor (not part of this tutorial).
Note that this diagram is an Ecore Diagram rather than a UML Diagram and so the default multiplicities for attributes is Ecore’s [0..1] rather than OCLinEcore’s and UML’s [1..1].
Note also that the OCL types
String
and
Integer
map
to
EString
and
EBigInteger
in Ecore.
At this point a corresponding EMF tutorial would show how to generate Java code for the meta-model and an editor for the meta-model. Here we are concerned with modeling, so we will continue with the models alone.
In the editor view, double-click on
Library to select it and then right-click to show the context-sensitive menu
and then left-click on
Create Dynamic Instance... to start to create a
new Dynamic Model with
Library
at its root.
Creating a Dynamic Instance requires a valid *.ecore file to exist. It does not work when editing *.oclinecore files.
In the Create Dynamic Instance dialog select Tutorial/model as the parent folder and enter Tutorial.xmi as the file name for the dynamic model instance and left-click Finish.
The model is automatically opened for editing.
If it is does not open with the Sample Reflective Ecore Model Editor, close the editor and open explicitly using Open With->Sample Reflective Ecore Model Editor. This gives a tree-like presentation of the model. The properties of each node can be seen in the Properties View.
If the Properties View is not visible, right-click within the editor and left-click on Show Properties View.
Select the
Library
and use the Properties View to give it a name such as
lib
.
From the right-button menu for
Library
use
New Child->Books Book twice,
use
New Child->Loans Loan once and
New Child->Members Member three times
to populate the model with two books, one loan and three members.
Left-click to select each of the Books and Members in turn and enter a name
such as
b1
or
m2
using the Properties View. Specify that b1
has one copy and that b2 has 2 copies.
The books and members now have distinct titles in the outline.
When you left-click to select the Loan and edit its Book and Member attributes,
the associated pull-down has meaningful entries. Specify that the Loan is for
b2
by
m3
.
The configuration so far is simple, three members, two books and one loan. We can
validate that this by right-clicking on the
Library
node, and left-clicking
to
Validate
Library
and all its children.
Beware. If you have installed WST for web support, or M2E for Maven support, there is a rival Validate with a checkbox near the bottom of the menu. It does not appear to do anything useful for EMF-based models. The correct Validate is the one just above Live Validation.
Since the model is so simple, it is difficult to have anything wrong; most of the illegal modeling options such as a Loan composing rather than referencing a Book are prevented by the Editor’s enforcement of the meta-model.
If you have an error at this point, a Details button will lead you to some diagnostics that may clarify the problem. Pasting the following XMI into Tutorial.xmi should also resolve an entry problem.
<?xml version="1.0" encoding="ASCII"?>
<tut:Library xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tut="http://www.eclipse.org/mdt/ocl/oclinecore/tutorial"
xsi:schemaLocation="http://www.eclipse.org/mdt/ocl/oclinecore/tutorial Tutorial.ecore"
name="lib">
<books name="b1" copies="1"/>
<books name="b2" copies="2"/>
<loans book="//@books.1" member="//@members.2"/>
<members name="m1"/>
<members name="m2"/>
<members name="m3"/>
</tut:Library>
We will now create two further identical loans of
b2
by
m3
. This
may conveniently be performed by left-clicking to select the existing loan,
typing Ctrl-C to copy it, left-clicking to select the
Library
as the new parent,
then typing Ctrl-V to paste it on the library. Repeat so that there are three
identical loans.
Validating the library should still be successful, although it is clearly wrong for
the two copies of
b2
to participate in three loans.
The semantic constraint that a book cannot be borrowed more times than there are copies of the book is a simple example of a constraint that cannot be expressed by simple multiplicities; a more powerful capability is required that may potentially require evaluation of functions of almost arbitrary complexity. The Object Constraint Language provides this capability.
The constraint can be realized as an invariant on a book that specifies that that (the size of the (selection of loans involving the book)) is less than or equal to (the number of copies of the book).
invariant SufficientCopies:
library.loans->select(book=self)->size() <= copies;
In more detail:
an invariant is defined whose name is
SufficientCopies
within the invariant on a Book,
self
is the instance of
Book
being validated.
library.loans
, which is short for
self.library.loans
, navigates to the library and then to all loans in the library.
->select(...)
is a collection iteration over the loans. It selects each loan for which its argument expression is true
book=self
, which is short for
aLoan : Loan | aLoan.book = self
, uses the
aLoan
iterator over each loan to select those for which the book is the book being validated
->size()
is a collection operation that just counts the number of selected loans
<= copies
, which is short for
<= self.copies
converts the count to
true
if it is consistent, or
false
if inconsistent.
It used to be necessary to close an XML editor such as Tutorial.xmi before modifying its meta-model since a wide variety of unpleasant errors could occur if the meta-model changes after the model is loaded. This EMF limitation now appears to have been resolved. It is nonetheless prudent to do so.
Add the invariant shown below to the meta-model.
The required semantic is expressed by the
SufficientCopies
invariant constraint for a Book.
For a valid model the SufficientCopies invariant must always be true.
If you reopen the
Tutorial.xmi editor and invoke
Validate for the
Library
,
you will now get a validation error. Left click
Details for details.
The
Details identifies that the
SufficientCopies
invariant is not
satisfied for the
b2
book.
Alternatively you may enable Live Validation so that validation happens automatically with error icons and hover text identifying problems.
If you now change the first loan so that
b1
is borrowed and then validate
again, the problem is resolved. It is all right for
m3
to borrow
the one copy of
b1
and the two copies of
b2
.
Before introducing a further constraint of no duplicate loans, we will show how OCL expressions can be exercised. OCL is a very powerful compact language; the example hides a loop over all the loans. More complex examples may easily involve three or four levels of hidden loops on a single line, but may equally easily have simple errors. It is therefore helpful to simplify expressions and use helper operations and properties to modularise them. These may then be exercised using the OCL Console or debugged using the OCL Debugger.
The OCL Console supports interactive execution of an OCL expression in the context of a model instance.
To make the OCL Console visible, first make the Console view visible by Window->Show View->Console. Then left click on the Open Console pull down near top right of the console toolbar and left click on Interactive Xtext OCL.
Alternatively, you can just invoke OCL->Show Xtext OCL Console from the right button menu within the Sample Ecore Editor or Sample Reflective Ecore Editor.
The Xtext OCL console is new Xtext-based functionality that uses the Pivot binding. It is faster, and more compliant with the OCL specification, than the OCL console that uses the LPG parser and Ecore binding.
The Interactive Xtext OCL console comprises two main text panes. The upper pane displays results. The lower pane supports entry of queries.
Left-click to select the
Library
in the
Tutorial.xmi editor as the context
for a query. The title of the console view shows the that the
Library
has been selected as OCL’s
self
.
Type
books
followed by a new line into the lower pane of the console.
The result of evaluating this query for the
Library
is shown.
Substantial OCL queries spanning many lines may be entered and so the cursor up and cursor down keys move across lines. If you want to access an earlier query, you may use the Page Up or Page Down keys to save typing them again.
You can examine the execution of the query within our example invariant by selecting each of the books
in turn and executing
library.loans->select(book=self)
, to see that
b1
has one Loan and
b2
two.
We will now introduce some helper attributes and operations to make
the OCL clearer and provide a richer meta-model API.
Close the
Tutorial.xmi editor and modify the meta-model to include
the derived
loans
property and the helper operation
isAvailable()
.
Simplify the invariant to use the derived property.
Note that the derived property must also be volatile to avoid problems when a model is loaded but has no content.
Reopen Tutorial.xmi and select Book b2 so that the derived loans property is visible in the Properties view.
Beware. The Properties View provides a ‘pleasanter’ presentation by capitalizing property names. This can be confusing when debugging.
The helper operation can be evaluated in the
Console view by selecting book
b2
and typing
isAvailable()
for execution. (It is no longer necessary to close and reopen the Console after a metamodel change.)
We will now add further helpers and constraints to enforce an at most two loans per member policy and to require loans to be unique.
It is prudent, but perhaps no longer necessary, to close Tutorial.xmi and the Console while changing the meta-model.
The additional
books
property may be evaluated in the OCL
Console to show which books each member has on loan. The property may also be seen in the
Properties view.
Select the library again and invoke Validate from the right button menu. There are now two validation failures.
We have shown how OCL may be used to enrich Ecore meta-models, how model instances can be created and validated and how expressions can be evaluated, all without generating any Java code. The OCLinEcore support provides the requisite Ecore EAnnotations to ensure that the dynamic execution of EMF activates an OCL interpreter where OCL functionality is required.
Exactly the same facilities are available if you generate Java code for the Ecore model and as a result you gain some speed benefits. You have two choices for the realization of the OCL embedded with Ecore models.
Generate Java code within the *Impl classes
Delete for interpretation at run-time
The first newer approach takes longer to generate the model since the OCL is compiled to Java incurring parser overheads at compile-time. However the OCL runs approximately five times faster and has fewer dependencies since no parsing is necessary at run-time. This approach is described In the Code Generation Tutorial.
The second older approach embeds the OCL in the Java as unparsed stings so that parsing occurs overheads at run-time. The generated Java is smaller since the executable Java for OCL is larger than uncompiled strings.
We continue here with the older approach for which delegation to yyje interpreter is selected as a root Window->Preferences->OCL relaization preference.
Generating Java code is exactly the same as for any other EMF project. (Prior to EMF 2.8, there was one important difference; you must explicitly set Operation Reflection to true. The default for this changed to true in EMF 2.8.)
Select the Tutorial.ecore file and invoke New->Other... from the right button menu and select Eclipse Modeling Framework and EMF Generator Model.
Select Next.
Select Next.
Select Next.
Select Load and Next.
Select Finish.
The Tutorial.genmodel editor opens.
Most of the default settings are suitable. The one that may not be is highlighted. Select the root Tutorial and scroll down the Properties view and set Operation Reflection to true if it is not already true. (As from EMF 2.8 the default is true.)
You may now invoke Generate Model Code from the right button menu of either Tutorial to generate Java models that invoke OCL.
You can check that the OCL appears in your Java by looking at tutorial.util.TutorialValidator.java where you’ll find the OCL expression as a String awaiting compilation at run-time, and the validate invocation that triggers that compilation and execution.
protected static final String MEMBER__AT_MOST_TWO_LOANS__EEXPRESSION = "\n" +
"\t\t\tloans->size() <= 2";
public boolean validateMember_AtMostTwoLoans(Member member, DiagnosticChain
diagnostics, Map<Object, Object> context) {
return
validate
(TutorialPackage.Literals.MEMBER,
member,
diagnostics,
context,
"http://www.eclipse.org/emf/2002/Ecore/OCL",
"AtMostTwoLoans",
MEMBER__AT_MOST_TWO_LOANS__EEXPRESSION,
Diagnostic.ERROR,
DIAGNOSTIC_SOURCE,
0);
}
Similarly in BookImpl you will find the declaration of a cached delegate and the dynamic invocation that provokes the first time compilation.
protected static final EOperation.Internal.InvocationDelegate
IS_AVAILABLE__EINVOCATION_DELEGATE = ((EOperation.Internal)
TutorialPackage.Literals.BOOK___IS_AVAILABLE).getInvocationDelegate();
public boolean isAvailable() {
try {
return (Boolean)
IS_AVAILABLE__EINVOCATION_DELEGATE.dynamicInvoke(this, null);
}
catch (InvocationTargetException ite) {
throw new WrappedException(ite);
}
}
The OCL expression for the invocation delegate may be found in TutorialPackageImpl.createPivotAnnotations().
addAnnotation
(getBook__IsAvailable(),
source,
new String[] {
"body", "loans->size() < copies"
});
The invariants we have used so far do not contribute to the class API.
If you want to have fine grain control of which validations are performed, perhaps because in some incremental context not all are appropriate, you may use the operation form of an invariant.
class Book
{
operation sufficientCopies(diagnostics : ecore::EDiagnosticChain,
context : ecore::EMap<ecore::EJavaObject,ecore::EJavaObject>) : Boolean
{
body: library.loans->select(book=self)->size() <= copies;
}
attribute name : String;
attribute copies : Integer;
property library#books : Library;
}
Note that the operation must have a Boolean return (true for valid) and diagnostics and context arguments.
To illustrate how to work with the OCL and Ecore as models we have
Created an Ecore meta-model using the OCLinEcore text editor
Created a dynamic model instance from that meta-model
Enriched the meta-model with embedded OCL
Used the embedded OCL while validating the model
Queried the model using the Interactive OCL Console.
Evaluated OCL embedded in the meta-model in the Console.
To use OCL and Ecore as generated Java models we have
Generated Java that exploits the embedded OCL.