In
Parsing Constraints, we saw how to use
the
OCL
Facade to parse a textual OCL constraint or query expressions to give its
ExpressionInOCL
compiled representation. Parsing constraints is interesting,
but evaluating them using the
Query
API is much more useful.
The
Query
class wraps the minimal
ExpressionInOCL
parse result to provide evaluation capabilities.
The
Query
encapsulates an
EvaluationEnvironment
providing the run-time values of context variables to the OCL interpreter. These
context variables are set and retrieved using the following methods:
add(TypedElement, Object)
: adds a TypedElement-to-value binding
replace(TypedElement, Object)
: replaces an existing binding
remove(TypedElement)
: removes a binding
getValueOf(TypedElement)
: obtains a binding value
.bq The Ecore/UML binding of Eclipse OCL used String rather than TypedElement to support name-to-value bindings. The use of TypedElement rather than String avoids whereby the same name refers to multiple Variables depending on context. .p
The context variables of primary interest are
self
and, in operation constraints, the variables corresponding to its parameters.
An important consideration for OCL evaluation is the
allInstances()
operation, which obtains the entire
extent of a classifier. For data types, this is a simple problem: the extent
of an
Enumeration
is well defined and the extents of
other kinds of
DataType
s are undefined. For
Class
extents, the
OCL
handle references a
ModelManager
that provides access to the user’s models. The default
PivotModelManager
lazily computes the extent of a class from the EMF
ResourceSet
containing the context element of the evaluation.
So, after optionally setting values of context variables (other than
self
; the
Query
takes care
of this) and an extent map, simply construct a query and use it to evaluate
the expression or check the constraint:
The example above uses
evaluateUnboxed()
so that the return value is unboxed and so compatible with the Classic Ecore/UML OCL binding.
The Pivot binding of OCL supports three distinct Java representations.
boxed for internal use
unboxed for traditional API compatibility
Ecore for Ecore API compatibility
OCL | Unboxed | Ecore | Boxed |
---|---|---|---|
Boolean | Boolean | Boolean | Boolean |
String | String | String | String |
Integer | Integer/Long/BigDecimal | Integer/Long/BigDecimal | IntegerValue |
Real | Float/Double | Float/Double | RealValue |
Object | EObject | EObject | EObject |
Type | EClassifier | EClassifier | TypeValue |
null | null | null | null |
invalid | InvalidValueException | InvalidValueException | InvalidValueException |
Collection | Collection | EList | CollectionValue |
Bag | Bag | EList | BagValue |
Sequence | List | EList | SequenceValue |
OrderedSet | OrderedSet | EList | OrderedSetValue |
Set | Set | EList | SetValue |
The boxed representation is used wherever the Java semantics of
Object.equals(Object)
is different
to the OCL semantics of
OclAny::_'='(OclAny)
.
The unboxed representation is used when a similar representation to the Ecore/UML binding is required.
The Ecore representation is used for all interchange with Ecore EStructuralFeature values or EOperation arguments and returns.
One of the advantages of the
Query
API is that a
query’s evaluation environment can be reused for multiple evaluations, as
above. The extent of any classifier is only computed once. For convenience,
however, in situations where only a single evaluation is required, the
OCL
class provides shortcuts:
The
Query
API also provides methods that work on
multiple elements. The first example, above, could be written more succinctly as: