Evaluating Constraints and Queries

Evaluating Constraints and Queries

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 OCL Query

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 DataTypes 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:

[Text for cut and paste]

Object representations

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.

Multiple Evaluations

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:

[Text for cut and paste]

Succint Evaluations

The Query API also provides methods that work on multiple elements. The first example, above, could be written more succinctly as:

[Text for cut and paste]