This tutorial illustrates the various services provided by the Classic Eclipse OCL implementation.
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 Interpreter Example plug-in into your workspace.
Other references:
For an environment in which to test the OCL expressions that you will create in this tutorial, install the Library Metamodel example.
OCL 2.0 specification.
The first responsibility of the OCL interpreter is to parse OCL expressions. One of the purposes of parsing an expression is to validate it: if it can be parsed, it is well-formed (the parser automatically validates the expression against the semantic well-formedness rules).
The main entrypoint into the OCL API is the 
					OCL class. An 
					OCL provides an autonomous OCL parsing environment.  It tracks all constraints that are parsed in this environment, including the definitions of additional operations and attributes. The 
					OCL.newInstance() factory method is used to create a new OCL with an 
					EnvironmentFactory that provides the binding to a particular metamodel (Ecore or UML).  In this tutorial, we will use the Ecore binding.
				
To parse a query expression, we will use the 
					
						OCLHelper
					 object, which provides convenient operations for parsing queries and constraints
					(intended for processing constraints embedded in models).
				
boolean valid;
OCLExpression<EClassifier> query = null;
try {
    // create an OCL instance for Ecore
    OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> ocl;
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);
    
    // create an OCL helper object
    OCLHelper<EClassifier, ?, ?, Constraint> helper = ocl.createOCLHelper();
    
    // set the OCL context classifier
    helper.setContext(EXTLibraryPackage.Literals.WRITER);
    
    query = helper.createQuery("self.books->collect(b : Book | b.category)->asSet()");
    
    // record success
    valid = true;
} catch (ParserException e) {
    // record failure to parse
    valid = false;
    System.err.println(e.getLocalizedMessage());
}
The example above parses an expression that computes the distinct categories
					of 
					Book s associated with a 
					Writer.  The possible
					reasons why it would fail to parse (in which case a
					ParserException is thrown) include:
				
syntactical problems: misplaced or missing constructs such as closing
parentheses, variable declarations, type expressions, etc.
semantic problems: unknown attributes or operations of the context
type or referenced types, unknown packages, classes, etc.
OCL is primarily intended for the specification of 
					constraint s.  Unlike
					queries, there are a variety of different kinds of constraints used in different
					places in a model.  These include classifier invariants, operation constraints,
					and attribute derivation constraints.  The 
					OCLHelper
					can parse these for us.
				
Let’s imagine the confusion that arises from a library that has more than one book of the same title (we are not intending to model copies). We will create an invariant constraint for @Book@s stipulating that this is not permitted:
Constraint invariant = null;
try {
    // set the OCL context classifier
    helper.setContext(EXTLibraryPackage.Literals.LIBRARY);
    
    invariant = helper.createInvariant(
        "Library.allInstances()->forAll(b1, b2 | b1 <> b2 implies b1.title <> b2.title)");
} catch (ParserException e) {
    // record failure to parse
    System.err.println(e.getLocalizedMessage());
}
Parsing constraints differs from parsing query expressions because they have
					additional well-formedness rules that the parser checks.  For example, an
					invariant constraint must be boolean-valued, an attribute derivation constraint
					must conform to the type of the attribute, and such constructs as @pre 
					and 
					oclIsNew() may only be used in operation post-condition constraints.
				
More interesting than parsing an OCL expression or constraint is evaluating it
					on some object.  The 
					
						Query
					 
					interface provides two methods for evaluating expressions. Queries are
					constructed by factory methods on the 
					OCL class.
				
		evaluates the expression on the specified object, returning the result.
							The caller is expected to know the result type, which could be a
							primitive, 
					EObject, or a collection.  There
							are variants of this method for evaluation of the query on multiple
							objects and on no object at all (for queries that require no "self"
							context).
				
This method evaluates a special kind of OCL expression called a constraint. Constraints are distinguished from other OCL queries by having a boolean value; thus, they can be used to implement invariant or pre/post-condition constraints. There are variants for checking multiple objects and for selecting/rejecting elements of a list that satisfy the constraint.
In order to support the 
					allInstances() operation on OCL types,
					the 
					OCL API provides the
				
					
						setExtentMap(Map<CLS, ? extends Set<? extends E>> extentMap)
					 
					method.  This assigns a mapping of classes (in the Ecore binding,
					EClass es) to the sets of their instances.  By default,
					the 
					OCL provides a dynamic map that computes the
					extents on demand from the contents of a 
					Resource.
					An alternative extent map can be
					found in 
					
						org.eclipse.ocl.ecore.opposites.ExtentMap
					 .
					We will use a custom extent map in evaluating a query expression that finds
					books that have the same title as a designated book:
				
