The EMF Query Framework provides support for specifying matching conditions using Object Constraint Language (OCL) version 2.0.
This tutorial will illustrate a variety of ways to query models using OCL.
This tutorial assumes that the reader is familiar with the Eclipse extension point architecture. There is an abundance of on-line help in Eclipse for those unfamiliar with extension points.
To see the complete source code for the examples shown in this tutorial, install the OCL Query Example plug-in into your workspace.
Other references:
The simplest kind of query is one that searches for a single kind of model
element using a single OCL condition. This OCL condition may be arbitrarily
complex, but is still a single expression. This case is best handled using the
IOclQueryHelper
API for its convenience.
For example, to find writers that have written books in more than 2 categories:
Resource myResource = ... // get the resource EObject root = (EObject) myResource.getContents().get(0); IOclQueryHelper helper = new OclQueryHelper(); Set results = helper.executeQueryUsingOclFilter( root, LibraryPackage.eINSTANCE.getWriter(), "self.books->collect(b : Book | b.category)->asSet()->size() > 2", new NullProgressMonitor()); // do something with the results selectInEditor(results);
In the snippet above, the Writer
metaclass is declared as the
context type for the OCL condition; the context type is the type of the special
self
variable.
The previous section illustrated an query that specifies an OCL condition with
a context type. This is somewhat restrictive, in that only elements of one
type (and its sub-types) can be retrieved by such a query. But what if we want
to find instances of various metaclasses that are not related to a single common
ancestor metaclass but have similar features to query? An example is the
name
feature in the Library metamodel: both the
Library
and Writer
metaclasses define a name, but
they have no common ancestor with the same feature.
The solution is these cases is a context-free OCL condition. It allows us to specify a condition which may be applicable to one or more context types, and the query engine will take care of determining to which metaclasses is does apply. In effect, any metaclass on which the OCL expression will parse can be selected by the query.
The following query will retrieve any library or writer whose name is 'Bob'. This is probably unlikely to match a library, but we can try it anyway:
Resource myResource = ... // get the resource EObject root = (EObject) myResource.getContents().get(0); IOclQueryHelper helper = new OclQueryHelper(); Set results = helper.executeQueryUsingOclFilter( root, null, // this indicates a context-free condition "self.name = 'Bob'", new NullProgressMonitor()); // do something with the results selectInEditor(results);
Most real applications can expect to have more complex queries than we have seen so far. For example, how can we with one OCL condition expression find all of the writers and books that have a specific name/title? We could try a disjunction ("or") with type testing and casting:
Set results = helper.executeQueryUsingOclFilter( root, null, // this indicates a context-free condition "(self.oclIsKindOf(Writer) and self.oclAsType(Writer).name = 'Bob')" + "or (self.oclIsKindOf(Book) and self.oclAsType(Book).title = 'Bob')", new NullProgressMonitor());but we find that this does not actually work. We get a "java.lang.IllegalArgumentException: Conformance Type Mismatch. No common supertype: (Writer), (Book)" error in trying to parse this expression against either the
Writer
or Book
type, because OCL
knows that neither of these types can be cast to the other.
The solution is to break the query into two distinct OCL condition expressions.
Because the
IOclQueryHelper
API does not accept more than one condition, we use the more flexible (and
complex) EMF Query Framework. We will construct a
SELECT
query with two
OclConstraintCondition
s
in a disjunction:
Resource myResource = ... // get the resource EObject root = (EObject) myResource.getContents().get(0); IEStructuralFeatureValueGetter getter = new EStructuralFeatureValueGetter(); // create the two OCL conditions Condition cond1 = new OclConstraintCondition( "self.name = 'Bob'", LibraryPackage.eINSTANCE.getWriter(), getter); Condition cond2 = new OclConstraintCondition( "self.title = 'Bob'", LibraryPackage.eINSTANCE.getBook(), getter); // combine them into a SELECT query statement SELECT statement = new SELECT( SELECT.UNBOUNDED, false, new FROM(Collections.singleton(root)), new WHERE(cond1.OR(cond2)), new NullProgressMonitor()); // execute the query IQueryResult results = statement.execute(); // do something with the results selectInEditor(results);
This gives us the result that we were looking for. Using this query framework, we can even mix OCL conditions with other kinds of conditions (implemented in Java code).
To illustrate how to query EMF models using OCL, we
Copyright (c) 2000,2005 IBM Corporation and others. All Rights Reserved.