Issue 19146: Specify List::reject and other iterations (qvt-rtf) Source: Model Driven Solutions (Dr. Edward Willink, ed(at)willink.me.uk) Nature: Clarification Severity: Minor Summary: The current specification of List has a vague claim that all OCL Collection operations are available. Are iterations available? Are Sequence operations and iterations not available? Are return types adjusted to List? The specification needs to provide a clear model defining all the operations and iterations. Resolution: Specifying the operations is straightforward, but highlights some nasty inconsistencies such as two versions of insertAt. The OCL 2.4 operations are included. From Issue 13223 clarify wording of insertAt: From Issue 13251 add remove operations. A model must wait till OCL 2.5 provides an extensible library model Revised Text: <<Issue 13182 revises redundant (T) in some signatures.>> Replace 8.3.8 Operations On List All operations of the OCL Collection type are available. In addition the following are available. add List(T)::add(T) : Void Adds a value at the end of the mutable list. Synonym: append prepend List(T)::prepend(T) : Void Adds a value at the beginning of the mutable list. insertAt List(T)::insertAt(T,int) : Void Adds a value at the given position. The index starts at zero (in compliance with OCL convention). joinfields List(T)::joinfields(sep:String,begin:String,end:String) :String Creates a string separated by sep and delimited with begin and end strings. asList Set(T)::asList() : List(T) OrderedSet(T)::asList(T) : List(T) Sequence(T)::asList(T) : List(T) Bag(T)::asList(T) : List(T) Converts a collection into the equivalent mutable list. by The following operations can be invoked on any list. A list type is a parameterized type. The symbol T denotes the type of the values. The operations include all those of the OCL Sequence type. = List(T)::=(s : List(T)) : Boolean True if self contains the same elements as s in the same order. post: result = (size() = s->size()) and Sequence{1..size()}->forAll(i | at(i) = s->at(i)) <> List(T)::<>(c : List(T)) : Boolean True if c is not equal to self. post: result = not (self = c) add List(T)::add(T) : Void Adds a value at the end of the mutable list. post: size() = size@pre() + 1 post: Sequence{1..size@pre()}->forAll(i | at(i) = at@pre(i)) post: at(size()) = object append List(T)::append(object: T) : List(T) Returns a new list of elements, consisting of all elements of self, followed by object. post: result->size() = size() + 1 post: Sequence{1..size()}->forAll(i | result->at(i) = at(i)) post: result->at(result->size()) = object asBag List(T)::asBag() : Bag(T) Returns a Bag containing all the elements from self, including duplicates. The element order is indeterminate. post: result->forAll(elem | self->count(elem) = result->count(elem)) post: self->forAll(elem | self->count(elem) = result->count(elem)) asList List(T)::asList(T) : List(T) Returns a new list that is a shallow clone of self. post: Sequence{1..size()}->forAll(i | result->at(i) = self->at(i)) asOrderedSet List(T)::asOrderedSet() : OrderedSet(T) Returns a OrderedSet that contains all the elements from self, in the same order, with duplicates removed. post: result->forAll(elem | self ->includes(elem)) post: self->forAll(elem | result->count(elem) = 1) post: self->forAll(elem1, elem2 | self->indexOf(elem1) < self->indexOf(elem2) implies result->indexOf(elem1) < result->indexOf(elem2) ) asSequence List(T)::asSequence() : Sequence(T) Returns a Sequence that contains all the elements from self, in the same order. post: result->size() = size() post: Sequence{1..size()}->forAll(i | result->at(i) = self->at(i)) asSet List(T)::asSet() : Set(T) Returns a Set containing all the elements from self, with duplicates removed. The element order is indeterminate. post: result->forAll(elem | self->includes(elem)) post: self->forAll(elem | result->includes(elem)) at List(T)::at(i : Integer) : T Returns the element of the list at the i-th one-based index. pre : 1 <= i and i <= size() clone List(T)::clone(T) : List(T) Returns a new list that is a shallow clone of self. post: Sequence{1..size()}->forAll(i | result->at(i) = self->at(i)) count List(T)::count(object : T) : Integer Returns the number of occurrences of object in self. deepclone List(T)::deepclone(T) : List(T) Returns a new list that is a deep clone of self; that is a new list in which each element is in turn a deepclone of the corresponding element of self. excludes List(T)::excludes(object : T) : Boolean True if object is not an element of self, false otherwise. post: result = (self->count(object) = 0) excludesAll List(T)::excludesAll(c2 : Collection(T)) : Boolean Does self contain none of the elements of c2 ? post: result = c2->forAll(elem | self->excludes(elem)) excludesAll List(T)::excludesAll(c2 : List(T)) : Boolean Does self contain none of the elements of c2 ? post: result = c2->forAll(elem | self->excludes(elem)) excluding List(T)::excluding(object : T) : List(T) Returns a new list containing all elements of self apart from all occurrences of object. The order of the remaining elements is not changed. post:result->includes(object) = false post: result->size() = self->size()@pre - self->count(object)@pre post: result = self->iterate(elem; acc : List(T) = List{} | if elem = object then acc else acc->append(elem) endif ) first List(T)::first() : T Returns the first element in self. post: result = at(1) flatten List(T)::flatten() : List(T2) Returns a new list containing the recursively flattened contents of the old list. The order of the elements is partial. post: result = self->iterate(c; acc : List(T2) = List{} | if c.oclType().elementType.oclIsKindOf(CollectionType) then acc->union(c->flatten()->asList()) else acc->union(c) endif) includes List(T)::includes(object : T) : Boolean True if object is an element of self, false otherwise. post: result = (self->count(object) > 0) includesAll List(T)::includesAll(c2 : Collection(T)) : Boolean Does self contain all the elements of c2 ? post: result = c2->forAll(elem | self->includes(elem)) includesAll List(T)::includesAll(c2 : List(T)) : Boolean Does self contain all the elements of c2 ? post: result = c2->forAll(elem | self->includes(elem)) including List(T)::including(object : T) : List(T) Returns a new list containing all elements of self plus object added as the last element. post: result = append(object) indexOf List(T)::indexOf(obj : T) : Integer The one-based index of object obj in the list. pre : includes(obj) post : at(result) = obj insertAt List(T)::insertAt(index : Integer, object : T) : List(T) Returns a new list consisting of self with object inserted at the one-based position index. pre : 1 <= index and index <= size() post: result->size() = size() + 1 post: Sequence{1..(index - 1)}->forAll(i | result->at(i) = at(i)) post: result->at(index) = object post: Sequence{(index + 1)..size()}->forAll(i | result->at(i + 1) = at(i)) insertAt List(T)::insertAt(object : T, index : Integer) : Void The list is modifed to consist of self with object inserted at the one-based position index. pre : 1 <= index and index <= size() post: size() = size@pre() + 1 post: Sequence{1..(index - 1)}->forAll(i | at(i) = at@pre(i)) post: at(index) = object post: Sequence{(index + 1)..size()}->forAll(i | at(i) = at@pre(i - 1)) isEmpty List(T)::isEmpty() : Boolean Is self an empty list? post: result = (self->size() = 0) joinfields List(T)::joinfields(sep:String,begin:String,end:String) :String Creates a string separated by sep and delimited with begin and end strings. post: result = begin + Sequence{1..size()}->iterate(i; acc : String = '' | acc + if i = 1 then '' else sep endif + at(i).toString()) + end last List(T)::last() : T Returns the last element in self. post: result = at(size()) max List(T)::max() : T The element with the maximum value of all elements in self. Elements must be of a type supporting the max operation. The max operation - supported by the elements - must take one parameter of type T. Integer and Real fulfill this condition. post: result = self->iterate(elem; acc : T = self->any(true) | acc.max(elem)) min List(T)::min() : T The element with the minimum value of all elements in self. Elements must be of a type supporting the min operation. The min operation - supported by the elements - must take one parameter of type T. Integer and Real fulfill this condition. post: result = self->iterate(elem; acc : T = self->any(true) | acc.min(elem)) notEmpty List(T)::notEmpty() : Boolean Is self not an empty list? post: result = (self->size() <> 0) prepend List(T)::prepend(object : T) : List(T) Returns a new list consisting of object, followed by all elements in self. post: result->size = size() + 1 post: result->at(1) = object post: Sequence{1..size()}->forAll(i | result->at(i + 1) = at(i)) product List(T)::product(c2: Collection(T2)) : Set(Tuple(first: T, second: T2)) The cartesian product operation of self and c2. post: result = self->iterate(e1; acc: Set(Tuple(first: T, second: T2)) = Set{} | c2->iterate(e2; acc2: Set(Tuple(first: T, second: T2)) = acc | acc2->including(Tuple{first = e1, second = e2}))) remove List(T)::remove(element : T) : Void Removes .all elements from self equal to element. post: result = self@pre->reject(e = element) removeAll List(T)::removeAll(elements : Collection(T)) : Void Removes .all elements from self equal to any of elements. post: result = self@pre->reject(e | elements->includes(e)) removeAll List(T)::removeAll(elements : List(T)) : Void Removes .all elements from self equal to any of elements. post: result = self@pre->reject(e | elements->includes(e)) removeAt List(T)::removeAt(index : Integer) : T Removes .and returns .the list element at index. Returns invalid for an invalid index. pre: 1 <= index and index <= size() post: size() = size@pre() - 1 post: Sequence{1..index}->forAll(i | at(i) = at@pre(i)) post: Sequence{(index+1)..size()}->forAll(i | at(i) = at@pre(i+1)) post: result = at@pre(index) removeFirst List(T)::removeFirst() : T Removes .and returns .the first list element. Returns invalid for an empty list. pre: 1 <= size() post: size() = size@pre() - 1 post: Sequence{1..size()}->forAll(i | at(i) = at@pre(i+1)) post: result = at@pre(1) removeLast List(T)::removeLast() : T Removes .and returns .the last list element. Returns invalid for an empty list. pre: 1 <= size() post: size() = size()@pre - 1 post: Sequence{1..size()}->forAll(i | at(i) = at@pre(i)) post: result = at@pre(size@pre()) reverse List(T)::reverse() : List(T) Returns a new list containing the same elements but with the opposite order. post: result->size() = self->size() post: Sequence{1..size()}->forAll(i | result->at(i) = at(size() - (i-1))) selectByKind List(T)::selectByKind(type : Classifier) : List(T1) Returns a new list containing the non-null elements of self whose type is type or a subtype of type. The returned list element type T1 is the type specified as type. post: result = self ->collect(if oclIsKindOf(type) then oclAsType(type) else null endif) ->excluding(null) selectByType List(T)::selectByType(type : Classifier) : List(T1) Returns a new list containing the non-null elements of self whose type is type but which are not a subtype of type. The returned list element type T1 is the type specified as type. post: result = self ->collect(if oclIsTypeOf(type) then oclAsType(type) else null endif) ->excluding(null) size List(T)::size() : Integer The number of elements in the collection self. post: result = self->iterate(elem; acc : Integer = 0 | acc + 1) subSequence List(T)::subSequence(lower : Integer, upper : Integer) : Sequence(T) Returns a new sub-List of self starting at number lower, up to and including element number upper. pre : 1 <= lower and lower <= upper and upper <= size() post: result->size() = upper -lower + 1 post: Sequence{lower..upper}->forAll(i | result->at(i - lower + 1) = at(i)) sum List(T)::sum() : T The addition of all elements in self. Elements must be of a type supporting the + operation. The + operation must take one parameter of type T. It does not need to be commutative or associative since the iteration order over a list is well-defined. Integer and Real fulfill this condition. post: result = self->iterate(elem; acc : T = 0 | acc + elem) union List(T)::union (s : List(T)) : List(T) Returns a new list consisting of all elements in self, followed by all elements in s. post: result->size() = size() + s->size() post: Sequence{1..size()}->forAll(i | result->at(i) = at(i)) post: Sequence{1..s->size()}->forAll(i | result->at(i + size()) = s->at(i))) <New sub-sub-section number> Iterations on Lists There are no iterations defined for Lists since Lists are mutable and iteration domains are immutable. However the iterations defined for Sequences may be used without explicitly converting the List to a Sequence. Invocation of one the following list iterations returning non-Lists aList->iteration(...) is therefore shorthand for aList->asSequence()->iteration(...) List(T)::any(i : T[?]) : T[?] List(T)::collect(i : T[?]) : Collection(T1) List(T)::collectNested(i : T[?]) : Collection(T1) List(T)::exists(i : T[?]) : Boolean[?] List(T)::exists(i : T[?], j : T[?]) : Boolean[?] List(T)::forAll(i : T[?]) : Boolean[?] List(T)::forAll(i : T[?], j : T[?]) : Boolean[?] List(T)::isUnique(i : T[?]) : Boolean List(T)::iterate(i : T[?]; acc : T2[?]) : T2[?] List(T)::one(i : T[?]) : Boolean Invocation of one the following list iterations returning Lists aList->iteration(...) is shorthand for aList->asSequence()->iteration(...)->asList() List(T)::reject(i : T[?]) : List(T) List(T)::select(i : T[?]) : List(T) List(T)::sortedBy(i : T[?]) : List(T) <New sub-sub-section number> Operations on Collections The following operations are added to the standard OCL collections Collection::asList Collection(T)::asList(T) : List(T) Returns a new list containing all the elements of a collection. Whether the order is determinate depends on the derived collection type.. post: result->size() = size() post: self->asSet()->forAll(e | result->count(e) = self->count(e)) Collection::clone Collection(T)::clone(T) : Collection(T) Collections are immutable so a clone returns self. post: result = self Collection::deepclone Collection(T)::deepclone(T) : Collection(T) Collections are immutable so a deep clone returns self. post: result = self <New sub-sub-section number> Operations on Bags The following operations are added to the standard OCL bags Bag::asList Bag(T)::asList(T) : List(T) Returns a new list containing all the elements of a bag in an indeterminate order. Bag:: clone Bag(T)::clone(T) : Bag(T) Bags are immutable so a clone returns self. Bag:: deepclone Bag(T)::deepclone(T) : Bag(T) Bags are immutable so a deep clone returns self. <New sub-sub-section number> Operations on OrderedSets The following operations are added to the standard OCL ordered sets OrderedSet::asList OrderedSet(T)::asList(T) : List(T) Returns a new list that contains all the elements an ordered set in the same order. post: Sequence{1..size()}->forAll(i | result->at(i) = self->at(i)) OrderedSet:: clone OrderedSet(T)::clone(T) : OrderedSet(T) OrderedSets are immutable so a clone returns self. OrderedSet:: deepclone OrderedSet(T)::deepclone(T) : OrderedSet(T) OrderedSets are immutable so a deep clone returns self. <New sub-sub-section number> Operations on Sequences The following operations are added to the standard OCL sequences Sequence::asList Sequence(T)::asList(T) : List(T) Returns a new list that contains all the elements a sequence in the same order. post: Sequence{1..size()}->forAll(i | result->at(i) = self->at(i)) Sequence:: clone Sequence(T)::clone(T) : Sequence(T) Sequences are immutable so a clone returns self. Sequence:: deepclone Sequence(T)::deepclone(T) : Sequence(T) Sequences are immutable so a deep clone returns self. <New sub-sub-section number> Operations on Sets The following operations are added to the standard OCL sets Set::asList Set(T)::asList() : List(T) Returns a new list containing all the elements of a set in an indeterminate order. Set:: clone Set(T)::clone() : Set(T) Sets are immutable so a clone returns self. Set:: deepclone Set(T)::deepclone() : Set(T) Sets are immutable so a deep clone returns self. Actions taken: December 18, 2013: received issue July 15, 2014: closed issue Discussion: End of Annotations:===== m: webmaster@omg.org Date: 18 Dec 2013 11:25:32 -0500 To: Subject: Issue/Bug Report ******************************************************************************* Name: Ed Willink Employer: Willink Transformation Ltd mailFrom: ed@willink.me.uk Terms_Agreement: I agree Specification: QVT Section: 8.3.8 FormalNumber: 2011-01-01 Version: 1.1 Doc_Year: 2011 Doc_Month: January Doc_Day: 01 Page: 130 Title: Specify List::reject and other iterations Nature: Clarification Severity: Minor CODE: 3TMw8 B1: Report Issue Remote Name: 88-96-239-178.dsl.zen.co.uk Remote User: HTTP User Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0 Time: 11:25 AM Description: The current specification of List has a vague claim that all OCL Collection operations are available. Are iterations available? Are Sequence operations and iterations not available? Are return types adjusted to List? The specification needs to provide a clear model defining all the operations and iterations.