// create an extent map
Map<EClass, Set<? extends EObject>> extents = new HashMap<EClass, Set<? extends EObject>>();
Set<Book> books = new HashSet<Book>();
extents.put(EXTLibraryPackage.Literals.BOOK, books);
// tell the OCL environment what our classifier extents are
ocl.setExtentMap(extents);
Library library = EXTLibraryFactory.eINSTANCE.createLibrary();
Book myBook = EXTLibraryFactory.eINSTANCE.createBook();
myBook.setTitle("David Copperfield");
books.add(myBook);
// this book is in our library
library.add(myBook);
Writer dickens = EXTLibraryFactory.eINSTANCE.createWriter();
dickens.setName("Charles Dickens");
Book aBook = EXTLibraryFactory.eINSTANCE.createBook();
aBook.setTitle("The Pickwick Papers");
aBook.setCategory(BookCategory.MYSTERY_LITERAL);
books.add(aBook);
aBook = EXTLibraryFactory.eINSTANCE.createBook();
aBook.setTitle("David Copperfield");
aBook.setCategory(BookCategory.BIOGRAPHY_LITERAL);  // not actually, of course!
books.add(aBook);
aBook = EXTLibraryFactory.eINSTANCE.createBook();
aBook.setTitle("Nicholas Nickleby");
aBook.setCategory(BookCategory.BIOGRAPHY_LITERAL);  // not really
books.add(aBook);
dickens.addAll(books);  // Dickens wrote these books
library.addAll(books);  // and they are all in our library
// use the query expression parsed before to create a Query
Query<EClassifier, EClass, EObject> eval = ocl.createQuery(query);
Collection<?> result = (Collection<?>) eval.evaluate(dickens);
System.out.println(result);
The same 
					Query API is used to check constraints.
					Using the 
					library and 
					extents map from above and the
					constraint parsed previously:
				
eval = ocl.createQuery(constraint);
boolean ok = eval.check(library);
System.out.println(ok);
The 
					OCLHelper interface provides an operation that
					computes content-assist proposals in an abstract form, as
					
						Choice
					 s.
					An application’s UI can then convert these to JFace’s
					ICompletionProposal type.
				
Obtaining completion choices consists of supplying a partial OCL expression
					(up to the cursor location in the UI editor) to the
					
						OCLHelper::getSyntaxHelp(ConstraintKind, String)
					, java.lang.String) 
					method.  This method requires a 
					ConstraintKind
					enumeration indicating the type of constraint that is to be parsed (some OCL
					constructs are restricted in the kinds of constraints in which they may be used).
				
helper.setContext(EXTLibraryPackage.Literals.BOOK);
List<Choice> choices = helper.getSyntaxHelp(
    ConstraintKind.INVARIANT,
    "Book.allInstances()->excluding(self).");
