OCL Abstract Syntax Model

OCL Abstract Syntax Model

The OCL Abstract Syntax Model is defined by the OCL Language 2.3 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.

The Visitable and Visitor Interfaces

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.

Implementing a Visitor

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);

The OppositePropertyCallExp Extension

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.