Issue 13077: The following collection operations would be useful for the HL7 GELLO project: (ocl2-rtf) Source: (, ) Nature: Enhancement Severity: Significant Summary: The HL7 GELLO project would find it useful to have additional collection operators in OCL. A method to define these in the underlying model would be good. Alternatively, we request the addition of the following operations: max, min: To determine the maximum or minimum value in a collection firstN: Returns a sequence with the first n elements of this sequence lastN: Returns a sequence with the last n elements of this sequence reverse: Returns a sequence in reverse order join(namesOfCollections; namesOfProperties; booleanExpression; orderByExpression) Where: - namesOfCollections is a list of strings separated by commas, where each string represents the name of a collection from where data is retrieved. - namesOfProperties is a list of strings separated by commas, where each string is the full description of the properties from the objects in the collections we want to get in the result. - booleanExpression is a valid boolean expression containing the conditions the elements from the collections defined in listOfCollections must satisfy in order to be included in the result - booleanExpression is a valid boolean expression containing the conditions the elements from the collections defined in listOfCollections must satisfy in order to be included in the result average: Calculate the average value in a collection stdev: Calculate the standard deviation of a collection variance: Calculate the variance of a collection median: Calculate the median of a collection mode: Calculate the mode of a collection Resolution: Revised Text: (1) At the end of section 11.1 Introduction add the following sentence: The Standard Library may be extended with new types, new operations and new iterators. In particular new operations can be defined for collections. (2) In section 11.7.1 Collection, after "sum" operation definition add the following two definitions: 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 and be both associative and commutative. Integer and Real fulfill this condition. post: result = self->iterate( elem; acc : T = self.first() | acc.max(elem) ) 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 and be both associative and commutative. Integer and Real fulfill this condition. post: result = self->iterate( elem; acc : T = self.first() | acc.min(elem) ) (3) In section 11.7.3 OrderedSet, append the following operation definition: reverse () : OrderedSet(T) The set of elements with same elements but with the opposite order. post: result->size() = self->size() (4) In section 11.7.5 Sequence, append the following operation definition: reverse () : Sequence(T) The sequence containing the same elements but with the opposite order. post: result->size() = self->size() Actions taken: November 10, 2008: received issue October 16, 2009: closed issue Discussion: In the OCL specification we found some statements that indicate that the standard library can be extended (example 11.8.1), not only with new operations but also with iterator expressions. However the mechanism for representing the standard library itself (as a M1 instance of the OCL metamodel) is unclear (a Package containing types which in turn contain operations?) and the extension mechanism for the standard library is also undefined (a pure replacement of the Package, or a new Package importing the StdLib package?). Some other issues address specifically this problem, so the resolution of this issue will be formulated in a neutral way is respect to this representation issue: we confirm that new collection operations can be defined (adding a sentence) and we treat specifically the case of some generic operations that can be easily introduced: "reverse" as operation of "Sequence" and "OrderedSet", max, min as operations of Collection but with constraints on the type of parameters. We consider that average, stdev, variance, median and mode are too specific and can be defined as extensions. The proposed join functionality can be re-formulated as an iterator extending the standard library. End of Annotations:===== iler: QUALCOMM Windows Eudora Version 7.1.0.9 Date: Wed, 12 Nov 2008 13:59:23 -0500 To: issues@omg.org, ocl2-rtf@omg.org From: Juergen Boldt Subject: issue 13077 -- OCL 2 RTF issue From: webmaster@omg.org Date: 10 Nov 2008 04:54:12 -0500 To: Subject: Issue/Bug Report -------------------------------------------------------------------------------- Name: Craig Lucas Company: InferMed Ltd mailFrom: craig.lucas@infermed.com Notification: Yes Specification: Object Constraint Language (OCL) Section: 7.6 FormalNumber: formal/06-05-01 Version: 2.0 RevisionDate: 06/05/2001 Page: 25--28 Nature: Enhancement Severity: Significant HTTP User Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 1.1.4322) Description The following collection operations would be useful for the HL7 GELLO project: context Sequence def: firstN(n : Integer) : Sequence = self->subSequence(1, n) def: lastN(n : Integer) : Sequence = self->subSequence( count() - (n+1), count() ) /* ->JOIN currentCollection ? join(namesOfCollections; namesOfProperties; booleanExpression; orderByExpression) Where: â.¢ namesOfCollections is a list of strings separated by commas, where each string represents the name of a collection from where data is retrieved. â.¢ The name of the current collection currentCollection must appear in the list. Notation for namesOfCollections: â.¢ Collection1, collection2,â.¦collectionn; E.g. patient, labTest, â.¦, treatment â.¢ Alias1 in collection1, alias2 in collection2, â.¦, aliasn in collectionn; E.g. p in patient, lt in labTest, â.¦, t in treatment â.¢ namesOfProperties is a list of strings separated by commas, where each string is the full description of the properties from the objects in the collections we want to get in the result. The notation is: object.property. E.g. [patient.ID, labTest.ID, labTest.result, labTest.date, treatment.ID, treatment.description]. Or using aliases: p.ID, lt.ID, etc. â.¢ booleanExpression is a valid GELLO Boolean expression containing the conditions the elements from the collections defined in listOfCollections must satisfy in order to be included in the result. For each pair of collections there must be at least one condition related to these collections in the booleanExpression. In general, the number of conditions must be at least equal to the number of collections in listOfCollections-1. â.¢ orderByExpression is a valid GELLO expression specifying the properties by which the result should be ordered. E.g. patient.ID, treatmentID or using aliases p.ID, t.ID, will sort the result by patientID and treatment ID. Types for "join" (collection x parameterList x parameterList x booleanExpression x OrderExpression) ? bag_of_tuples*: for any type of collection if OrderExpression is not specified. (collection x parameterList x parameterList x booleanExpression x OrderExpression) ? sequence_of_tuples+: for any type of collection if OrderExpression is specified. Definition of evaluation function for Fjoin(V,S1,S2,E1,E2) Fjoin(V,S1,S2,E1,E2) = bag of tuples If V is collection, S1 is a parameter list of strings with the names of the collections from where data will be retrieved, S2 is a parameter list of strings with the full names of the properties to be included in the result, E1 is a boolean expression containing the conditions C the returning elements must satisfy. The number of conditions Ci in E1 = [S1(size()-1]. E2 is an optional parameter that specifies the criteria for ordering the resulting elements. If E2 is not specified,the result is a bag. Fjoin(V,S1,S2,E1,E2) = sequence of tuples If V is collection, S1 is a parameter list of strings with the names of the collections from where data will be retrieved, S2 is a parameter list of strings with the full names of the properties to be included in the result, E1 is a boolean expression containing the conditions Ci < C1 booleanOP C2 â.¦ booleanOP Cn > the returning elements must satisfy. The number of conditions Ci in E1 = [S1(size()-1]. E2 is an optional parameter that specifies the criteria for ordering the resulting elements. If ordering is required, then a GELLO expression specifying the properties by which the result should be ordered by must be defined. The resulting collection is a sequence. = undefined otherwise ->MAX Max returns the biggest and smallest value respectively in a collection. The collection must contain numbers. ->MIN Min returns the smallest value respectively in a collection. The collection must contain numbers. ->REVERSE Reverse returns a sequence in reversed order. E.g. the first element of the current sequence is returned as the last and so on ->ELEMAT Returns the element at the Nth position from the current sequence ->SORTBY collection -> sortBy(orderByExpression) Types for "sortBy" (collection x ListOfProperties) ?sequence */ context Collection def: average( c : Collection ) : Real = let t : Real = self->sum(), n : Integer = self->count() in t/n /* ->AVERAGE Average returns the average (arithmetic mean) of the numerical elements in a collection. Type for "average" (collection) ? real ->STDEV Stdev returns the standard deviation of the numerical elements in a collection. Type is (collection) : Real ->VARIANCE Variance returns the variance of the numerical elements in a collection ->MEDIAN Median returns the median of the numerical elements in a collection. The median is the number in the middle of a set of numbers; that is, half the numbers have values that are greater than the median, and half have values that are less. If the number of elements is even, then the median is the average value of the two numbers in the middle. ->MODE Mode returns the most frequently occurring value in a collection. ->LIKE Like operator searches a collection of strings and returns those that match a given pattern. The following example returns a collection with problems that are like "ast" (asthma, astigmatism...): patient.problemList ? collect(code) ? like("ast") ->NOTLIKE NotLike operator searches a collection of strings and returns those that do not match a given pattern. ->BETWEEN Between operator searches a collection of strings and returns those strings that are between a given range. {asthma, copd, diabetes, IRS, meningitis, reflux, UTI} ? between(diabetes, reflux) returns: {diabetes, IRS, meningitis, reflux} ->DISTINCT Distinct operator returns a collection (set) with no duplicate elements. Basically is a casting operation, that converts a bag or sequence into a set, hence eliminating duplicates. {asthma, copd, diabetes, copd, UTI, IRS, reflux, UTI} -> distinct() returns: {asthma, copd, diabetes, UTI, IRS, reflux} Other missing things at a quick glance: ToDate - returns point in time (Not an OCL concept) The operator ToDate takes a string and returns a PointInTime object Operator AddMonths, AddDate, NextDay These operations are supported by the RIM PoinInTime Class using the operator plus Lastday This operation is fully supported by the RIM (time) Interval Class using the operator high Factory All the interval operators Temporal Relations and Temporal Intervals May 12, 1987 from 8 to 9:30 PM is "[198705122000;198705122130]". */ context Set def: before( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.high < i2.low /* before() The notation is: before(interval1,interval2). Definition of evaluation function for Fbefore(IVL1,IVL2) Fbefore(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the end-point of interval1 occurs strictly earlier than the start-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and the end-point of interval1 does not occur strictly earlier than the start-point of interval2. = undefined otherwise */ def: after( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low > i2.high /* after() Fafter(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 occurs (starts) after the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and the start-point of interval1 does not occur after the end-point of interval2. = undefined otherwise */ def: meets( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.high = i2.low /* meets() Fmeets(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the end-point of interval1 is simultaneous with the start-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and end-point of interval1 is not simultaneous with the start-point of interval2. = undefined otherwise */ def: metBy( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low = i2.high /* met-by() Fmet-by(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 is simultaneous with the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and start-point of interval1 is not simultaneous with the end-point of interval2. = undefined otherwise */ def: overlaps( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low < i2.low and i1.high > i2.low and i1.high < i2.high /* overlaps() Foverlaps(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 is earlier than the start-point of interval2, but the end-point of interval1 occurs strictly between the start- and end-points of interval2. = false Else if IVL1 and IVL2 are both time intervals and start-point of interval1 is not earlier than the start-point of interval2, or the end-point of interval1 does not ocurr strictly between the start- and end-points of interval2. = undefined otherwise */ def: overlappedBy( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low > i2.low and i1.low < i2.high and i1.high > i2.high /* overlapped-by() Foverlapped-by(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 occurs between the start- and end-points of interval2, but the end-point of interval1 occurs later than the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and the start-point of interval1 does not occur between the start- and end-points of interval2, or the end-point of interval1 occurs before the end-point of interval2. = undefined otherwise */ def: starts( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low = i2.low and i1.high < i2.high /* starts() Fstarts(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 occurs simultaneously with the start-point of interval2, but the end-point of interval1 occurs before the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and the start-point of interval1 does not occur simultaneously with the start-point of interval2, or the end-point of interval1 occurs after the end-point of interval2. = undefined otherwise */ def: startedBy( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low = i2.low and i1.high > i2.high /* started-by() Fstarted-by(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point if interval1 is simultaneous with the start-point of interval2, but the end-point of interval1 occurs later than the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals and the start-point if interval1 is not simultaneous with the start-point of interval2, or the end-point of interval1 occurs before the end-point of interval2. = undefined otherwise */ def: during( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low > i2.low and i1.high < i2.high /* during() Fduring(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 occurs after the start-point of interval2 and the end-point of interval1 occurs earlier than the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals but the start-point of interval1 occurs before the start-point of interval2 or the end-point of interval1 occurs later than the end-point of interval2. = undefined otherwise */ def: contains( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low < i2.low and i1.high > i2.high /* contains() Fcontains(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start-point of interval1 is earlier than the start-point of interval2, and the end of interval1 occurs later than the end-point of interval2. = false Else if IVL1 and IVL2 are both time intervals but the start-point of interval1 is later than the start-point of interval2, or the end of interval1 occurs earlier than the end-point of interval2. = undefined otherwise */ def: finishes( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low = i2.low and i1.high > i2.high /* finishes() Ffinishes(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the end-point of interval1 is simultaneous with the end-point of interval2, but the start-point of interval1 is later than the start-point of interval2. = false Else if IVL1 and IVL2 are both time intervals but the end-point of interval1is not simultaneous with the end-point of interval2, or the start-point of interval1 is earlier than the start-point of interval2. = undefined otherwise */ def: finishedBy( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low < i2.low and i1.high = i2.high /* finished-by() Ffinished-by(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the end-point of interval1 is simultaneous with the end-point of interval2, but the start-point of interval1 is earlier than the start-point of interval2. = false Else if IVL1 and IVL2 are both time intervals but the end-point of interval1 is not simultaneous with the end-point of interval2, or the start-point of interval1 is later than the start-point of interval2. = undefined otherwise */ def: equals( i1 : IntervalPointInTime, i2 : IntervalPointInTime ) : Boolean = i1.low = i2.low and i1.high = i2.high /* equals() Fequals(IVL,IVL) = true If IVL1 and IVL2 are both time intervals and the start- and end-points of both interval1 and interval2 are respectively simultaneous. = false Else if IVL1 and IVL2 are both time intervals but the start- and end-points of both interval1 and interval2 respectively are not simultaneous. = undefined otherwise */ Juergen Boldt Director, Member Services Object Management Group 140 Kendrick St Building A Suite 300 Needham, MA 02494 USA tel: +1 781 444 0404 x 132 fax: +1 781 444 0320 email: juergen@omg.org www.omg.org