Basic Values and Types


In OCL, a number of basic types are predefined and available to the modeler at all time. These predefined value types are independent of any object model and part of the definition of OCL.
The most basic value in OCL is a value of one of the basic types. The basic types of OCL, with corresponding examples of their values, are shown in Table 2.
 
 

Table 2 - Basic Types

type 
values
Boolean  true,false
Integer  1, -5, 2, 34, 26524, ..
Real  1.5, 3.14, ...
String  'To be or not to be...'

OCL defines a number of operations on the predefined types. Table 3 gives some examples of the operations on the
predefined types. See Section "The OCL Standard Library - Primitive Types"  for a complete list of all operations.

Table 3 - Operations on predefined types


type  operations
Integer  *, +, -, /, abs()
Real  *,+, -, /, floor()
Boolean and, or, xor,not, implies, if-then-else 
String  concat(), size(), substring()

Collection, Set, Bag, Sequence and Tuple are basic types as well. Their specifics will be described in the upcoming sections.
 

Types from the UML Model

Each OCL expression is written in the context of a UML model, a number of classifiers (types/classes, ...), their features and associations, and their generalizations. All classifiers from the UML model are types in the OCL expressions that are attached to the model.
 

Enumeration Types

Enumerations are Datatypes in UML and have a name, just like any other Classifier. An enumeration defines a number of enumeration literals, that are the possible values of the enumeration. Within OCL one can refer to the value of an enumeration. When we have Datatype named Gender in the example model with values 'female' or 'male' they can be used as follows:

context Person inv: gender = Gender::male
 

Let Expressions

Sometimes a sub-expression is used more than once in a constraint. The let expression allows one to define a variable which can be used in the constraint.

context Person inv:
let income : Integer = self.job.salary->sum() in
if isUnemployed then
income < 100
else
income >= 100
endif

A let expression may be included in any kind of OCL expression. It is only known within this specific expression.
 

Additional operations/attributes through «definition» expressions

The Let expression allows a variable to be used in one Ocl expression. To enable reuse of variables/operations over multiple OCL expressions one can use a Constraint with the stereotype «definition», in which helper variables/operations are defined. This «definition» Constraint must be attached to a Classifier and may only contain variable and/or operation definitions, nothing else. All variables and operations defined in the «definition» constraint are known in the same context as where any property of the Classifier can be used. Such variables and operations are attributes and operations with stereotype «OclHelper» of the classifier. They are used in an OCL expression in exactly the same way as normal attributes or operations are used. The syntax of the attribute or operation definitions is similar to the Let expression, but each attribute and operation definition is prefixed with the keyword 'def' as shown below.

context Person
def: income : Integer = self.job.salary->sum()
def: nickname : String = 'Little Red Rooster'
def: hasTitle(t : String) : Boolean = self.job->exists(title = t)

The names of the attributes / operations in a let expression may not conflict with the names of respective attributes/ associationEnds and operations of the Classifier.

Using this definition syntax is identical to defining an attribute/operation in the UML with stereotype «OclHelper» with an attached OCL constraint for its derivation.
 

Type Conformance

OCL is a typed language and the basic value types are organized in a type hierarchy. This hierarchy determines conformance of the different types to each other. You cannot, for example, compare an Integer with a Boolean or a String.

An OCL expression in which all the types conform is a valid expression. An OCL expression in which the types don't conform is an invalid expression. It contains a type conformance error. A type type1 conforms to a type type2 when an instance of type1 can be substituted at each place where an instance of type2 is expected. The type conformance rules for types in the class diagrams are simple.

The effect of this is that a type conforms to its supertype, and all the supertypes above. The type conformance rules for the types from the OCL Standard Library are listed in Table 4.

Table 4 - Type conformance rules


Type
    Conforms to/Is a subtype of
  Condition
Set(T1) Collection(T2)  if T1 conforms to T2
Sequence(T1) Collection(T2) if T1 conforms to T2
Bag(T1)  Collection(T2)  if T1 conforms to T2
Integer  Real  

The conformance relation between the collection types only holds if they are collections of element types that conform to each other. See Section 7.5.13, "Collection Type Hierarchy and Type Conformance Rules,"  for the complete conformance rules for collections.

Table 5 provides examples of valid and invalid expressions.

Table 5 - Valid Expressions


OCL expression
valid
explanation
1 + 2 * 34 yes
1 + 'motorcycle' no type String does not conform to type Integer
23 * false no type Boolean does not conform to Integer
12 + 13.5 yes

 

Re-typing or Casting

In some circumstances, it is desirable to use a property of an object that is defined on a subtype of the current known type of the object. Because the property is not defined on the current known type, this results in a type conformance error.

When it is certain that the actual type of the object is the subtype, the object can be re-typed using the operation
oclAsType(OclType). This operation results in the same object, but the known type is the argument OclType. When there is an object object of type Type1 and Type2 is another type, it is allowed to write:

object.oclAsType(Type2) --- evaluates to object with type Type2

An object can only be re-typed to one of its subtypes; therefore, in the example, Type2 must be a subtype of Type1.

If the actual type of the object is not a subtype of the type to which it is re-typed, the expression is undefined (see  ("Undefined Values")).
 

Precedence Rules

The precedence order for the operations, starting with highest precedence, in OCL is:


Parentheses `(' and `)' can be used to change precedence.
 

Use of Infix Operators

The use of infix operators is allowed in OCL. The operators `+', `-', `*'. `/', `<`, `>', `<>' `<=' `>=' are used as infix operators. If a type defines one of those operators with the correct signature, they will be used as infix operators. The expression:

a + b

is conceptually equal to the expression:

a.+(b)

that is, invoking the `+' operation on a with b as the parameter to the operation.

The infix operators defined for a type must have exactly one parameter. For the infix operators `<`, `>', `<=', `>=', `<>',`and', `or', and `xor' the return type must be Boolean.
 

Keywords

Keywords in OCL are reserved words. That means that the keywords cannot occur anywhere in an OCL expression as the name of a package, a type or a property. The list of keywords is shown below:

Comment

Comments in OCL are written following two successive dashes (minus signs). Everything immediately following the two dashes up to and including the end of line is part of the comment. For example:
 

Undefined Values

Some expressions will, when evaluated, have an undefined value. For instance, typecasting with oclAsType() to a type that the object does not support or getting the ->first() element of an empty collection will result in undefined. In general, an expression where one of the parts is undefined will itself be undefined. There are some important exceptions to this rule, however. First, there are the logical operators:

The rules for OR and AND are valid irrespective of the order of the arguments and they are valid whether the value of the other sub-expression is known or not.

The IF-expression is another exception. It will be valid as long as the chosen branch is valid, irrespective of the value of the other branch.

Finally, there is an explicit operation for testing if the value of an expression is undefined. oclIsUndefined() is an operation on OclAny that results in True if its argument is undefined and False otherwise.