A Comprehensive Guide to the Object Constraint Language (OCL)
Based on the provided lecture materials, this guide covers the fundamental concepts, syntax, and practical applications of the Object Constraint Language (OCL) in the context of UML modeling.

1. Introduction to OCL
Why do we need OCL?
While UML diagrams (like class diagrams) are excellent for visualizing structure, they cannot express all relevant aspects of a specification.
-
Natural language is ambiguous.
-
Traditional formal languages are unambiguous but too difficult for average system modelers to read and write.
-
OCL bridges this gap: it is a formal language used to express precise constraints, yet it remains easy to read and write.
Core Characteristics of OCL
-
Pure Expression Language: Expressions have no side effects. Evaluating an OCL expression returns a value but cannot alter the state of the system. It can, however, describe a state change (e.g., in a post-condition).
-
Not a Programming Language: You cannot write program logic, flow of control, or invoke processes/operations that alter state.
-
Typed Language: Every expression has a type. Well-formed expressions must obey type conformance rules. Every UML classifier is an OCL type.
-
Instantaneous Evaluation: The state of objects cannot change during the evaluation of an OCL expression.
2. Basic Syntax and Semantics
Context and Self
Every OCL expression is evaluated in the context of a specific UML classifier (class, interface, etc.).
-
context: Defines the class the constraint applies to. -
self: A reserved word referring to the specific instance of the context class being evaluated.
context Person
-- 'self' refers to a specific instance of Person
Comments and Operators
-
Comments: Denoted by
--(e.g.,-- this is a comment). -
Infix Operators: You can use standard math/logic operators (
+,-,=,<). Conceptually,a + bis identical toa.+(b).
Accessing Properties
Use dot notation to access attributes or operations. Parentheses are mandatory for operations, even if they have no parameters.
context Person
self.age -- Accesses the 'age' attribute
self.stockPrice() -- Calls the 'stockPrice' operation
3. Types and Collections
Basic Predefined Types
OCL includes standard types with specific operations:
-
Boolean:
true,false(Operations:and,or,not,implies,xor) -
Integer / Real: Numbers (Operations:
+,-,*,/,abs,max,min,div,mod) -
String: Text (Operations:
size,concat,substring,toInteger)
Collections
Collections are abstract types used to hold multiple elements. They are accessed using the -> notation.
-
Set: Mathematical set (no duplicates, unordered).
Set {1, 2, 5} -
OrderedSet: No duplicates, but ordered by position.
OrderedSet {5, 4, 3} -
Bag: Allows duplicates, unordered.
Bag {1, 2, 2, 5} -
Sequence: Allows duplicates, ordered.
Sequence {1, 2, 5, 10}-
Note: The
..notation creates a sequence of consecutive integers:Sequence {1..5}.
-
Key Collection Operations
-
Common:
size(),isEmpty(),notEmpty(),includes(v),excludes(v),count(v),sum(). -
Iterators (Crucial for OCL):
-
select(expr): Returns elements whereexpris true. -
reject(expr): Returns elements whereexpris false. -
collect(expr): Extracts a specific property from all elements into a new collection. -
forAll(expr): Returnstrueifexpris true for all elements. -
exists(expr): Returnstrueifexpris true for at least one element. -
iterate(elem; acc = init | expr): The generic base for all iterators.
-
Iterator Syntax Variations:
self.employee->select(age > 50)
self.employee->select(p | p.age > 50)
self.employee->select(p: Person | p.age > 50)
4. Navigating UML Models
OCL is heavily used as a navigation language to traverse associations.
Standard Navigation
Navigate using the opposite role name. The return type depends on multiplicity:
-
Multiplicity
1or0..1: Returns a single object. -
Multiplicity
*: Returns aSet(orOrderedSetif marked{ordered}).
context Company
self.manager.age > 40 -- manager is 0..1, returns a Person object
self.employee->notEmpty() -- employee is 0..*, returns a Set
If a role name is missing, use the lowercase name of the class at the other end (e.g., self.bank).
Association Classes
Navigate to an association class using the lowercase name of the association class.
context Person
self.job->size() >= 1 -- 'job' is the association class between Person and Company
Recursive Associations
When an association loops back to the same class (e.g., Marriage between two Persons), you must specify the direction using square brackets [roleName].
context Person
self.marriage[wife]->select(m | m.ended = false)->size() = 1
Qualified Associations
Use qualifier attributes in square brackets to select a specific object.
context Bank
self.customer[12345] -- Returns the specific Person with accountNo 12345
self.customer -- Returns a Set of all customers
Re-typing (Casting)
Use oclAsType() to access properties of a subtype or a hidden superclass property.
self.oclAsType(Sub).p -- Accesses property 'p' defined in Subtype
self.oclAsType(Super).p -- Accesses property 'p' defined in Superclass
5. Writing Constraints
1. Invariants (inv)
Conditions that must always be true for all instances of a class.
context Company
inv: self.noEmployees <= 50
inv SME: self.stockPrice() > 0 -- 'SME' is an optional name for the constraint
2. Pre- and Post-conditions (pre, post)
Constraints tied to operations.
-
pre: Must be true before execution. -
post: Must be true after execution. Useresultfor the return value. -
@pre: Used in post-conditions to reference the value of a property before the operation started.
context Person::income(): Integer
pre adult: self.age >= 18
post resultOK: result < 5000
context Company::hireEmployee(p: Person)
post: employee = employee@pre->including(p)
3. Body, Initial, and Derived Expressions
-
body: Defines the result of a query operation (can be recursive). -
init: Defines the initial value of an attribute. -
derive: Defines a derived attribute.
context Person::income(): Integer
body: self.job.salary->sum()
context Person::isMarried: Boolean
init: false
context Company::noEmployees: Integer
derive: self.employee->size()
6. Advanced OCL Features
Let and Def Expressions
-
let: Defines a local variable within a single expression.let totHours: Integer = self.worksOn->collect(hours)->sum() in totHours >= 30 -
def: Defines reusable variables or operations attached to a classifier.def: hasTitle(t: String): Boolean = self.job->exists(title = t)
Undefined Values (3-Valued Logic)
If a sub-expression is undefined (e.g., navigating a null association), the whole expression is undefined. However, Boolean operators have short-circuit exceptions:
-
true or <undefined>= true -
false and <undefined>= false -
false implies <undefined>= true -
<undefined> implies true= true
Class Features & Predefined Object Properties
-
allInstances(): Returns a Set of all existing instances of a class.Person.allInstances()->size() <= 100 -
Object properties:
oclIsTypeOf(Type),oclIsKindOf(Type),oclInState(State),oclIsNew(used in post-conditions).
7. Comprehensive Case Study: The Company Model
To solidify these concepts, here is a collection of integrity constraints applied to a Company database model (Employees, Departments, Projects, Dependents).
Basic Attribute Constraints
-- Age must be >= 18
context Employee inv: self.age() >= 18
-- Hire date must be after birth date
context Employee inv: self.hireDate > self.birthDate
-- Manager start date must be after hire date
context Employee inv: self.manages->notEmpty() implies self.manages.startDate > self.hireDate
Navigation and Relationship Constraints
-- Supervisor must be older and paid more than the employee
context Employee
inv: self.supervisor->notEmpty() implies self.age() > self.supervisor.age()
inv: self.supervisor->notEmpty() implies self.salary < self.supervisor.salary
-- A supervisor must be hired before their subordinates
context Employee
inv: self.subordinates->notEmpty() implies self.subordinates->forall(e | e.hireDate > self.hireDate)
-- Department manager must be an employee of that department
context Department
inv: self.worksFor->includes(self.manages.employee)
-- Project location must be one of the department's locations
context Project
inv: self.controls.locations->includes(self.location)
-- Employee can only work on projects controlled by their department
context Employee
inv: self.worksFor.controls->includesAll(self.worksOn.project)
Collection and Iterator Constraints
-- SSN must be a unique identifier across all employees
context Employee
inv: Employee.allInstances->forAll(e1, e2 | e1 <> e2 implies e1.SSN <> e2.SSN)
-- Dependent name and relationship must be unique per employee
context Employee
inv: self.dependents->forAll(e1, e2 |
e1 <> e2 implies (e1.name <> e2.name or e1.relationship <> e2.relationship))
-- Derived attribute: number of employees in a department
context Department
inv: self.nbrEmployees = self.worksFor->size()
-- Employee works on at most 4 projects
context Employee
inv: self.worksOn->size() <= 4
Complex Logic using let and select
-- Total working hours must be between 30 and 50 per week
context Employee
inv: let totHours: Integer = self.worksOn->collect(hours)->sum()
in totHours >= 30 and totHours <= 50
-- At most 2 employees can work < 10 hours on a specific project
context Project
inv: self.worksOn->select(hours < 10)->size() <= 2
-- Only department managers can work < 5 hours on a project
context Employee
inv: self.worksOn->select(hours < 5)->notEmpty() implies
self.worksFor.manages.employee = self
-- Employees without subordinates must work >= 10 hours on ALL their projects
context Employee
inv: self.subordinates->isEmpty() implies self.worksOn->forAll(hours >= 10)
-- Department manager must work >= 5 hours on all projects controlled by the dept
context Department
inv: self.controls->forall(p: Project |
self.manages.employee.worksOn->select(hours >= 5)->contains(p))
Preventing Cycles (Recursive Logic)
-- An employee cannot supervise themselves
context Employee inv: self.subordinates->excludes(self)
-- Supervision hierarchy must not be cyclic
context Employee
def: allSubordinates = self.subordinates->union(
self.subordinates->collect(e: Employee | e.allSubordinates))
inv: self.allSubordinates->excludes(self)

