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:


  1. 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.

  2. An OrderedSet is a set whose elements are ordered.

  3. 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.

  4. 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


  • All operations on collections are denoted in OCL expressions using an arrow


  • The operation following the arrow is applied to the collection before the arrow

  • This practice makes it possible for the user to define operations in the model that have the same names as the standard operations

  • The user-defined operation is taken when it follows a dot

  • The standard operation is taken when it follows an arrow

  • Collection operations do not change a collection, but they may result in a new collection
  • 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
    inv: account.isEmpty()
    context Membership
    inv: account->isEmpty()


    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}}
    Set { 1, 2, 3, 4, 5, 6 }
    (flattened)

    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



  • The equals and notEquals Operations

  • For sets, this means that all elements present in the first set must be present in the second set and vice versa

  • For ordered sets, an extra restriction specifies that the order in which the elements appear must also be the same

  • For two bags to be equal, not only must all elements be present in both, but the number of times an element is present must also be the same

  • For two sequences, the rules for bags apply, plus the extra restriction that the order of elements must be equal

  • The including and excluding Operations

  • The including operation results in a new collection with one element added to the original collection
  • For a bag, this description is completely true

  • If the collection is a set or ordered set, then the element is added only if it is not already present in the set

  • If the collection is a sequence or an ordered set, the element is added after all elements in the original collection

  • The excluding operation results in a new collection with an element removed from the original collection
  • From a set or ordered set, it removes only one element

  • From a bag or sequence, it removes all occurrences of the given object

  • The flatten Operation

  • The flatten operation changes a collection of collections into a collection of single objects


  • 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)

  • When the flatten operation is applied to a sequence or ordered set, the result is a sequence or ordered set (respectively)

  • Sequence {Set{1,2}, Set{2,3}, Set{4,5,6}}
    Sequence { 2, 1, 2, 3, 5, 6, 4 }
    (flatten)



  • The asSet, asSequence, asBag, and asOrderedSet Operations


  • Instances of all four concrete collection types can be transformed into instances of another concrete collection type

  • Applying asSet on a bag or asOrderedSet on a sequence means that of any duplicate elements, only one remains in the result

  • Applying asBag on a sequence or asSet on an ordered set means that the ordering is lost

  • Applying asOrderedSet or asSequence on a set or bag means that the elements are placed randomly in some order in the result

  • Applying the operation on the same original twice does not guarantee that both results will be equal

  • The union Operation

  • Combines two collections into a new one

  • The union of a set with a set will result in a set; any duplicate elements are added to the result only once

  • Combining a set with a bag (and vice versa) results in a bag

  • A sequence or ordered set may not be combined with either a set or a bag, only with another ordered collection

  • The intersection Operation

  • Results in another collection containing the elements in both collections

  • Valid for combinations of two sets, a set and a bag, or two bags

  • Not for combinations involving a sequence or ordered set

  • The minus Operation

  • Results in a new set containing all the elements in the set on which the operation is called, but not in the parameter set

  • This operation is defined for sets and ordered sets
  • When applied to an ordered set, the ordering remains

  • Set{1,4,7,10} - Set{4,7} = Set{1,10}
    OrderedSet{12,9,6,3} - Set{1,3,2} = OrderedSet{12,9,6}

  • The symmetricDifference Operation

  • Results in a set containing all elements in the set on which the operation is called, or in the parameter set, but not in both

  • This operation is defined on sets only

  • Set{1,4,7,10}.symmetricDifference(Set{4,5,7}) = Set{1,5,10}

    Operations on OrderedSets and Sequences Only
  • The first and last operations result in the first and the last elements of the collection, respectively

  • Sequence{'a','b','c','c','d','e'}->first()= 'a'
    OrderedSet{'a','b','c','d'}->last() = 'd'

  • The at operation results in the element at the given position


  • Sequence{'a','b','c','c','d','e'}->at(3)= 'c'


  • The indexOf operation results in an integer value that indicates the position of the element in the collection
  • When the element is present more than once in the collection, the result is the position of the first element

  • The index numbers start with one, not zero

  • Sequence{'a','b','c','c','d','e'}->indexOf( 'c' ) = 3

  • The insertAt operation results in a sequence or ordered set that has an extra element inserted at the given position

    OrderedSet{'a','b','c','d'}->insertAt(3,'X’)= OrderedSet{'a','b','X','c','d'}


  • The subSequence operation may be applied to sequences only


  • Results in a sequence that contains the elements from the lower index to the upper index, inclusive, in the original order

  • Sequence{'a','b','c','c','d','e'}-> subSequence(3,5)= Sequence{'c','c','d'}


  • The subOrderedSet operation may be applied to ordered sets only


  • Its result is equal to the subSequence operation, although it results in an ordered set instead of a sequence

  • OrderedSet{'a','b','c','d'}->subOrderedSet( 2, 3 ) = OrderedSet{'b','c'}


  • The append and prepend operations add an element to a sequence as the last or first element, respectively

  • 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
  • An iterator variable is a variable that is used within the body parameter to indicate the element of the collection for which the body parameter is being calculated

  • The type of this iterator variable is always the type of the elements in the collection

  • Because the type is known, it may be span omitted in the declaration of the iterator variable

  • 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

  • We can demand an ordering on the elements of any collection using the sortedBy operation

  • The parameter of this operation is a property of the type of the elements in the collection

  • For this property, the lesserThan operation (denoted by <) must be defined

  • The result is a sequence or ordered set, depending on the type of the original collection

  • Loop over all elements in the original collection and order all elements according to the value derived from calculating the parameter property

  • The first element in the result is the element for which the property is the lowest


  • context LoyaltyProgram
    def: sortedAccounts:Sequence(LoyaltyAccount) = self.Membership.account->sortedBy(number)


    The select Operation

  • The select operation enables us to specify a selection from the original collection

  • The result of the select operation is always a proper subset of the original collection

  • The parameter of the select operation is a boolean expression that specifies which elements we want to select from the collection

  • The result of select is the collection that contains all elements for which the boolean expression is true

  • context CustomerCard
    inv: self.transactions->select(points>100)->notEmpty()

  • The result of select can be described by the following pseudocode:

  • element = collection.firstElement();
    while(collection.notEmpty()) do
    if()
    then
    result.add(element);
    endif
    element = collection.nextElement();
    endwhile
    return result;


    The reject Operation
  • The reject operation is analogous to select, with the distinction that reject selects all elements from the collection for which the expression evaluates to false

  • The existence of reject is merely a convenience

  • The following two invariants are semantically equivalent:


  • context Customer
    inv: Membership.account->select(points>0)

    context Customer
    inv: Membership.account-> reject(not(points>0))


    The any Operation

  • To obtain any element from a collection for which a certain condition holds

  • The body parameter of this operation is a boolean expression

  • The result is a single element of the original collection

  • If the condition holds for more than one element, one of them is randomly chosen

  • If the condition does not hold for any element in the source collection, the result is undefined

  • self.Membership.account->any(number<10000)


    The forAll Operation

  • To specify that a certain condition must hold for all elements of a collection

  • The result is a boolean value

  • It is true if the expression is true for all elements of the collection

  • If the expression is false for one or more elements in the collection, then forAll results in false

  • context LoyaltyProgram
    inv: participants->forAll(age()<=70)


  • The forAll operation has an extended variant in which multiple iterator variables can be declared

  • 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)


  • Although the number of iterators is unrestricted, more than two iterators are seldom used

  • The multiple iterators are allowed only with the forAll operation and not with any other operation that uses iterators


  • The exists Operation

  • To specify that there is at least one object in a collection for which a certain condition holds

  • The result is a boolean

  • It is true if the expression is true for at least one element of the collection

  • If the expression is false for all elements in the collection, then the exists operation results in false

  • context LoyaltyAccount
    inv: points > 0 implies transactions-> exists(t ▏t.points > 0)


  • There is a relationship between the exists and the forAll operations

  • collection->exists(‹expression›)
    not collection->forAll(not ‹expression›)


    The one Operation

  • The one operation gives a boolean result stating whether there is exactly one element in the collection for which a condition holds

  • If there is exactly one such element, then the result is true

  • Otherwise, the result is false

  • context LoyaltyProgram
    inv: self.Membership.account-> one(number<10000)


  • The difference between the any and one operations

  • The any operation can be seen as a variant of the select operation

  • Its result is an element selected from the source collection

  • The one operation is a variant of the exists operation

  • Its result is either true or false depending on whether or not a certain element exists in the source collection


  • The collect Operation

  • Iterates over the collection, computes a value for each element of the collection, and gathers the evaluated values into a new collection

  • The type of the elements in the resulting collection is usually different from the type of the elements in the collection on which the operation is applied

  • The result of the collect operation on a set or bag is a bag and on an ordered set or sequence, the result is a sequence

  • The result of the collect is always a flattened collection


  • context LoyaltyAccount
    inv: transactions->collect( points )-> exists( p : Integer ▏p = 500 )

    context LoyaltyAccount
    inv: transactions.points->exists(p : Integer ▏p = 500 )


    The collectNested Operation

  • Iterates over the collection, like the collect operation

  • Whereas the result of the collect is always a flattened collection

  • The result of the collectNested operation maintains the nested structure of collections within collections

  • self.programs->collect(partners)-> collectNested( deliveredServices )
    the type of this expression is Bag(Set(Service))


    The iterate Operation

  • The iterate operation is the most fundamental and complex of the loop operations

  • At the same time, it is the most generic loop operation

  • The syntax of the iterate operation is as follows:

  • collection->iterate( element : Type1; result:Type2 = ‹expression› ▏‹expression-with-element-and-result›)


  • The resulting value is accumulated in the variable result, which is also called the accumulator

  • The accumulator gets an initial value, given by the expression after the equal sign

  • None of the parameters is optional

  • Set{1,2,3}->iterate( i: Integer, sum: Integer = 0 ▏sum + i )

  • Suppose that the class ProgramPartner needs a query operation that returns all transactions on services of all partners that burn points

  • 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:


  • Predefined types, as defined in the standard library, including the following:

  • Basic types: Integer, Real, String, and Boolean

  • Collection types: Collection, Set, Bag, OrderedSet, and Sequence


  • User-defined types

  • Defined by the user in the UML diagrams
  • Every instantiable model element in a UML diagram is automatically a type in OCL


  • E.g., each class, interface, component, or datatype


  • 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-else
    if ‹boolean OCL expression›
    then ‹OCL expression›
    else ‹OCL expression›
    endif


    The Integer and Real Types

    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 operation
    context 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))
    技術提供:Blogger.