| Eclipse Corner Article |

Summary
The MDT OCL project provides the building blocks for Model-Driven tools to weave OCL declarative specifications into software artifacts. We showcase some of these possibilities, taking as starting point a plugin to visualize OCL ASTs in the form of annotated trees. This example motivates some practical tips about patterns for OCL visitors, including using Java 5 generics and achieving conciseness by letting MDT OCL take care of the "walking" order. To really reap the benefits of OCL-enriched specifications, tools in our modeling chain have to be able to transform such expressions into the target software platform (e.g. compile into Java, translate into SQL). Work in this area is summarized, to ease jumpstarting your own OCL processing project.By Miguel Garcia, Technische Universität Hamburg-Harburg (Germany)
April 30th, 2007
The MDT OCL framework supports all the way from parsing a textual OCL expression to evaluating such expression on some object population, for both Ecore and UML2 models. This requires some infrastructure, which MDT OCL provides as visitors for:
The new org.eclipse.ocl.OCL generic class is the focal point of OCL parsing and evaluation. Besides creating org.eclipse.ocl.helper.OCLHelpers for parsing embedded constraints and org.eclipse.ocl.Querys for evaluating constraints, it can also parse OCL documents (text files external to the model).
Although this article does not focus on concrete-syntax trees prepared by the parser, two remarks are in order. First, CST classes are internal, i.e. not part of the public API of the MDT OCL plugin. And second, the validation of a CST is actually performed by the same visitor in charge of the CST to AST conversion.
OCL expressions cannot be understood in isolation, they always appear in the context of some model element. Sample contexts include:
It could be argued that, if OCL is human-readable, why is an AST view needed at all? The answer is that it comes handy not just for those writing OCL-processing algorithms, but for everyone. Consider the following unparenthesized invariant:
currentLevel.name = 'Silver' implies card.color = Color::silver and currentLevel.name = 'Gold' implies card.color = Color::goldHow is it to be interpreted?
| Like this ... | ... or like this? |
|---|---|
(currentLevel.name = 'Silver' implies card.color = Color::silver) and (currentLevel.name = 'Gold' implies card.color = Color::gold) |
( currentLevel.name = 'Silver'
implies
(card.color = Color::silver and currentLevel.name = 'Gold')
)
implies
( self.card.color = Color::gold )
|
This article assumes some familiarity with OCL syntax. The examples shown are part of a case study (Royal & Loyal) originally discussed in the book "The Object Constraint Language. Getting your models ready for MDA" by Warmer and Kleppe, as well as in the following course notes:
Following the tradition of other OCL tools, the Royal & Loyal case study is followed (its class diagram can be found in Appendix A). Both EMF and UML2 versions are provided . The R&L model contains samples for all OCL language constructs. We'll discuss some of them in connection with our viewer. This model can be explored with any editor that allows selecting model elements (e.g. the built-in Sample Ecore Model Editor, or some graphical editor). The contained OCL expressions can thus be modified and new ones added. You won't get feedback on syntax checks with those editors (as the OCL annotations are opaque to them). For that, installing the OCLASTView plugin is needed.
In order to have an .uml file initialized with the contents of a pre-existing .ecore file, you may do one of the following: (a) right click on the corresponding Ecore .genmodel, select "Export Model..." and then "UML model". Alternatively, you can (b) open the Ecore model, select the EPackage you want to convert, and then from the "Sample Ecore Editor" menu (in the main menu bar) choose "Convert to UML Model ...". You will need the org.eclipse.uml2.examples plugins to see those menu options.
The interactive evaluation of OCL expressions against a particular object population is not the focus of this article. For that, the OCL Interpreter delivered as part of MDT OCL can be used. It accepts ad-hoc OCL expressions provided by the user at runtime, also allowing serializing them to .xmi. This document can be explored with the standard XMI editor provided by EMF, however OCLASTView has been designed to display OCL ASTs in a more readable format. A comparison can be found in Appendix B.
The occurrences of OCL language constructs are internally represented in ASTs as instances of the OCL "metamodel" classes (depicted in Figure 2). In the AST shown in Figure 1, the root is an IteratorExp. This particular iterator (a forAll with two iterator variables) evaluates whether the boolean condition expressed in its body is true for all pairs of items in the source collection (the source collection being self.participants, whose type is OrderedSet(Customer)). As a whole, this forAll reports whether items with duplicate names exist. OCLASTView depicts iterators with the source collection as first child, the iterator variables as second child, and the boolean expression as third child (just like the appearance order in the textual syntax). More diagrams of the OCL 2.0 metamodel can be found in the latest spec by the OMG, formal/2006-05-01.
![]() |
MDT OCL contains great examples of visitors for OCL processing, e.g. to obtain the textual representation of an OCL AST (ToStringVisitor). These visitors can be easily spotted in that they implement interface org.eclipse.ocl.utilities.Visitor.
A useful starting point for writing your own visitor is the abstract class AbstractVisitor which shows how to visit children nodes for each OCL construct.
Let's say we've got a reference myExp to an OCLExpression (the root interface of the OCL metamodel) most probably obtained as a result of invoking OCLHelper#createQuery(String expression). The OCLExpression interface extends Visitable, therefore we can invoke myExp.accept(myVisitor) on it. Assuming that myExp is the AST for the forAll expression in Figure 1, and thanks to overriding, the accept() method in IteratorExpImpl will be invoked, which in turn delegates to a handler for that language construct in our visitor.
Before getting to see the
default implementation of AbstractVisitor#visitIteratorExp(), please notice that:
The above implementation performs no processing at all, it just visits all nodes. Instead, thepublic T visitIteratorExp(IteratorExp<C, PM> callExp) { T sourceResult = callExp.getSource().accept(this); List<T> variableResults; List<Variable<C, PM>> variables = callExp.getIterator(); if (variables.isEmpty()) { variableResults = Collections.emptyList(); } else { variableResults = new java.util.ArrayList
(variables.size()); for (Variable<C, PM> iterVar : variables) { variableResults.add(iterVar.accept(this)); } } T bodyResult = callExp.getBody().accept(this); return handleIteratorExp(callExp, sourceResult, variableResults, bodyResult); } protected T handleIteratorExp(IteratorExp<C, PM> callExp, T sourceResult, List<T> variableResults, T bodyResult) { return null; }
protected String handleIteratorExp(IteratorExp<C,PM> callExp, String sourceResult, List<String> variableResults, String bodyResult) { StringBuffer result = new StringBuffer(); String name = callExp.getName(); result.append(sourceResult).append("->").append(name).append('('); //$NON-NLS-1$ for (Iterator<String> iter = variableResults.iterator(); iter.hasNext();) { result.append(iter.next()); if (iter.hasNext()) { result.append(", ");//$NON-NLS-1$ } } result.append(" | ").append(bodyResult).append(')');//$NON-NLS-1$ return result.toString(); }
ToStringVisitor needs only override handleIteratorExp() to get its job done, as visitIteratorExp() takes up the recurrent duty of visiting the owned parts, moreover passing such results (Strings in this case) as actual arguments to handleIteratorExp(). In case the default visit order established by AbstractVisitorImpl is not deemed appropriate for some particular scenario, its visit... methods can be overridden. This is necessary for example when evaluating an OCL if-then-else-endif: the else part is to be evaluated only in case the condition part evaluates to false. This is precisely what EvaluationVisitorImpl#visitIfExp() does.
It's a fact of life that MDT OCL makes copius use of Generics. However it's a small set of type parameters that's used over and over again. With a bit of practice you'll recognize in a flash that C stands for either an EMF org.eclipse.emf.ecore.EClassifier or an UML2 org.eclipse.uml2.uml.Classifier. The complete list of type parameters is described in the Javadoc for the interface org.eclipse.ocl.Environment (reproduced below for ease of reference while reading this article). In fact, there is a method to all this, and the Type Hierarchy helps in visualizing it. Environment has two subtypes: EcoreEnvironment and UMLEnvironment, where the correct type substitutions appear for easy reference. Other types exhibiting the same pattern include org.eclipse.ocl.OCL, org.eclipse.ocl.utilities.UMLReflection, and org.eclipse.ocl.utilities.ExpressionInOCL.
The intriguing T type parameter in the code above stands for the result type of visitor methods. For example, the declaration of ToStringVisitor subclasses AbstractVisitor by substituting the generic T return type with String. In detail,
public class ToStringVisitor<C, O, P, EL, PM, S, COA, SSA, CT> extends AbstractVisitor<String, C, O, P, EL, PM, S, COA, SSA, CT>
Given that some types have specializations providing type substitutions for Ecore and for UML2, a natural question is why AbstractVisitor does not follow the same pattern. After all, one could type:
public class AbstractVisitorEcore<T>
extends AbstractVisitor<T, EClassifier, EOperation, EStructuralFeature,
EEnumLiteral, EParameter, EObject,
CallOperationAction, SendSignalAction, org.eclipse.ocl.ecore.Constraint>
(by checking what substitutions are used in EcoreEnvironment) and then let the Java Development Tools perform auto-completion given the start of a method name, for example:
@Override
protected T handleIteratorExp(IteratorExp<EClassifier, EParameter> callExp,
T sourceResult, List<T> variableResults, T bodyResult) {
// TODO Auto-generated method stub
return super.handleIteratorExp(callExp, sourceResult, variableResults,
bodyResult);
}
This move is not as smart as it might seem at first sight given that the very benefits of Generics are lost: any OCL visitor extending AbstractVisitorEcore will only work on Ecore-based models, although it could have most probably worked on UML2-based models as well. Bounded generics allow writing algorithms which minimally depend on the types of the input, while preserving static type-safety. That's the reason why EvaluationVisitorImpl, for example, has no specializations: its methods work for models expressed in either of Ecore or UML2.
org.eclipse.ocl
|
With this brief exposition to the OCL metamodel and the usage of parametric polymorphism in MDT OCL, we set out to build our first OCL visitor.
A tree-based visualization of OCL expressions helps big time in familiarizing with the structure of the different kinds of nodes in OCL ASTs, and thus we will get that visitor to work first (a slightly more sophisticate visitor, a simplifier of arithmetic expressions, comes next).
We cover first the visitor itself (devoid of UI aspects), leaving for the next section the plugin extension that defines the view to display the tree prepared by the visitor. This view mostly consists of code generated by a PDE wizard. The remaining bits and pieces (how to track the current selection in an Ecore editor, how to parse OCL, how to display an XML document in a TreeViewer) are described in detail elsewhere.
It makes sense to consider first what output we want for leaf nodes in OCL ASTs. These nodes are recognized because AbstractVisitor defines their handler to simply return null; no owned nodes are visited, and thus no handler... method is defined for such construct. In our case, we want our visitor to prepare an XML element to show the node kind and type. Some leaf nodes have additionally a value, e.g. all the ...LiteralExp ones: they stand for literal constants of some type. We will also display such literal constants as attributes of an XML node. In summary, leaf nodes are handled by:
public T visitVariableExp(VariableExp<C, PM> v); public T visitTypeExp(TypeExp<C> t); public T visitUnspecifiedValueExp(UnspecifiedValueExp<C> unspecExp); public T visitStateExp(StateExp<C, S> stateExp); public T visitIntegerLiteralExp(IntegerLiteralExp<C> literalExp); public T visitRealLiteralExp(RealLiteralExp<C> literalExp); public T visitStringLiteralExp(StringLiteralExp<C> literalExp); public T visitBooleanLiteralExp(BooleanLiteralExp<C> literalExp); public T visitNullLiteralExp(NullLiteralExp<C> literalExp); public T visitInvalidLiteralExp(InvalidLiteralExp<C> literalExp); public T visitEnumLiteralExp(EnumLiteralExp<C, EL> literalExp); public T visitUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExpliteralExp)
These are the methods we'll implement first in class XMLforOCL which extends AbstractVisitor and that imports classes from the MDT OCL plugin (the plugins that OCLASTView depends on are shown in Figure 11 below)
Method
visitVariableExp() in XMLforOCL handles usages of variables in OCL. VariableExp references the declaration of the referred variable, i.e. it references a org.eclipse.ocl.expressions.Variable<C, PM>:
@Override
public Element visitVariableExp(VariableExp<C, PM> v) {
Variable<C, PM> vd = v.getReferredVariable();
Element res = new Element("VariableExp"); //$NON-NLS-1$
res.setAttribute("name", vd.getName()); //$NON-NLS-1$
addTypeInfo(res, v);
return res;
}
private void addTypeInfo(org.jdom.Element res, TypedElement<C> exp) {
C ec = exp.getType();
String tName = getName(ec);
res.setAttribute("type", tName); //$NON-NLS-1$
}
An OCL expression may contain literals for primitive datatypes (Real, Integer, Boolean, String). A handler for such occurrences of literals in an AST node typically looks like:
@Override
public Element visitRealLiteralExp(RealLiteralExp<C> literalExp) {
Element res = new Element("RealLiteralExp"); //$NON-NLS-1$
res.setAttribute("symbol", Double.toString(literalExp.getRealSymbol())); //$NON-NLS-1$
return res;
}
Two novelties of OCL 2.0 are the built-in literal values null and OclInvalid which may appear in the textual syntax. In general, an expression any of whose arguments is OclInvalid evaluates to OclInvalid, that value propagates during evaluation much like NULL does in SQL. By including an invocation to addTypeInfo() our handlers will reveal what the types of those literal values are (and they better have one, as OCL is a strongly-typed language). The instances returned are maintained by MDT OCL, they are part of the symbol tables (in compiler-implementation terminology) and form part of the bookkeeping required to parse, validate against static semantics, and transform OCL expressions.
According to our definition, non-leaf nodes are those whose metamodel class defines one or more containment associations, as for example IfExp, IterateExp, and IteratorExp:
The XMLforOCL handler of IfExp simply pieces together the results of visiting the owned parts (just like ToStringVisitor does). These results (conditionResult, thenResult, and elseResult) are never null because according to the well-formedness rules of OCL an IfExp must have those three parts and visitors are coded under the assumption of well-formed input. Notice also that had not been org.jdom.Element substituted for the type parameter T, the visitor would have resorted to explicit type-casts (downcasts from Object to Element). Generics instead enable compile-time type-checking.
@Override protected org.jdom.Element handleIfExp(IfExpifExp, Element conditionResult, Element thenResult, Element elseResult) { Element res = new Element("IfExp"); //$NON-NLS-1$ Element eConditionPart = new Element("condition"); //$NON-NLS-1$ eConditionPart.addContent(conditionResult); res.addContent(eConditionPart); Element eThenPart = new Element("then"); //$NON-NLS-1$ eThenPart.addContent(thenResult); res.addContent(eThenPart); Element eElsePart = new Element("else"); //$NON-NLS-1$ eElsePart.addContent(elseResult); res.addContent(eElsePart); return res; }
We've written handlers returning org.jdom.Element for several OCL constructs by considering them in isolation, without worrying about other sub-expressions in the whole OCL expression. This we can make for IfExp, the ...LiteralExp, and in general for metamodel classes which are not subtypes of CallExp, as a CallExp further evaluates some source expression which can be obtained with getSource(). For example, the metamodel in Figure 2 shows that a PropertyCallExp is a subtype of CallExp. That explains why we can write: self.age >= 18, where age stands for an attribute. (The VariableExp self is its source expression). What EMF calls EStructuralFeature (an attribute or a reference) OCL calls property, also in agreement with UML terminology.
In terms of OCL visitors this means that for each instance of CallExp its source expression should be visited, and the visit... methods in AbstractVisitor do just that. This behavior was already shown in
and
, the default AST visitor and the serializer into textual representation, respectively. The root node in Figure 1 has <source> as a subnode only because in our overridden handleIteratorExp the following was done:
Element sourceE = new Element("source");
sourceE.addContent(sourceResult);
res.addContent(sourceE);
Other OCL constructs owning sub-expressions occur in connection with the initializers of variables and the arguments of operation calls. As a sidenote, in programming-language parlance, OCL's "source" is usually called "target", while in OCL MessageExps the target is actually named "target".
We've overriden so far the methods highlighted in Figure 4. The remaining ones follow the same principles and can be found in the finished XMLforOCL.
Exploring the list of not-yet-implemented methods reveals a type that cannot be found in the fragment of the OCL metamodel depicted in Figure 2 (TupleLiteralPart). Implementing another method (visitCollectionLiteralExp()) reveals a containment association to types also not shown in Figure 2 (CollectionItem and CollectionRange, taken together the only subtypes of CollectionLiteralPart, also not shown in Figure 2). Finally, we've implemented a handler (visitVariable()) for a type nowhere to be found in Figure 2. What's going on? As Figure 5 shows, not all OCL constructs have counterparts subtyping OCLExpression in the metamodel (Figure 2 depicts only the classes branching off from OCLExpression).
|
|
not all OCL language constructs subtype OCLExpression |
To complete the picture on visiting Variables, version 1.1 of MDT OCL introduces the ExpressionInOcl metaclass (described in Chapter 12 of the OCL spec). This metaclass does not subtype TypedElement but Visitable and constitutes the container for context variables (self, result) and for those variables standing for operation parameters (if any). As for other OCL constructs, AbstractVisitor#visitExpressionInOCL() will visit those owned parts, passings the results to AbstractVisitor#handleExpressionInOCL().
Navigation in OCL expressions as enabled by CallExp is very useful. Some constructs however must serve as anchors, i.e. they have no source expression themselves. We've met some of them already: all the ...LiteralExp ones, VariableExp, IfExp, and LetExp. The remaining constructs without a source share the property of being subtypes of OCLExpression but not of CallExp: TypeExp, UnspecifiedValueExp, StateExp, and MessageExp.
There's a distinction between VariableExp and Variable, the former denotes a usage and the latter a declaration. The declaration for a usage is accessible through VariableExp#getReferredVariable(). The AST builder in MDT OCL makes available as pre-defined some variables in some OCL expression contexts:
Moreover, the OCL syntax allows for a collect iterator to be elided from the textual syntax, to support the intuitive concept of "dot navigation". This feature can be seen at work in Figure 7.
With some more work, the OCL AST visualizer can be extended to show the variables available in the current environment of a sub-expression.
The following section covers GUI issues, setting the ground for covering later our last visitor, a simplifier of arithmetic expressions.
In the just created plugin.xml the dependencies shown in Figure 11 are added:
In its current state, the view generated by the wizard can be shown alongside the Ecore or UML editor we've chosen yet it is unaware about the current selection. We want it instead to display the XML tree so laboriously built for an OCL-annotated operation or classifier. Some classes cherry-picked from other plugins save the day (they've been placed in the util package in the source code of OCLASTView):
Finally, we make the view implement ISelectionListener with selectionChanged() checking whether a valid context has been selected (an operation or a classifier, to later check whether that context has a valid OCL annotation. At the XMI level, an OCL annotation looks as shown in Figure 12:
Using the Sample Ecore editor the same OCL invariant looks as shown in Figure 13:
Go ahead and click operations at will, the OCLASTView will get notified and will update the view as a result of parsing the string containing the OCL expression and visiting it (debug for increased thrill)
private Element displayOCL(String expression, EObject context, String key) {
org.eclipse.ocl.OCL ocl = null;
org.eclipse.ocl.helper.OCLHelper helper = null;
Element res = new Element(key);
res.setAttribute("textualInput", expression);
if (context instanceof org.eclipse.uml2.uml.NamedElement) {
ocl = org.eclipse.ocl.uml.OCL.newInstance();
} else {
ocl = org.eclipse.ocl.ecore.OCL.newInstance();
}
helper = ocl.createOCLHelper();
// set our helper's context object to parse against it
if ((context instanceof org.eclipse.emf.ecore.EClass)
|| (context instanceof org.eclipse.uml2.uml.Classifier)) {
helper.setContext(context);
} else if (context instanceof org.eclipse.emf.ecore.EOperation) {
EOperation eOp = (EOperation) context;
helper.setOperationContext(eOp.getEContainingClass(), eOp);
} else if (context instanceof org.eclipse.uml2.uml.Operation) {
org.eclipse.uml2.uml.Operation op = (org.eclipse.uml2.uml.Operation) context;
helper.setOperationContext(op.getOwner(), op);
} else if (context instanceof org.eclipse.emf.ecore.EStructuralFeature) {
EStructuralFeature sf = (EStructuralFeature) context;
helper.setAttributeContext(sf.getEContainingClass(), sf);
} else if (context instanceof org.eclipse.uml2.uml.Property) {
org.eclipse.uml2.uml.Property p = (org.eclipse.uml2.uml.Property) context;
helper.setAttributeContext(p.getOwner(), p);
}
OCLExpression<EClassifier> oclExp = null;
Element xmlAST = null;
try {
oclExp = helper.createQuery(expression);
} catch (Exception e) {
xmlAST = reportException(e);
res.addContent(xmlAST);
return res;
}
XMLforOCL xfo = null;
if (context instanceof org.eclipse.uml2.uml.NamedElement) {
xfo = XMLforOCL.getUML2Version();
} else {
xfo = XMLforOCL.getEcoreVersion();
}
try {
xmlAST = (Element) oclExp.accept(xfo);
} catch (Exception e) {
xmlAST = reportException(e);
}
res.addContent(xmlAST);
return res;
}
Emfatic can also be used to edit an OCL-annotated Ecore model, yet its Outline view does not provide an EOperation selection. Emfatic makes however for quick editing of OCL expressions themselves, as depicted in Figure 15. Additionally, a new version of Emfatic is being developed. For details check this document. The new features include: syntax to account for EMF Generics, folding, hovers displaying the declaration of the element under the cursor, hyperlinks, AutoEdits, templates, and Type Hierarchy, among others.
OCLASTView also works with the built-in UML Model Editor, as shown below:
Whether an OCL expression is interpreted or compiled, one way to make OCL evaluation faster consists in computing at compile-time as much as possible of the expression, as a result of analyses such as constant folding or strength reduction or common sub-expression elimination or partial redundancy elimination (compiler-implementation terminology, for an overview see Max Hailperin's course notes) or partial evaluation (functional programming terminology). We will tap into this area by showcasing a visitor to clone an input OCL expression, except that arithmetic sub-expressions involving only literal constants will be reduced to their final result.
This is actually the first example involving processing, in this case transformation, of an OCL expression. So far we've just relied on the AST builder to resolve identifiers to declarations and in general provide us with well-formed ASTs. As we output an updated AST thanks to an algorithm of our invention, we have to make sure that we in turn play by the rules and return a valid AST.
Moreover, it would be nice if we documented the set of OCL expressions that our algorithm has been designed to handle (it's all right to write a visitor that covers only a subset of OCL, as long as that visitor reports whether it can cope with the input or not).
You may want to review the checks for well-formedness embodied in org.eclipse.ocl.internal.parser.ValidationVisitor.
The sketched reduction is not the only possible one, logical operations can be simplified according to de Morgan rules, and so on. In such visitors one wants to return unmodified AST nodes most of the time . In the arithmetic reduction case, a modified AST is returned only from the handler for OperationCallExp in case both the source expression and the argument are any of RealLiteralExp or IntegerLiteralExp. In the resulting OCL expression, the CallExp (if any) that was referencing the old (unmodified) source expression should be changed to point to the freshly instantiated AST node. The behavior we saw in AbstractVisitor for subtypes of CallExp (visiting the source, visiting the contained nodes, doing processing, return) hints at how to implement this.
It would be cumbersome to duplicate over and over that strategy whenever a "reducer" visitor is written. Instead, we'll code OCLCloner to provide such behavior by default. A new visitor need only override those methods where it may detect an opportunity for applying a reduction, invoking the non-overridden version in case the preconditions for the reduction are not fulfilled. For example, method visitOperationCallExp() in OCLArithSimplifier starts as follows:
@Override
protected Visitable handleOperationCallExp(
OperationCallExp<C, O> callExp, Visitable sourceResult,
List<Visitable> argumentResults) {
int opcode = callExp.getOperationCode();
if (!isArithmeticOp(callExp)) {
return super.handleOperationCallExp(callExp, sourceResult, argumentResults);
}
OCLExpression<C> newSource = (OCLExpression<C>) sourceResult;
OCLExpression<C> newArg = (OCLExpression<C>) argumentResults.get(0);
if (!(newSource instanceof NumericLiteralExp)
|| !(newArg instanceof NumericLiteralExp)) {
return super.handleOperationCallExp(callExp, sourceResult, argumentResults);
}
/*
* actual reduction comes here
*/
A visitor is not limited to letting other components know of its processing only through return values, it may also update instance state. This is frequently the case with chains of visitors: a visitor receives as argument in its constructor the previous one. However, for reduction-style visitors no instance state has been necessary.
You can plug OCLArithSimplifier into OCLASTView#displayOCL() to display as XML the reduced AST. Just let oclExp in OCLASTView#displayOCL() accept an instace of this visitor, right before
, as follows:
XMLforOCL xfo = null; OCLArithSimplifier simplifier = null; if (context instanceof org.eclipse.uml2.uml.NamedElement) { xfo = XMLforOCL.getUML2Version(); simplifier = OCLArithSimplifier.getUML2Version(); } else { xfo = XMLforOCL.getEcoreVersion(); simplifier = OCLArithSimplifier.getEcoreVersion(); } try { oclExp = (OCLExpression) oclExp.accept(simplifier);xmlAST = (Element) oclExp.accept(xfo); // } catch (Exception e) { xmlAST = reportException(e); }
In spite of having been around for some years now, the best times for OCL are yet to come by leveraging model compilers to map declarative specifications into the chosen target software architecture. Some projects and prototypes in this field are mentioned for further exploration:
The examples above are just a glimpse of on-going work involving processing of Abstract Syntax Trees of OCL (in particular) and of software models in general. The availability of OCL infrastructure as provided by MDT OCL, together with contributions from the community, is a step forward towards the widespread availability of reliable, standards-based model-compiler technology. The building blocks for this promising area are also at your disposal.
![]() |
A comparison with Figure 1 shows that:
The visitor for simplifying arithmetic expressions presented here is based on work by Veronica Tedjasukmana (for another OCL tool) on a family of visitors that cover a larger set of simplification rules, for both arithmetic and boolean operations.
For running this plugin, just copy the provided .jar of the OCLASTView plugin into your <ECLIPSE_INSTALL>/plugins folder, and restart Eclipse (with eclipse.exe -clean to be really sure that the just added plugin will be picked-up in the product configuration).
MDT OCL 1.1 is required. Depending on whether you'll want to edit Ecore or UML models you'll also need EMF 2.3 or UML2 2.x.
Source code is provided in the plugin itself. After the plugin has been installed, the source code can be imported into the workspace as an Eclipse project with File > Import > Plug-ins and Fragments > Projects with source folders > OCLASTView.
If you want to take a look at the source code before actually installing the plugin, unpack the plugin jar and take a look inside the src folder.