Using the Object Constraint Language with ECO

By: John Bushakra

Abstract: This article describes basic OCL types and navigation, with some brief examples showing how OCL is used in Delphi.

Introduction

The Object Constraint Language (OCL) is a formal language used to express constraints in an unambiguous way. Expressions written in OCL can return single values and collections of objects, but they do not alter the state of the objects.

With Delphis ECO II framework, you can use OCL throughout the process of designing your application. OCL is available to:

  • Specify the values of derived attributes of classes.
  • Specify the values of derived association ends.
  • Specify constraints for your objects.
  • Configure ECO handles, specifying the values or objects to which the handles refer.
  • Evaluate dynamically built OCL queries and expressions at runtime.
  • Translate OCL queries to SQL to be efficiently executed in the database.

This article presents a brief introduction to OCL. It shows some of the common operations you can perform, but is not meant to be an exhaustive reference. The official OCL specification in PDF form can be found at the Object Management Group (OMG) website (http://www.omg.org/cgi-bin/doc?formal/03-03-13).

Establishing the Context of an OCL Expression

OCL expressions are evaluated in the context of a data type. Typically this type is a class declared in the model. Within the expression, the keyword self refers to the instance of that data type. For example, if the context is a class called Person, and the expression is

self.Name

then self refers to an instance of the Person class. This OCL expression returns the value of the Name attribute of the Person instance.

Knowing the context of the expression is particularly important when configuring ECO handles. For more information on this topic see the Delphi online help topic Working with ECO Handles.

Predefined OCL Types

The OCL Specification includes four predefined, basic data types. These are:

  • Integer
  • Real
  • Boolean
  • String

In addition to these intrinsic types, OCL expressions can refer to any data type defined in the model. These additional types can be either values or objects.

The OCL specification includes basic collection types:

  • Set: Unordered, no duplicates
  • Sequence: Ordered, no duplicates
  • Bag: Unordered, duplicates allowed

Basic Anatomy of an OCL Expression

An OCL expression consists of a context, and navigation from that context to the target value you are interested in. For example, consider the following class diagram:

If the context is Teacher, the following are valid OCL expressions:

self.firstName

Results in a value of type string

self.lastName

Results in a value of type string

self.classRoomAssignment

Results in a value of type string

self.Courses

Results in a collection of Course objects (because the multiplicity of the association is 1..*)

self.hireDate

Results in a DateTime object

self.Courses.numberOfStudents

Results in a collection of integer values (one for each course)

self.Courses.numberOfStudents->sum

Results in a value of type integer (the total amount of students in the courses of the teacher)

If the context is Course, the expression

self.Instructor

results in a single instance of a Teacher (because the multiplicity of that association is 1). If the multiplicity had been 0..1, the result would have been either an instance of Teacher, or a null value.

ECO deals with navigation on NULL values differently than you might expect. In the context of a Course, the expression

self.Instructor.firstName

is valid and returns the name of the instructor if there is one and an empty string if the Course has no instructor.

Expressions are evaluated from left to right to get the type and value of the result.

Association Classes

You use the dot operator to navigate to an association class, the same way as navigating to an attribute or association end. The difference is that you follow the dot with the name of the association class, starting with a lowercase letter.

Suppose the association above had an association class of type Room. If the context is Teacher, you could navigate to the association class with the expression

self.room

This expression would result in a collection of all the room assignments for all the courses to which the teacher is assigned.

Similarly, you can navigate from an association class to either end of the association. Starting with a context of the Room class, the expressions

self.instructor
self.courses

are valid. Navigating from an association class always results in a single object. Later this article will show how to add an association class to a relationship.

Operations on Types

In OCL, types themselves have predefined operations that can be performed on them. Operations are defined on both value types, and on collections. If the operation is specified on a basic type, use the dot to continue the expression. If the operation is specified on a collection, use the -> operator to continue the expression.

For example, substring is a predefined OCL operation defined for the string type. The following expression

self.firstName.substring(start,stop)

returns the number of characters in the firstName attribute.

However, in the expression

self.Courses->size()

self.Courses results in a collection of all Course objects. The arrow operator is used instead of the dot. The expression returns the number of elements in the collection.

Some operations take parameters. When this is the case, the parameter list is enclosed in parentheses. For operations that take no parameters, such as size, the OCL specification calls for parentheses with an empty argument list. However, in ECO, you can omit the empty parentheses.

Operations on Basic Types

The following table shows some of the operations defined by the OCL specification for the basic types.

Integer

  • =, +, -, /, *
  • abs()
  • div(i: Integer)
  • mod(i: Integer)
  • max(i: Integer)
  • min(i: Integer)

Real

  • =, <>, <, >, <=, >=
  • +, -, /, *
  • abs()
  • floor()
  • round()
  • max(r: Real)
  • min(r: Real)

Boolean

  • =
  • or, xor, and, not

String

  • =
  • length()
  • Note: The OCL specification defines the size() operation on strings. ECO uses the name length().

  • toUpper()
  • toLower()
  • subString(low: Integer, high: Integer)
  • concat(s: String)
  • Note: In ECO, the operator + is defined as a string concatenator.

For all types, the operators +, , *, / <, >, <>, <=, >=, div, mod, and, or, xor, may be written using infix notation. The following two expressions are equivalent:

a.+(b)
a + b

Perhaps the most common operation performed on a type is the allInstances operation. The allInstances operation retrieves a collection of all instances of the given type. The expression

Teacher.allInstances()

will result in a collection of all Teacher objects in the ECO space.

Operations on Meta Types

The following table shows other operations that are defined for all types.

typeName

Returns the name of the type.

attributes

Returns the set of attributes of the type.

associationEnds

Returns the set of navigable association ends.

supertypes

Returns the set of all direct supertypes.

allSuperTypes

Returns the entire set of supertypes.

allSubClasses

Returns the set of all subclasses defined on the type.

Other type-related operations

oclIsKindOf(aType)

Returns true if the value is of the specified type or one of its subtypes.

oclIsTypeOf(aType)

Returns true if the value is of the specified type exactly.

oclAsType(aType)

Returns the same value, but typed as the specified type. A runtime exception is thrown if the typecast fails.

Operations on Collections

The following table shows some of the common operations performed on collections. It is not an exhaustive list.

size()

Returns the number of elements in the collection.

includes(object)

Returns true if the collection contains the given object.

excludes(object)

Returns true if the collection does not include the given object.

count(object)

Returns the number of times object occurs in the collection.

isEmpty()

Returns true if the collection is empty.

notEmpty()

Returns true if the collection is not empty.

Iterators

There is a special construct in OCL called iterators. Iterators are defined for all collections, and behave different from normal operations. For example:

Teacher.allInstances()->select(courses->size() > 2)

The above expression will iterate over all the teacher objects, and return a new collection with the teachers that fulfill the condition in the select statement. Normally in an expression it is possible to omit the self keyword since this is implicit. Inside an iteration the implicit variable is the loop-variable. The courses in the example will be applied to an implicit variable of the type Teacher (regardless of the context of the expression). The following table shows the different iterators in OCL.

select(booleanexpr)

Returns the collection of elements that yields true.

reject(booleanexpr)

Returns the collection of elements that yields false.

orderBy(anyexpr)

Returns the same set of elements but ordered according to the anyexpr.

orderDescending(anyexpr)

Reverse order compared to previous.

forAll(booleanexpr)

Returns true if all of the objects in the collection yields true.

exists(booleanexpr)

Returns true if one of the object in the collection yields true .

collect

Returns a collection if the values returned by the iteration expression.

iterate

Generic iteration in the OCL specification , but not implemented in ECO.

Iterators can be nested:

Teacher.allInstances()->select(courses->exists(numberOfStudents > 2))

It is also possible to make the implicit iterator variable explicit:

Teacher.allInstances()->select(t | t.courses->size() > 2)

Here the variable t is introduced and used to reference the loop variable.

Using OCL with ECO

Derived Attributes

The first place you are likely to encounter OCL is on the class diagram. Attributes you create on a class can be derived by expressing a computation with OCL.

To create an attribute with a derived value, set the attributes derived property to true in the Object Inspector. Note that the names of derived attributes are automatically prefixed with a slash (/). This is a UML convention. Also note that setting the derived property to true causes the persistence property to be set to transient, meaning the attribute will not be stored in the database.

Next, enter the OCL expression in the Derivation OCL property. Unfortunately the OCL Expression Editor is not available on the Class Diagram, so you will have to write the expression by hand.

For example, to add a derived attribute to the Teacher class, you could do the following:

  1. Right-click the Teacher class and choose Add Attribute.
  2. Change the attributes name to fullName.
  3. Set the type of the attribute to String.
  4. Set the derived property to true.
  5. Notice that Delphi changes the attribute name to /fullName, and sets the persistent property to transient.
  6. Enter the following OCL expression in the Derivation OCL property:
  7. firstName +   + lastName
    

The expression you use to calculate the value of a derived attribute must result in value of type string (in this case, since the attribute is declared as a string attribute); it cannot result in an object, or a collection.

Derived Association Ends

You can also use OCL to derive association ends. Here, unlike the case of derived attributes, the expression must result in a single object or a collection of objects, depending on the multiplicity of the association end.

If it is derived, the association end must be derived from another association. To create an association end that restricts the teachers course assignments to those courses containing more than 30 students, do the following:

  1. Add a new association between the Teacher and Course classes.
    Drag from the Course class to the Teacher class.
  2. Select the association on the Class Diagram.
  3. Set the associations derived property to true.
  4. Change the names of the association and association ends (double-click the name-labels in the diagram) according to the diagram below.
  5. In the Model View tree, expand the Teacher node and select the CrowdedCourses subnode.
    In this example we are setting the OCL expression for only one of the association ends, but either or both ends can have an OCL expression associated with it.
  6. In the Derivation OCL property enter the expression
    self.Courses->select(numberOfStudents> 30)
  7. Set the Multiplicity of CrowdedCourses to 0..*.
  8. In the Model View tree, expand the Course node and select the subnode for the Teacher association end.
  9. Set the Navigable property of the Teacher association end to false.

The Class Diagram should look similar to the following:

More on Association Classes

Earlier you saw how to navigate to an association class in an OCL expression. Here is how you create an association class on the Class Diagram.

  1. Add a new ECO class to the diagram.
    For this example, the class is called Room.
  2. Click the association to which you want to connect the class.
  3. Click the property editor ellipses for the Association Class property in the Object Inspector.
    The Choose Class to Instantiate dialog box opens.



  4. Expand the tree and select the Room class.
  5. Click OK.

The following screen shot shows the Teacher and Course classes with an association class called Room. Notice the name of the association has changed to that of the association class.

Configuring ECO Handles with OCL Expressions

The next place you are going to encounter OCL is designing your applications user interface. Here, the use of OCL is not an option. You must use OCL expressions to configure the ECO handles you place on a form. The handles OCL expression determines those objects or values the handle will represent at runtime. You then bind the handles to .NET controls that you place on the form. This subject is covered in the Delphi online help topic, Working with ECO Handles.

On the WinForms designer, you will use the OCL Expression Editor to quickly create the expressions you need. You access the OCL Expression Editor by clicking the expression handles Expression property editor ellipses in the Object Inspector. The editor shows you exactly what is available at any given step in building the expression. The syntax of the expression is checked dynamically as you build it. You can also type the expression manually if you wish.

The screen shot below shows the OCL Expression Editor as it opens when first configuring an ECO ExpressionHandle (that is connected to a ReferenceHandle component via the RootHandle property). The StaticValueType property of the root handle has been set to the Teacher class.

In the right-hand pane the editor shows the available attributes, types, and operations, based on the current context (Teacher). You can build the expression by double-clicking the item you want in the right-hand pane. The expression appears in the left-hand pane. For more information on the OCL Expression Editor, see the Delphi online help topic, Using the OCL Expression Editor.

To bind the expression handle to the association end called Courses, double-click self.Courses in the list. The next screen shot shows how the expression is built in the left-hand pane. Notice the list in the right-hand pane has changed to reflect those operations that are valid, given the current expression.

At runtime, you must set the ReferenceHandle to refer to a Teacher object in the ECO space. When you do this, the connected expression handle will bind to the Courses taught by that Teacher, as dictated by the association. You could accomplish this with the following Delphi code:

// rhRoot is a default component on every ECO Winform
   Winform.rhRoot.SetElement(aTeacher.AsIObject());

Using the Object Inspector, you bind a Winform controls DataSource property to the expression handle. The control will then update itself automatically when a different Teacher object is assigned to rhRoot.

Summary

This article has shown the basics of constructing an OCL expression, and presented some common ways to use OCL in an ECO application. More information on ECO handles and OCL is available in the following Delphi online help topics:

  • Overview of the ECO Framework
  • Working with ECO Handles
  • Using the OCL Expression Editor
  • Configuring an OclVariables Component
  • Adding Columns and Nestings to an ECO Handle

Server Response from: ETNASC03