The OCL Abstract Syntax Model is defined by the OCL Language 2.4 specification . We will not attempt to describe this model, here. However, the Eclipse implementation of OCL defines some extensions to this model that provide additional services. The most important of these is support for the Visitor design pattern.
All of the metaclasses in the Abstract Syntax Model (nodes in the AST) that can be visited implement the
Visitable
interface. It define a single operation
accept(Visitor)
. This method delegates to the appropriate
visitXyz(Xyz)
method of the
Visitor
. The direct implementors of the
Visitable
interface are the
OCLExpression
and those metaclasses of the
Expressions
package that do not conform to
OCLExpression
:
Variable
CollectionLiteralPart
TupleLiteralPart
ExpressionInOCL
This last is not defined in the
Expressions
package
because it pertains to the placement of OCL in
Constraint
elements in models.
The OCL parser, internally, defines a few implementations of visitors, including
a
ValidationVisitor
for validating OCL expressions and an
EvaluationVisitor
for evaluating OCL expressions.
The best way to implement a visitor is to extend the
AbstractVisitor
class. It provides a
result
variable of the generic
type parameter type
T
to store the result computed
by the visitor (optional) and a convenient pattern of selective method overrides
to process only those nodes of interest for the task at hand.
The
AbstractVisitor
provides implementations of all
of the
visitXyz()
interface methods that simply
return the current
result
value. Furthermore, for
any internal nodes of the syntax tree (such as
OperationCallExp
and
IfExp
),
the
visitXyz()
methods recursively visit the child
nodes, feeding the results of those descents into a
handleXyz()
method that the subclass can override to compute some result from the child
results.
Thus, a subclass needs only to selectively override the default implementations
of
visitXyz()
methods for leaf tree nodes and
handleXyz()
methods for non-leaves. For example, to
find all variables that are declared but never used:
OCLExpression<Classifier> expr = getExpression(); // hypothetical source of an expression
Set<Variable<Classifier, Parameter>> variables = expr.accept(
new AbstractVisitor<Set<Variable<Classifier, Parameter>>,
Classifier, Operation, Property, EnumerationLiteral,
Parameter, State, CallOperationAction, SendSignalAction, Constraint>(
new HashSet<Variable<Classifier, Parameter>>()) { // initialize the result
@Override
protected Set<Variable<Classifier, Parameter>> handleVariable(
Variable<Classifier, Parameter> variable,
Set<Variable<Classifier, Parameter>> initResult) {
result.add(variable);
return result;
}
@Override
public Set<Variable<Classifier, Parameter>> visitVariableExp(
VariableExp<Classifier, Parameter> v) {
result.remove(v.getReferredVariable());
return result;
}
}});
Set<String> varNames = new HashSet<String>();
for (Variable<?, ?> next : variables) {
varNames.add(next.getName());
}
System.out.println("Unused variables: + " varNames);
In Ecore models, a reference may have defined an
opposite
reference, usually owned by the class that is
the type of the forward reference. An opposite reference has several, often undesirable or even
prohibitive, implications on the class owning it:
A getter and, for settable features with upper multiplicity 1, a setter will be added, requiring the class to know the class owning the forward reference. This would create cyclic component references if the two classes lived in different components and would therefore not be possible.
The default serialization format and usually the storage format for non-default model stores changes to include the opposite reference.
Yet, particularly for expressing constraints over the instance models it is often instrumental
to be able to navigate such forward references also in reverse. The
OppositePropertyCallExp
class which inherits from
NavigationCallExp
and is sibling of
PropertyCallExp
allows for this reverse navigation in OCL. It
points to the forward reference, and its semantics are to navigate this reference in reverse.
To allow for convenient creation of such expressions in the OCL concrete syntax, the standard
property call syntax, such as
self.x
can be used, where
x
is not the name of a forward reference on
self
's class but rather
an annotated name on a reference using
self
's class or any of its
base classes as its type. To enable this feature, use the special environment factory class
EcoreEnvironmentFactoryWithHiddenOpposites
when initializing the
OCL environment, e.g., by passing such an object to the
OCL.newInstance(...)
method.
The name for the reverse navigation can be specified by an
EAnnotation
with
source
http://schema.omg.org/spec/MOF/2.0/emof.xml
and with
details key
Property.oppositeRoleName
. The details value
contains the name by which the “hidden” opposite can be referred to in OCL
expressions.
If OCL delegates are to be used, the standard EPackage annotations with
invocationDelegate
,
settingDelegate
and
validationDelegate
details for the
http://www.eclipse.org/emf/2002/Ecore
source must be augmented as shown by a further
hiddenOpposites
detail for the
http://www.eclipse.org/emf/2002/Ecore/OCL
source.
This additional annotation causes the
EnvironmentFactory
functionality for the EPackage to be provided by an instance of the
EcoreEnvironmentFactoryWithHiddenOpposites
class
which uses the
DefaultOppositeEndFinder
class will be used for finding
and navigating the hidden opposites. More substantial customisation is possible by specifying
an
environmentFactoryClass
detail with the fully qualified
name of a derived
EcoreEnvironmentFactory
that
provides a constructor taking an
EPackage.Registry
argument.
Note, that the class specified must be visible by your Ecore model’s bundle.