for (Choice next : choices) {
    switch (next.getKind()) {
    case OPERATION:
    case SIGNAL:
        // the description is already complete
        System.out.println(next.getDescription());
    case PROPERTY:
    case ENUMERATION_LITERAL:
    case VARIABLE:
        System.out.println(next.getName() + " : " + next.getDescription();
        break;
    default:
        System.out.println(next.getName());
        break;
    }
}
A sample of the output looks like:
author : Writer
title : String
oclIsKindOf(typespec : OclType)
oclAsType(typespec : OclType) : T
...
The choices also provide the model element that they represent, from which a more sophisticated application can construct appropriate JFace completions, including context information, documentation, etc.
The OCL Interpreter models the OCL language using EMF’s Ecore with support for Java-style generic types. The bindings of this generic Abstract Syntax Model for Ecore and for UML substitutes these metamodels' constructs for the generic type parameters, plugging in the definitions of the “classifier”, “operation”, “constraint”, etc. constructs of the OCL vocabulary. These bindings, then, support persistence in or as an adjunct to Ecore and UML models.
For processing the abstract syntax tree (AST) parsed from OCL text, the API
					supplies a 
					
						Visitor
					 
					interface.  By implementing this interface (or extending the
					
						AbstractVisitor
					 
					class, which is recommended), we can walk the AST of an OCL expression to
					transform it in some way.
					This is exactly what the interpreter, itself, does to evaluate an
					expression:  it just walks the expression using an evaluation visitor.  For
					example, we can count the number times that a specific attribute is
					referenced in an expression:
				
helper.setContext(EXTLibraryPackage.Literals.BOOK);
OCLExpression<EClassifier> query = helper.parseQuery(
    "Book.allInstances()->select(b : Book | b <> self and b.title = self.title)");
AttributeCounter visitor = new AttributeCounter(
    EXTLibraryPackage.Literals.BOOK__TITLE);
System.out.println(
    "Number of accesses to the 'Book::title' attribute: " + query.accept(visitor));
where the visitor is defined thus:
class AttributeCounter extends AbstractVisitor<Integer,
		EClassifier, EOperation, EStructuralFeature, EEnumLiteral,
		EParameter, EObject, EObject, EObject, Constraint> {
    private final EAttribute attribute;
    
    AttributeCounter(EAttribute attribute) {
        super(0);  // initialize the result of the AST visitiation to zero
        this.attribute = attribute;
    }
    
    protected Integer handlePropertyCallExp(PropertyCallExp<EClassifier, EStructuralFeature> callExp,
    		Integer sourceResult, List<Integer> sourceResults) {
        if (callExp.getReferredProperty() == attribute) {
            // count one
            result++;
        }
        
        return result;
    }
}
Because the OCL expression AST is a graph of EMF objects, we can serialize it
					to an XMI file and deserialize it again later.  To save our example expression,
					we start over by initializing our 
					OCL instance with
					a resource in which it will persist the environment and in which we will
					persist the parsed expression.  The key is in the persistence of the
					environment:  OCL defines a variety of classes on the fly by template
					instantiation.  These include collection types, tuple types, and message types.
					Other elements needing to be persisted are additional operations and attributes
					that may be defined in the local environment.
				
// create a resource in which to store our parsed OCL expressions and constraints
Resource res = resourceSet.createResource(
    URI.createPlatformResourceURI("/MyProject/myOcl.xmi", true);
// initialize a new OCL environment, persisted in this resource
ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE, res);
// for the new OCL environment, create a new helper
helper = OCL.createOCLHelper();
helper.setContext(EXTLibraryPackage.Literals.BOOK);
// try a very simple expression
OCLExpression<EClassifier> query = helper.createQuery("self.title");
// store our query in this resource.  All of its necessary environment has
// already been stored, so we insert the query as the first resource root
res.getContents().add(0, query);
res.save(Collections.emptyMap());
res.unload();
To load a saved OCL expression is just as easy:
Resource res = resourceSet.getResource(
    URI.createPlatformResourceURI("/MyProject/myOcl.xmi", true),
    true;
@SuppressWarnings("unchecked")
OCLExpression<EClassifier> query = (OCLExpression<EClassifier>) res.getContents().get(0);
System.out.println(ocl.evaluate(myBook, query));
In the snippet above, we used the 
					OCL's convenience
					method for a one-shot evaluation of a query.  Looking at the contents of the
					XMI document that we saved, we see that the 
					self
					variable declaration is not owned by the query expression, but is, rather,
					free-standing.  The 
					ExpressionInOCL metaclass solves
					this problem by providing properties that contain context variable declarations,
					including 
					self and (in the context of operations)
					operation parameters. 
				
<?xml version="1.0" encoding="ASCII"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:ocl.ecore="http://www.eclipse.org/ocl/1.1.0/Ecore">
  <ocl.ecore:PropertyCallExp xmi:id="_897fVPfmEduCQ48h829a5g">
    <eType xsi:type="ocl.ecore:PrimitiveType" href="http://www.eclipse.org/ocl/1.1.0/oclstdlib.ecore#/0/String"/>
    <source xsi:type="ocl.ecore:VariableExp" xmi:id="_897fVvfmEduCQ48h829a5g" name="self" referredVariable="_897fUvfmEduCQ48h829a5g">
      <eType xsi:type="ecore:EClass" href="http://www.org.eclipse/ocl/examples/library/extlibrary.ecore/1.0.0#//Book"/>
    </source>
    <referredProperty xsi:type="ecore:EAttribute" href="http://www.org.eclipse/ocl/examples/library/extlibrary.ecore/1.0.0#//Book/title"/>
  </ocl.ecore:PropertyCallExp>
  <ocl.ecore:Variable xmi:id="_897fUvfmEduCQ48h829a5g" name="self">
    <eType xsi:type="ecore:EClass" href="http://www.org.eclipse/ocl/examples/library/extlibrary.ecore/1.0.0#//Book"/>
  </ocl.ecore:Variable>
</xmi:XMI>
To illustrate how to work with the OCL API, we
Parsed and validated OCL expressions and constraints.
Evaluated OCL query expressions and constraints.
Obtained content-assist suggestions for the completion of OCL expressions.
Transformed an OCL expression AST using the Visitor pattern.
Saved and loaded OCL expressions to/from XMI resources.