2007年8月14日 星期二
Collection Types
The Collection Types
There are five collection types. Four of them—the Set, OrderedSet, Bag, and Sequence types—are concrete types and can be used in expressions. The fifth, the Collection type, is the abstract supertype of the other four and is used to define the operations common to all collection types.
The four concrete collection types are defined as follows:
- A Set is a collection that contains instances of a valid OCL type. A set does not contain duplicate elements; any instance can be present only once. Elements in a set are not ordered.
- An OrderedSet is a set whose elements are ordered.
- A Bag is a collection that may contain duplicate elements. A bag is typically the result of combining navigations. Elements in a bag are not ordered.
- A Sequence is a bag whose elements are ordered.
(Note that a value of type Sequence or OrderedSet is ordered and not sorted.)Set { 1 , 2 , 5 , 88 }
Set { 'apple' , 'orange', 'strawberry' }
OrderedSet { 'apple' , 'orange', 'strawberry', 'pear' }
Sequence { 1, 3, 45, 2, 3 }
Sequence { 'ape', 'nut' }
Bag {1 , 3 , 4, 3, 5 }
Sequence{ 1..(6 + 4) }
Sequence{ 1..10 }
-- are both identical to
Sequence{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
Collection Type Expressions
When the type of an element is a collection, this can be indicated using the words Set, OrderedSet, Bag, or Sequence, and the type of the elements of the collection between rounded brackets, as shown in the following examples:Set(Customer)
Sequence(Set(ProgramPartners))
OrderedSet(ServiceLevel)
Bag(Burning)
Collection Operations
context LoyaltyProgram
inv: self.participants->size()
Treating Instances as Collections
Because the OCL syntax for applying collection operations is different from that for user-defined type operations, you can use a single instance as a collection. This collection is considered to be a set with the instance as the only element.
| As an instance: | As a collection: |
context Membership | context Membership |
Collections of Collections
When a collection is inserted into another collection, the resulting collection is automatically flattened; the elements of the inserted collection are considered direct elements of the resulting collection.Set {Set{1,2}, Set{3,4}, Set{5,6}}(flattened)
Set { 1, 2, 3, 4, 5, 6 }
Operations on Collection Types
Standard operations on all collection types
In the invariant, you specify that the actual service level of a membership must be one of the service levels of the program to which the membership belongs:context Membership
inv: programs.levels ->includes(currentLevel)
The following invariant specifies that the available services for a service level must be offered by a partner of the loyalty program to which the service level belongs:context ServiceLevel
inv:program.partners->includesAll(self.availableServices.partner)
Operations with Variant Meaning
Collection operations with variant meaning
Set {Set{1,2}, Set{2,3}, Set{4,5,6}}
Set { 1, 2, 3, 4, 5, 6 }(flatten)Bag {Set{1,2}, Set{1,2}, Set{4,5,6}}
Bag { 1, 1, 2, 2, 4, 5, 6 }(flatten)Sequence {Set{1,2}, Set{2,3}, Set{4,5,6}}
Sequence { 2, 1, 2, 3, 5, 6, 4 }(flatten)Set{1,4,7,10} - Set{4,7} = Set{1,10}
OrderedSet{12,9,6,3} - Set{1,3,2} = OrderedSet{12,9,6}Set{1,4,7,10}.symmetricDifference(Set{4,5,7}) = Set{1,5,10}Operations on OrderedSets and Sequences Only
Sequence{'a','b','c','c','d','e'}->first()= 'a'
OrderedSet{'a','b','c','d'}->last() = 'd'Sequence{'a','b','c','c','d','e'}->at(3)= 'c'
Sequence{'a','b','c','c','d','e'}->indexOf( 'c' ) = 3OrderedSet{'a','b','c','d'}->insertAt(3,'X’)= OrderedSet{'a','b','X','c','d'}Sequence{'a','b','c','c','d','e'}-> subSequence(3,5)= Sequence{'c','c','d'}OrderedSet{'a','b','c','d'}->subOrderedSet( 2, 3 ) = OrderedSet{'b','c'}
Sequence{'a','b','c','c','d','e'}-> append('X’)= Sequence{'a','b','c','c','d','e','X'}
Sequence{'a','b','c','c','d','e'}-> prepend('X’)= Sequence{'X','a','b','c','c','d','e'}
Loop Operations or Iterators
Loop operations on all collection types

Iterator Variables
context LoyaltyProgram
inv: self.Membership.account->isUnique(accacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(acc: LoyaltyAccountacc.number)
context LoyaltyProgram
inv: self.Membership.account->isUnique(number)
The sortedBy Operation
context LoyaltyProgram
def: sortedAccounts:Sequence(LoyaltyAccount) = self.Membership.account->sortedBy(number)The select Operation
context CustomerCard
inv: self.transactions->select(points>100)->notEmpty()
element = collection.firstElement();
while(collection.notEmpty()) do
if()
then
result.add(element);
endif
element = collection.nextElement();
endwhile
return result;
The reject Operation
context Customer
inv: Membership.account->select(points>0)
context Customer
inv: Membership.account-> reject(not(points>0))The any Operation
self.Membership.account->any(number<10000)The forAll Operation
context LoyaltyProgram
inv: participants->forAll(age()<=70)context LoyaltyProgram
inv: self.participants->forAll(c1 ▏self.participants->forAll( c2 ▏c1 <> c2 implies c1.name <> c2.name ))
context LoyaltyProgram
inv: self.participants->forAll(c1, c2 ▏c1 <> c2 implies c1.name <> c2.name)The exists Operation
context LoyaltyAccount
inv: points > 0 implies transactions-> exists(t ▏t.points > 0)collection->exists(‹expression›)
not collection->forAll(not ‹expression›)The one Operation
context LoyaltyProgram
inv: self.Membership.account-> one(number<10000)The collect Operation
context LoyaltyAccount
inv: transactions->collect( points )-> exists( p : Integer ▏p = 500 )
context LoyaltyAccount
inv: transactions.points->exists(p : Integer ▏p = 500 )The collectNested Operation
self.programs->collect(partners)-> collectNested( deliveredServices )the type of this expression is Bag(Set(Service))
The iterate Operation
collection->iterate( element : Type1; result:Type2 = ‹expression› ▏‹expression-with-element-and-result›)Set{1,2,3}->iterate( i: Integer, sum: Integer = 0 ▏sum + i )context ProgramPartner
def:getBurningTransactions():Set(Transaction) =
self.deliveredServices.transactions->iterate(
t: Transaction;
resultSet:Set(Transaction) = Set{}
if t.oclIsTypeOf(Burning) then
resultSet.including(t)
else
resultSet
endif
)
2007年7月17日 星期二
Basic OCL Elements
Expressions, Types, and Values
Types in OCL are divided into the following groups:Basic Types and Operators
The Boolean Type
Example: The result of the following sample expression is true if for every service it can be said that when it offers bonus points it never burns bonus points
context Service
inv: self.pointsEarned > 0 implies not (self.pointsBurned = 0)
Another interesting operation on the Boolean type is the if-then-elseif ‹boolean OCL expression›
then ‹OCL expression›
else ‹OCL expression›
endif
All these examples are expressions of the Boolean type, which result in true:
2654 * 4.3 + 101 = 11513.2
(3.2).floor() / 3 = 1
1.175 * (-8.9).abs() - 10 = 0.4575
12 > 22.7 = false
12.max(33) = 33
33.max(12) = 33
13.mod(2) = 1
13.div(2) = 6
33.7.min(12) = 12.0
-24.abs() = 24
(-2.4).floor() = -3

The String Types
All these examples are expressions of the Boolean type, and result in true:
'Anneke'.size() = 6
('Anneke' = 'Jos') = false
'Anneke '.concat('and Jos') = 'Anneke and Jos'
'Anneke'.toUpper() = 'ANNEKE'
'Anneke'.toLower() = 'anneke'
'Anneke and Jos'.substring(12, 14) = 'Jos'

Precedence Rules

Comments
An OCL line comment begins with two hyphens. All text from the hyphens to the end of the line is considered to be a comment. Comments longer than one line may be enclosed between /* and */.
-- the expression 20 * 5 + 4 should be evaluated here
20 * 5 + 4 -- this is a comment
/* this is a very long comment that does not enlighten the reader one bit about what the expression is really about */
20 * -- this is a comment 5 + 4 (invalid OCL expression) 2007年7月11日 星期三
The Context of OCL Expression
The link between an entity in a UML diagram and an OCL expression is called the context definition of that OCL expression.
The Context of OCL Expression
The context definition specifies the model entity for which the OCL expression is defined. It is always a specific element defined in a UML diagram, e.g. a class, interface, datatype, or component. This element is called the context of the expression.
context Customer
inv: name = 'Edward'
The self Keyword
The keyword self is used to refer explicitly to the contextual instance. Whenever the reference to the contextual instance is obvious, the use of the keyword self is optional.
context Customer
inv: self.name = 'Edward'
More Than One Expression to a Context
The following two examples have exactly the same meaning:
context Customer
inv: self.name = 'Edward'
inv: self.title = 'Mr.'
context Customer
inv: self.name = 'Edward' and self.title = 'Mr.'
The following two sets of pre- and postconditions have the same meaning as well:
context LoyaltyProgram::addService( p:ProgramPartner,
l:ServiceLevel,
s:Service)
pre: partners->includes(p)
pre: levels->includes(l)
post: partners.deliveredServices->includes(s)
post: levels.availableServices->includes(s)
context LoyaltyProgram::addService(p:ProgramPartner, l:ServiceLevel, s:Service)
pre: partners->includes() and levels->includes(l)
post: partners.deliveredServices->includes(s) and levels.availableServices->includes(s)
Classes and Other Types
Invariants
The first way in which an expression with a type as context can be used is as an invariant. An invariant is described using a boolean expression that evaluates to true if the invariant is met. To indicate that the expression is intended to be an invariant, the context declaration is followed by the keyword inv, an optional name, and a colon, as shown in the following example:
context Customer
inv myInvariant23: self.name = 'Edward‘
An invariant may be named, which can be useful for reference in an accompanying text
Definitions of Attributes or Operations
Every instance of the contextual type holds an attribute or operation that conforms to the given definition. To indicate that the expression is intended to be a definition, the context declaration is followed by the keyword def and a colon, as shown in the following examples. In the case of an attribute definition, the name and type of the attribute must be given. The expression following the equal sign is also mandatory. This expression indicates how the value of the attribute must be calculated.
context Customer
def: initial : String = name.substring(1,1)
All operations defined by an OCL expression are considered to be query operations. The name, parameters (including their types), and the return type (if any) of the operation must be given. The expression following the equal sign is also mandatory, and states the result of the operationcontext CustomerCard
def: getTotalPoints(d: Date): Integer = transactions-> select(date.isAfter(d)).points->sum()
Attributes and Association Ends
Derivation Rules
An expression whose context is an attribute or association role may be used as a derivation rule. If the context is an attribute, the contextual type is the type that holds the attribute. If the context is an association end, the contextual type is the type at the opposite end of the association.
context LoyaltyAccount::totalPointsEarned : Integer
derive: transactions->select(oclIsTypeOf(Earning)).points->sum()
context CustomerCard::myLevel:ServiceLevel
derive: Membership.currentLevel
Initial Values
An initial value is the value that the attribute or association end will have at the moment that the contextual instance is created. The context declaration is followed by the keyword init, the name of the attribute, and the expression that gives the initial value, as shown in the following two examples:
context CustomerCard::transactions : Set(Transaction)
init: Set{}
context CustomerCard::valid:Boolean
init: true
The difference between an initial value and a derivation rule. A derivation rule states an invariant: The derived element should always have the same value that the rule expresses. An initial value must hold only at the moment when the contextual instance is created. After that moment, the attribute may have a different value at any point in time.
Operation
Preconditions and Postconditions
The first two ways in which expressions may be used for operations are pre- and postconditions: two forms of constraints. A precondition is a boolean expression that must be true at the moment when the operation starts its execution. A postcondition is a boolean expression that must be true at the moment when the operation ends its execution. A precondition specifies that the expression must evaluate to true; otherwise, the operation will not be executed.
context Type1::operation(arg: Type2): ReturnType
pre : -- some expression using the param arg and features of the
pre : -- contextual type
post: -- some expression using the param arg, features of the
post: -- contextual type, the @pre keyword, and messaging
post: -- expressions
Body of Query Operations
The context is indicated in the same manner as for pre- and postconditions. Instead of the keywords pre or post, the keyword body is used, followed by the body expression:
context CustomerCard::getTransactions (from: Date, until: Date): Set(Transaction)
body: transactions->select(date.isAfter(from) and date.isBefore(until))
