Section B.3. Advanced OCL Modeling


B.3. Advanced OCL Modeling

Like any other language, OCL has an order of precedence for operators, variable declarations, and logical constructs (only for evaluating your expressions, not for program flow). The following sections describe constructs that you can use in any OCL expression.

B.3.1. Conditionals

OCL supports basic boolean expression evaluation using the if-then-else-endif keywords. The conditions are used only to determine which expression is evaluated; they can't be used to influence the underlying system or to affect program flow. The following invariant enforces that a student's year of graduation is valid only if she has paid her tuition:

     context Student inv:     if tuitionPaid = true then       yearOfGraduation = 2005     else       yearOfGraduation = 0000     endif

OCL's logic rules are slightly different from typical programming language logic rules. The boolean evaluation rules are:

  1. True OR-ed with anything is true.

  2. False AND-ed with anything is false.

  3. False IMPLIES anything is TRue.

The implies keyword evaluate the first half of an expression, and, if that first half is true, the result is taken from the second half. For example, the following expression enforces that if a student's GPA is less than 1.0, their year of graduation is set to 0. If the GPA is higher than 1.0, Rule #3 applies, and the entire expression is evaluated as true (meaning the invariant is valid).

     context Student inv:     self.GPA < 1.0 IMPLIES self.yearOfGraduation = 0000

OCL's boolean expressions are valid regardless of the order of the arguments. Specifically, if the first argument of an AND operator is undefined, but the second operator is false, the entire expression is false. Likewise, even if one of the arguments to an OR operator is undefined, if the other is true, the expression is true. If-then-else-endif constructs are evaluated similarly; if the chosen branch can be evaluated to TRue or false, the nonchosen branch is completely disregarded (even if it would be undefined).

B.3.2. Variable Declaration

OCL supports several complex constructs you can use to make your constraints more expressive and easier to write. You can break complex expressions into reusable pieces (within the same expression) by using the let and in keywords to declare a variable. You declare a variable by giving it a name, followed by a colon (:), its type, an expression for its value, and the in keyword. The following example declares an expression that ensures a teacher of a high-level course has an appropriate salary:

     context Course inv:     let salary : float = self.instructor.salary in     if self.courseLevel > 4000 then       salary > 80000.00     else       salary < 80000.00     endif

You can define variables that can be used in multiple expressions on a classifier-by-classifier basis using the def keyword. For example, instead of declaring salary as a variable using the let keyword, you can define it using the def keyword for the Course context. Once you define a variable using the def keyword, you may use that variable in any expression that is in the same context. The syntax for the def keyword is the same as that for the let keyword:

     context Course     def: salary : float = self.instructor.salary

So, now you can write the previous invariant as:

     context Course inv:     if self.courseLevel > 4000 then       salary > 80000.00     getHonorsStudentselse       salary < 80000.00     endif

B.3.3. Operator Precedence

OCL operators have the following order of precedence (from highest to lowest):

  • @pre

  • dot (.) and arrow (->) operations

  • not and unary minus (-)

  • * and /

  • + and -

  • if-then-else-endif

  • <, >, <=, and >=

  • = and <>

  • and, or, and xor

  • implies

You can use parentheses to group expressions, which will be evaluated from the innermost set of parentheses to the outermost.

B.3.4. Built-in Object Properties

OCL provides a set of properties on all objects in a system. You can invoke these properties in your expressions as you do any other properties. The built-in properties are:


oclIsTypeOf (t : Type) : Boolean

Returns true if the tested object is exactly the same type as t.


oclIsKindOf( t : Type) : Boolean

Returns TRue if the tested object is the same type or a subtype of t.


oclInState(s : State) : Boolean

Returns TRue if the tested object is in state s. The states you can test must be part of a state machine attached to the classifier being tested.


oclIsNew( ) : Boolean

Designed to be used in a postcondition for an operation, it returns true if the object being tested was created as a result of executing the operation.


oclAsType (t : Type) : Type

Returns the owning object casted to Type. If the object isn't a descendant of t, the operation is undefined.

Here are some examples of the built-in properties:

     -- test that the instructor is an instance of Teacher     context Course     inv: self.instructor.oclIsTypeOf(Teacher)     -- cast a Date class to a java.sql.Date to verify the minutes     -- (it's very unlikely the foundationDate would be a java.sql.Date     --  so this invariant would be undefined, but this is an example     --  of using oclAsType(  ))     context School     inv: self.foundationDate.oclAsType(java.sql.Date).getMinutes(  ) = 0

B.3.5. Collections

OCL defines several types of collections that represent several instances of a classifier. The basic type is Collection, which is the base class for the other OCL collection classes. Quite a few operations are defined for Collection; see the OCL specification for the complete list.

All collections support a way to select or reject elements using the operations select( ) and reject( ), respectively. To invoke an operation on a collection, you use an arrow (->) rather than a dot (.) (a dot accesses a property). The result of select or reject is another collection containing the appropriate elements. Remember that since OCL can't modify a system in any way, the original collection is unchanged. The notation for a select is:

     collection->select(boolean expression)

So, to select students with GPAs higher than 3.0, you can use the expression:

     context Course::getHonorsStudents(  ) : Student     body: self.students->select(GPA > 3.0)

Or, to eliminate honor students that haven't paid their tuition:

     context Course::getHonorsStudents(  ) : Student     body: self.students->select(GPA > 3.0)->reject(tuitionPaid = false)

In the previous examples, the context for the select and reject statements was implied. You can explicitly name the element you want to use in the boolean expression by prefixing the expression with a label and a pipe symbol (|). So, a rewrite of the GPA example using a label to identify each student looks like this:

     context Course::getHonorsStudents(  ) : Student     body: self.students->select(curStudent | curStudent.GPA > 3.0)

Finally, you can specify the type of the element you want to evaluate. You indicate the type by placing a colon (:) and the classifier type after the label. Each element of the collection you are evaluating must be of the specified type, or else the expression is undefined. You can rewrite the GPA example to be even more specific and require that it evaluate only Students:

     context Course::getHonorsStudents(  ) : Student     body: self.students->select(curStudent : Student | curStudent.GPA > 3.0)

Often you will need to express a constraint across an entire collection of objects, so OCL provides the forAll operation that returns true if a given Boolean expression evaluates to true for all of the elements in a collection. The syntax for forAll is the same as that for select and reject. So, you can write a constraint that enforces that all students in a Course have paid their tuition:

     context Course     inv: self.students->forAll(tuitionPaid = true)

As you can with select, you can name and type the variable used in the expression:

     context Course     inv: self.students->forAll(curStudent : Student | curStudent.tuitionPaid = true)

If you need to check to see if there is at least one element in a collection that satisfies a boolean expression, you can use the operation exists. The syntax is the same as that for select. The following expression ensures that at least one of the students has paid their tuition:

     context Course     inv: self.students->exists(tuitionPaid = true)

Like select, you can name and type the variables used in the expression:

     context Course     inv: self.students->exists(curStudent : Student | curStudent.tuitionPaid = true)

You can check to see if a collection is empty using the operations isEmpty or notEmpty. The following expression ensures that the school has at least one course offering:

     context School     inv: self.Course->notEmpty(  )




UML 2.0 in a Nutshell
UML 2.0 in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596007957
EAN: 2147483647
Year: 2005
Pages: 132

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net