diff --git a/kie-dmn/FEELbuiltinfunctions.adoc b/kie-dmn/FEELbuiltinfunctions.adoc new file mode 100644 index 00000000000..09e4a532ecb --- /dev/null +++ b/kie-dmn/FEELbuiltinfunctions.adoc @@ -0,0 +1,1596 @@ +// If changing name, location, and structure of this document, align with Docs team + += FEEL built-in functions + +To promote interoperability, FEEL includes a library of built-in functions, described below. +The built-in functions are implemented and available to be used in the Drools DMN engine; this document provides and overall description and helpful examples. +The formal specification of these functions can be referenced in the original DMN Specification document from OMG. + +== Conversion functions + +These functions support conversion between values of different types. +Some specific string formats are used, such as: + +* `date string` as specified by XML Schema Part 2 Datatypes, for example `2020-06-01` +* `time string` either +** as specified by XML Schema Part 2 Datatypes, for example `23:59:00z` +** a local time as specified by ISO 8601 followed by `@` and a IANA Timezone, for example `00:01:00@Etc/UTC` +* `date time string` a composite of a `date string` followed by `T` and a `time string`, for example `2012-12-25T11:00:00Z` +* `duration string` as a date time duration or year month duration as specified by the XQuery 1.0 and XPath 2.0 Data Model, for example `P1Y2M` + +=== date(from) + +Convert `from` to a `date`. + +.Parameters +* `from` of type `string` of `date string` format + +.Examples +[source,FEEL] +---- +date("2012-12-25") - date("2012-12-24") = duration("P1D") +---- + +// ---------------------------------------------------------------------------- + +=== date(from) + +Convert `from` to a `date`, setting time components to null. + +.Parameters +* `from` of type `date and time` + +.Examples +[source,FEEL] +---- +date(date and time("2012-12-25T11:00:00Z")) = date("2012-12-25") +---- + +// ---------------------------------------------------------------------------- + +=== date(year, month, day) + +Produce a `date` from year, month and day values. + +.Parameters +* `year` of type `number` +* `month` of type `number` +* `day` of type `number` + +.Examples +[source,FEEL] +---- +date(2012, 12, 25) = date("2012-12-25") +---- + +// ---------------------------------------------------------------------------- + +=== date and time(date, time) + +Produce a `date and time` from the given date ignoring any time components and the given time. + +.Parameters +* `date` of type `date` or `date and time` +* `time` of type `time` + +.Examples +[source,FEEL] +---- +date and time ("2012-12-24T23:59:00") = date and time(date("2012-12-24"), time("23:59:00")) +---- + +// ---------------------------------------------------------------------------- + +=== date and time(from) + +Produce a `date and time` from the given string. + +.Parameters +* `from` of type `string` of `date time string` format + +.Examples +[source,FEEL] +---- +date and time("2012-12-24T23:59:00") + duration("PT1M") = date and time("2012-12-25T00:00:00") +---- + +// ---------------------------------------------------------------------------- + +=== time(from) + +Produce a `time` from the given string. + +.Parameters +* `from` of type `string` of `time string` format + +.Examples +[source,FEEL] +---- +time("23:59:00z") + duration("PT2M") = time("00:01:00@Etc/UTC") +---- + +// ---------------------------------------------------------------------------- + +=== time(from) + +Produce a `time` from the given parameter, ignoring any date components + +.Parameters +* `from` of type `time` or `date and time` + +.Examples +[source,FEEL] +---- +time(date and time("2012-12-25T11:00:00Z")) = time("11:00:00Z") +---- + +// ---------------------------------------------------------------------------- + +=== time(hour, minute, second, offset?) + +Produce a `time` from the given hour, minute and second component values + +.Parameters +* `hour` of type `number` +* `minute` of type `number` +* `second` of type `number` +* `offset` and optional parameter of type `days and time duration` or null + +.Examples +[source,FEEL] +---- +time("23:59:00z") = time(23, 59, 0, duration("PT0H")) +---- + +// ---------------------------------------------------------------------------- + +=== number(from, grouping separator, decimal separator) + +Converts `from` into a `number` using the specified separators. + +.Parameters +* `from` of type `string` representing a valid number +* `grouping separator` one of space (` `) or comma (`,`) or period (`.`) or null +* `decimal separator` as above, but different from the group separator, or also null + +.Examples +[source,FEEL] +---- +number("1 000,0", " ", ",") = number("1,000.0", ",", ".") +---- + +// ---------------------------------------------------------------------------- + +=== string(from) + +Provide a string representation of the supplied parameter. + +.Parameters +* `from` a non-null value + +.Examples +[source,FEEL] +---- +string(1.1) = "1.1" +string(null) = null +---- + +// ---------------------------------------------------------------------------- + +=== duration(from) + +Convert `from` to a `days and time` or `years and months duration`. + +.Parameters +* `from` of type `string` of `duration string` format + +.Examples +[source,FEEL] +---- +date and time("2012-12-24T23:59:00") - date and time("2012-12-22T03:45:00") = duration("P2DT20H14M") +duration("P2Y2M") = duration("P26M") +---- + +// ---------------------------------------------------------------------------- + +=== years and months duration(from, to) + +Calculate the years and months duration between the two supplied parameters + +.Parameters +* `from` of type `date` or `date and time` +* `to` same type as above + +.Examples +[source,FEEL] +---- +years and months duration( date("2011-12-22"), date("2013-08-24") ) = duration("P1Y8M") +---- + +// ---------------------------------------------------------------------------- + +== Boolean functions + +Function for Boolean operations. + +=== not(negand) + +Perform the logical negation of the `negand` operand. + +.Parameters +* `negand` of type `boolean` + +.Examples +[source,FEEL] +---- +not(true) = false +not(null) = null +---- + +// ---------------------------------------------------------------------------- + +== String functions + +Functions for string operations. + +NOTE: in FEEL, Unicode characters are counted using their codepoints. + +=== substring(string, start position, length?) + +Returns the substring from start position for the given length; the first character is at position value `1`. + +.Parameters +* `string` of type `string` +* `start position` of type `number` +* `length` optional parameter of type `number` + +.Examples +[source,FEEL] +---- +substring("foobar",3) = "obar" +substring("foobar",3,3) = "oba" +substring("foobar", -2, 1) = "a" +substring("\U01F40Eab", 2) = "ab" +---- + +NOTE: in FEEL the string literal ``"\U01F40Eab"`` is the `🐎ab` string + +// ---------------------------------------------------------------------------- + +=== string length(string) + +Calculates the length of the string + +.Parameters +* `string` of type `string` + +.Examples +[source,FEEL] +---- +string length("foo") = 3 +string length("\U01F40Eab") = 3 +---- + +// ---------------------------------------------------------------------------- + +=== upper case(string) + +Produce an upper case version of the string + +.Parameters +* `string` of type `string` + +.Examples +[source,FEEL] +---- +upper case("aBc4") = "ABC4" +---- + +// ---------------------------------------------------------------------------- + +=== lower case(string) + +Produce an lower case version of the string + +.Parameters +* `string` of type `string` + +.Examples +[source,FEEL] +---- +lower case("aBc4") = "abc4" +---- + +// ---------------------------------------------------------------------------- + +=== substring before(string, match) + +Calculates the substring before the match + +.Parameters +* `string` of type `string` +* `match` of type `string` + +.Examples +[source,FEEL] +---- +substring before("foobar", "bar") = "foo" +substring before("foobar", "xyz") = "" +---- + +// ---------------------------------------------------------------------------- + +=== substring after(string, match) + +Calculates the substring after the match + +.Parameters +* `string` of type `string` +* `match` of type `string` + +.Examples +[source,FEEL] +---- +substring after("foobar", "ob") = "ar" +substring after("", "a") = "" +---- + +// ---------------------------------------------------------------------------- + +=== replace(input, pattern, replacement, flags?) + +Calculates the regular expression replacement + +.Parameters +* `input` of type `string` +* `pattern` of type `string` +* `replacement` of type `string` +* `flags` optional parameter of type `string` + +NOTE: uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 + +.Examples +[source,FEEL] +---- +replace("abcd", "(ab)|(a)", "[1=$1][2=$2]") = "[1=ab][2=]cd" +---- + +// ---------------------------------------------------------------------------- + +=== contains(string, match) + +Returns true if the string contains the match + +.Parameters +* `string` of type `string` +* `match` of type `string` + +.Examples +[source,FEEL] +---- +contains("foobar", "of") = false +---- + +// ---------------------------------------------------------------------------- + +=== starts with(string, match) + +Returns true if the string starts with the match + +.Parameters +* `string` of type `string` +* `match` of type `string` + +.Examples +[source,FEEL] +---- +starts with("foobar", "fo") = true +---- + +// ---------------------------------------------------------------------------- + +=== ends with(string, match) + +Returns true if the string ends with the match + +.Parameters +* `string` of type `string` +* `match` of type `string` + +.Examples +[source,FEEL] +---- +ends with("foobar", "r") = true +---- + +// ---------------------------------------------------------------------------- + +=== matches(input, pattern, flags?) + +Returns true if the input matches the regular expression + +.Parameters +* `input` of type `string` +* `pattern` of type `string` +* `flags` optional parameter of type `string` + +NOTE: uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 + +.Examples +[source,FEEL] +---- +matches("foobar", "^fo*b") = true +---- + +// ---------------------------------------------------------------------------- + +=== split(string, delimiter) + +Returns a list of the original string, splitted at the delimiter regexp pattern. + +.Parameters +* `string` of type `string` +* `delimiter` of type `string` for a regular expression pattern + +NOTE: uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 + +.Examples +[source,FEEL] +---- +split( "John Doe", "\\s" ) = ["John", "Doe"] +split( "a;b;c;;", ";" ) = ["a","b","c","",""] +---- + +// ---------------------------------------------------------------------------- + +== List functions + +Functions for list operations. + +NOTE: in FEEL, the index of the first elements in a list is `1`, the index of the last element in a list cab also be identified as `-1`. + +=== list contains(list, element) + +Returns true if the list contains the element + +.Parameters +* `list` of type `list` +* `element` of any type, including null + +.Examples +[source,FEEL] +---- +list contains([1,2,3], 2) = true +---- + +// ---------------------------------------------------------------------------- + +=== count(list) + +Counts the element in the list + +.Parameters +* `list` of type `list` + +.Examples +[source,FEEL] +---- +count([1,2,3]) = 3 +count([]) = 0 +count([1,[2,3]]) = 2 +---- + +// ---------------------------------------------------------------------------- + +=== min(list) + +Returns the minimum comparable element in the list + +.Parameters +* `list` of type `list` + +.Alternative signature +---- +min(e1, e2, ..., eN) +---- + +.Examples +[source,FEEL] +---- +min([1,2,3]) = 1 +min(1) = 1 +min([1]) = 1 +---- + +// ---------------------------------------------------------------------------- + +=== max(list) + +Returns the maximum comparable element in the list + +.Parameters +* `list` of type `list` + +.Alternative signature +---- +max(e1, e2, ..., eN) +---- + +.Examples +[source,FEEL] +---- +max(1,2,3) = 3 +max([]) = null +---- + +// ---------------------------------------------------------------------------- + +=== sum(list) + +Returns the sum of the numbers in the list + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +sum(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +sum([1,2,3]) = 6 +sum(1,2,3) = 6 +sum(1) = 1 +---- + +---- +sum([]) = null +---- + +// ---------------------------------------------------------------------------- + +=== mean(list) + +Calculates the average (arithmetic mean) of the element in the list + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +mean(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +mean([1,2,3]) = 2 +mean(1,2,3) = 2 +mean(1) = 1 +mean([]) = null +---- + +// ---------------------------------------------------------------------------- + +=== all(list) + +Returns true if and only if all elements in the list are true + +.Parameters +* `list` of type `list` of `boolean` elements + +.Alternative signature +---- +all(b1, b2, ..., bN) +---- + +.Examples +[source,FEEL] +---- +all([false,null,true]) = false +all(true) = true +all([true]) = true +all([]) = true +all(0) = null +---- + +// ---------------------------------------------------------------------------- + +=== any(list) + +Returns true if any of the elements in the list is true + +.Parameters +* `list` of type `list` of `boolean` elements + +.Alternative signature +---- +any(b1, b2, ..., bN) +---- + +.Examples +[source,FEEL] +---- +any([false,null,true]) = true +any(false) = false +any([]) = false +any(0) = null +---- + +// ---------------------------------------------------------------------------- + +=== sublist(list, start position, length?) + +Returns the sublist from start position, limited to length elements + +.Parameters +* `list` of type `list` +* `start position` of type `number` +* `length` an optional parameter of type `number` + +.Examples +[source,FEEL] +---- +sublist([4,5,6], 1, 2) = [4,5] +---- + +// ---------------------------------------------------------------------------- + +=== append(list, item...) + +Creates a list appended with the item(s) + +.Parameters +* `list` of type `list` +* `item` parameters of any type + +.Examples +[source,FEEL] +---- +append([1], 2, 3) = [1,2,3] +---- + +// ---------------------------------------------------------------------------- + +=== concatenate(list...) + +Creates a list which is the result of the concatenated lists + +.Parameters +* `list` parameters of type `list` + +.Examples +[source,FEEL] +---- +concatenate([1,2],[3]) = [1,2,3] +---- + +// ---------------------------------------------------------------------------- + +=== insert before(list, position, newItem) + +Creates a list with the newItem inserted at the specified position + +.Parameters +* `list` of type `list` +* `position` of type `number` +* `newItem` of any type + +.Examples +[source,FEEL] +---- +insert before([1,3],1,2) = [2,1,3] +---- + +// ---------------------------------------------------------------------------- + +=== remove(list, position) + +Creates a list which the removed element from the specified position + +.Parameters +* `list` of type `list` +* `position` of type `number` + +.Examples +[source,FEEL] +---- +remove([1,2,3], 2) = [1,3] +---- + +// ---------------------------------------------------------------------------- + +=== reverse(list) + +Returns a reversed list + +.Parameters +* `list` of type `list` + +.Examples +[source,FEEL] +---- +reverse([1,2,3]) = [3,2,1] +---- + +// ---------------------------------------------------------------------------- + +=== index of(list, match) + +Returns indexes matching the element + +.Parameters +* `list` of type `list` +* `match` of any type + +.Examples +[source,FEEL] +---- +index of([1,2,3,2],2) = [2,4] +---- + +// ---------------------------------------------------------------------------- + +=== union(list...) + +Returns a list of all the elements from the lists without duplicates + +.Parameters +* `list` parameters of type `list` + +.Examples +[source,FEEL] +---- +union([1,2],[2,3]) = [1,2,3] +---- + +// ---------------------------------------------------------------------------- + +=== distinct values(list) + +Returns a list without duplicates + +.Parameters +* `list` of type `list` + +.Examples +[source,FEEL] +---- +distinct values([1,2,3,2,1]) = [1,2,3] +---- + +// ---------------------------------------------------------------------------- + +=== flatten(list) + +Returns a flattened list + +.Parameters +* `list` of type `list` + +.Examples +[source,FEEL] +---- +flatten([[1,2],[[3]], 4]) = [1,2,3,4] +---- + +// ---------------------------------------------------------------------------- + +=== product(list) + +Returns the product of the numbers in the list + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +product(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +product([2, 3, 4]) = 24 +product(2, 3, 4) = 24 +---- + +// ---------------------------------------------------------------------------- + +=== median( list ) + +Returns the median of the numbers in the list. +After sorting the elements, in the case of an odd number of elements, the result is the middle element; +in the case of even number of elements, the result is the average of the two middle elements. + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +median(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +median( 8, 2, 5, 3, 4 ) = 4 +median( [6, 1, 2, 3] ) = 2.5 +median( [ ] ) = null +---- + +// ---------------------------------------------------------------------------- + +=== stddev( list ) + +Returns the sample standard deviation of the numbers in the list. + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +stddev(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +stddev( 2, 4, 7, 5 ) = 2.081665999466132735282297706979931 +stddev( [ 47 ] ) = null +stddev( 47 ) = null +stddev( [ ] ) = null +---- + +// ---------------------------------------------------------------------------- + +=== mode( list ) + +Returns the mode of the numbers in the list; in case of multiple elements, these are returned in their ascending order. + +.Parameters +* `list` of type `list` of `number` elements + +.Alternative signature +---- +mode(n1, n2, ..., nN) +---- + +.Examples +[source,FEEL] +---- +mode( 6, 3, 9, 6, 6 ) = [ 6 ] +mode( [6, 1, 9, 6, 1] ) = [ 1, 6 ] +mode( [ ] ) = [ ] +---- + +// ---------------------------------------------------------------------------- + +== Numeric functions + +Functions for number operations. + +=== decimal(n, scale) + +Returns number with the given scale + +.Parameters +* `n` of type `number` +* `scale` of any `number` in the range ` [−6111..6176]` + +.Examples +[source,FEEL] +---- +decimal(1/3, 2) = .33 +decimal(1.5, 0) = 2 +decimal(2.5, 0) = 2 +---- + +// ---------------------------------------------------------------------------- + +=== floor(n) + +Returns the greatest integer less or equal to the number + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +floor(1.5) = 1 +floor(-1.5) = -2 +---- + +// ---------------------------------------------------------------------------- + +=== ceiling(n) + +Returns the smallest integer greater or equal to the number + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +ceiling(1.5) = 2 +ceiling(-1.5) = -1 +---- + +// ---------------------------------------------------------------------------- + +=== abs(n) + +Returns the absolute value + +.Parameters +* `n` of type `number`, `days and time duration` or `year and month duration` + +.Examples +[source,FEEL] +---- +abs( 10 ) = 10 +abs( -10 ) = 10 +abs(@"PT5H") = @"PT5H" +abs(@"-PT5H") = @"PT5H" +---- + +// ---------------------------------------------------------------------------- + +=== modulo( dividend, divisor ) + +Returns the remainder of the division of the dividend by divisor. +In the case either dividend or divisor is negative, the result will be of the same sign as the divisor. + +NOTE: this can be equivalently be exprssed as: `modulo(dividend, divisor) = dividend - divisor*floor(dividen d/divisor)`. + +.Parameters +* `dividend` of type `number` +* `divisor` of type `number` + +.Examples +[source,FEEL] +---- +modulo( 12, 5 ) = 2 +modulo(-12,5)= 3 +modulo(12,-5)= -3 +modulo(-12,-5)= -2 +modulo(10.1, 4.5)= 1.1 +modulo(-10.1, 4.5)= 3.4 +modulo(10.1, -4.5)= -3.4 +modulo(-10.1, -4.5)= -1.1 +---- + +// ---------------------------------------------------------------------------- + +=== sqrt( number ) + +Returns the square root of the number + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +sqrt( 16 ) = 4 +---- + +// ---------------------------------------------------------------------------- + +=== log( number ) + +Returns the natural logarithm of the number + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +decimal( log( 10 ), 2 ) = 2.30 +---- + +// ---------------------------------------------------------------------------- + +=== exp( number ) + +Returns the Euler’s number e raised to the power of the number + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +decimal( exp( 5 ), 2 ) = 148.41 +---- + +// ---------------------------------------------------------------------------- + +=== odd( number ) + +Returns true if the number is odd + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +odd( 5 ) = true +odd( 2 ) = false +---- + +// ---------------------------------------------------------------------------- + +=== odd( number ) + +Returns true if the number is even + +.Parameters +* `n` of type `number` + +.Examples +[source,FEEL] +---- +even( 5 ) = false +even ( 2 ) = true +---- + +// ---------------------------------------------------------------------------- + +== Date and time functions + +Functions for date and time specific operations. + +=== is(value1, value2) + +Returns true if both values are the same element in the FEEL semantic domain + +.Parameters +* `value1` of any type +* `value2` of any type + +.Examples +[source,FEEL] +---- +is(date("2012-12-25"), time("23:00:50")) = false +is(date("2012-12-25"), date("2012-12-25")) = true +is(time("23:00:50z"), time("23:00:50")) = false +---- + +// ---------------------------------------------------------------------------- + +== Range functions + +Temporal ordering functions to establish relationships between single scalar values and ranges of such values. +These functions are heavily inspired by their equivalent in the HL7 CQL (Clinical Quality Language) standard version 1.4. + +=== before() + +A before B + +.Signatures +a. before(point1 point2) +b. before(point range) +c. before(range point) +d. before(range1,range2) + +.Evaluates to true if and only if +a. `point1 < point2` +b. `point < range.start or (point = range.start and not(range.start included) )` +c. `range.end < point or (range.end = point and not(range.end included) )` +d. `range1.end < range2.start or (( not(range1.end included) or not(range2.start included)) and range1.end = range2.start)` + +.Examples +[source,FEEL] +---- +before( 1, 10 ) = true +before( 10, 1 ) = false +before( 1, [1..10] ) = false +before( 1, (1..10] ) = true +before( 1, [5..10] ) = true +before( [1..10], 10 ) = false +before( [1..10), 10 ) = true +before( [1..10], 15 ) = true +before( [1..10], [15..20] ) = true +before( [1..10], [10..20] ) = false +before( [1..10), [10..20] ) = true +before( [1..10], (10..20] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== after() + +A after B + +.Signatures +a. after(point1 point2) +b. after(point range) +c. after(range, point) +d. after(range1 range2) + +.Evaluates to true if and only if +a. `point1 > point2` +b. `point > range.end or (point = range.end and not(range.end included) )` +c. `range.start > point or (range.start = point and not(range.start included) )` +d. `range1.start > range2.end or (( not(range1.start included) or not(range2.end included) ) and range1.start = range2.end)` + +.Examples +[source,FEEL] +---- +after( 10, 5 ) = true +after( 5, 10 ) = false +after( 12, [1..10] ) = true +after( 10, [1..10) ) = true +after( 10, [1..10] ) = false +after( [11..20], 12 ) = false +after( [11..20], 10 ) = true +after( (11..20], 11 ) = true +after( [11..20], 11 ) = false +after( [11..20], [1..10] ) = true +after( [1..10], [11..20] ) = false +after( [11..20], [1..11) ) = true +after( (11..20], [1..11] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== meets() + +A meets B + +.Signatures +a. meets(range1, range2) + +.Evaluates to true if and only if +a. `range1.end included and range2.start included and range1.end = range2.start` + +.Examples +[source,FEEL] +---- +meets( [1..5], [5..10] ) = true +meets( [1..5), [5..10] ) = false +meets( [1..5], (5..10] ) = false +meets( [1..5], [6..10] ) = false +---- + +// ---------------------------------------------------------------------------- + +=== met by() + +A met by B + +.Signatures +a. met by(range1, range2) + +.Evaluates to true if and only if +a. `range1.start included and range2.end included and range1.start = range2.end` + +.Examples +[source,FEEL] +---- +met by( [5..10], [1..5] ) = true +met by( [5..10], [1..5) ) = false +met by( (5..10], [1..5] ) = false +met by( [6..10], [1..5] ) = false +---- + +// ---------------------------------------------------------------------------- + +=== overlaps() + +A overlaps B + +.Signatures +a. overlaps(range1, range2) + +.Evaluates to true if and only if +a. `(range1.end > range2.start or (range1.end = range2.start and (range1.end included or range2.end included))) and (range1.start < range2.end or (range1.start = range2.end and range1.start included and range2.end included))` + +.Examples +[source,FEEL] +---- +overlaps( [1..5], [3..8] ) = true +overlaps( [3..8], [1..5] ) = true +overlaps( [1..8], [3..5] ) = true +overlaps( [3..5], [1..8] ) = true +overlaps( [1..5], [6..8] ) = false +overlaps( [6..8], [1..5] ) = false +overlaps( [1..5], [5..8] ) = true +overlaps( [1..5], (5..8] ) = false +overlaps( [1..5), [5..8] ) = false +overlaps( [1..5), (5..8] ) = false +overlaps( [5..8], [1..5] ) = true +overlaps( (5..8], [1..5] ) = false +overlaps( [5..8], [1..5) ) = false +overlaps( (5..8], [1..5) ) = false +---- + +// ---------------------------------------------------------------------------- + +=== overlaps before() + +A overlaps before B + +.Signatures +a. overlaps before(range1 range2) + +.Evaluates to true if and only if +a. `(range1.start < range2.start or (range1.start = range2.start and range1.start included and range2.start included)) and (range1.end > range2.start or (range1.end = range2.start and range1.end included and range2.start included)) and (range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included )))` + +.Examples +[source,FEEL] +---- +overlaps before( [1..5], [3..8] ) = true +overlaps before( [1..5], [6..8] ) = false +overlaps before( [1..5], [5..8] ) = true +overlaps before( [1..5], (5..8] ) = false +overlaps before( [1..5), [5..8] ) = false +overlaps before( [1..5), (1..5] ) = true +overlaps before( [1..5], (1..5] ) = true +overlaps before( [1..5), [1..5] ) = false +overlaps before( [1..5], [1..5] ) = false +---- + +// ---------------------------------------------------------------------------- + +=== overlaps after() + +A overlaps after B + +.Signatures +a. overlaps after(range1 range2) + +.Evaluates to true if and only if +a. `(range2.start < range1.start or (range2.start = range1.start and range2.start included and not( range1.start included))) and (range2.end > range1.start or (range2.end = range1.start and range2.end included and range1.start included )) and (range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)))` + +.Examples +[source,FEEL] +---- +overlaps after( [3..8], [1..5] )= true +overlaps after( [6..8], [1..5] )= false +overlaps after( [5..8], [1..5] )= true +overlaps after( (5..8], [1..5] )= false +overlaps after( [5..8], [1..5) )= false +overlaps after( (1..5], [1..5) )= true +overlaps after( (1..5], [1..5] )= true +overlaps after( [1..5], [1..5) )= false +overlaps after( [1..5], [1..5] )= false +overlaps after( (1..5), [1..5] )= false +overlaps after( (1..5], [1..6] )= false +overlaps after( (1..5], (1..5] )= false +overlaps after( (1..5], [2..5] )= false +---- + +// ---------------------------------------------------------------------------- + +=== finishes() + +A finishes B + +.Signatures +a. finishes(point, range) +b. finishes(range1, range2) + +.Evaluates to true if and only if +a. `range.end included and range.end = point` +b. `range1.end included = range2.end included and range1.end = range2.end and (range1.start > range2.start or (range1.start = range2.start and (not(range1.start included) or range2.start included)))` + +.Examples +[source,FEEL] +---- +finishes( 10, [1..10] ) = true +finishes( 10, [1..10) ) = false +finishes( [5..10], [1..10] ) = true +finishes( [5..10), [1..10] ) = false +finishes( [5..10), [1..10) ) = true +finishes( [1..10], [1..10] ) = true +finishes( (1..10], [1..10] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== finished by() + +A finished by B + +.Signatures +a. finished by(range, point) +b. finished by(range1 range2) + +.Evaluates to true if and only if +a. `range.end included and range.end = point` +b. `range1.end included = range2.end included and range1.end = range2.end and (range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included))))` + +.Examples +[source,FEEL] +---- +finished by( [1..10], 10 ) = true +finished by( [1..10), 10 ) = false +finished by( [1..10], [5..10] ) = true +finished by( [1..10], [5..10) ) = false +finished by( [1..10), [5..10) ) = true +finished by( [1..10], [1..10] ) = true +finished by( [1..10], (1..10] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== includes() + +A includes B + +.Signatures +a. includes(range, point) +b. includes(range1, range2) + +.Evaluates to true if and only if +a. `(range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)` +b. `(range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included)))) and (range1.end > range2.end or (range1.end = range2.end and (range1.end included or not(range2.end included))))` + +.Examples +[source,FEEL] +---- +includes( [1..10], 5 ) = true +includes( [1..10], 12 ) = false +includes( [1..10], 1 ) = true +includes( [1..10], 10 ) = true +includes( (1..10], 1 ) = false +includes( [1..10), 10 ) = false +includes( [1..10], [4..6] ) = true +includes( [1..10], [1..5] ) = true +includes( (1..10], (1..5] ) = true +includes( [1..10], (1..10) ) = true +includes( [1..10), [5..10) ) = true +includes( [1..10], [1..10) ) = true +includes( [1..10], (1..10] ) = true +includes( [1..10], [1..10] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== during() + +A during B + +.Signatures +a. during(point, range) +b. during(range1 range2) + +.Evaluates to true if and only if +a. `(range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)` +b. `(range2.start < range1.start or (range2.start = range1.start and (range2.start included or not(range1.start included)))) and (range2.end > range1.end or (range2.end = range1.end and (range2.end included or not(range1.end included))))` + +.Examples +[source,FEEL] +---- +during( 5, [1..10] ) = true +during( 12, [1..10] ) = false +during( 1, [1..10] ) = true +during( 10, [1..10] ) = true +during( 1, (1..10] ) = false +during( 10, [1..10) ) = false +during( [4..6], [1..10] ) = true +during( [1..5], [1..10] ) = true +during( (1..5], (1..10] ) = true +during( (1..10), [1..10] ) = true +during( [5..10), [1..10) ) = true +during( [1..10), [1..10] ) = true +during( (1..10], [1..10] ) = true +during( [1..10], [1..10] ) = true +---- + +// ---------------------------------------------------------------------------- + +=== starts() + +A starts B + +.Signatures +a. starts(point, range) +b. starts(range1, range2) + +.Evaluates to true if and only if +a. `range.start = point and range.start included` +b. `range1.start = range2.start and range1.start included = range2.start included and (range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included)))` + +.Examples +[source,FEEL] +---- +starts( 1, [1..10] ) = true +starts( 1, (1..10] ) = false +starts( 2, [1..10] ) = false +starts( [1..5], [1..10] ) = true +starts( (1..5], (1..10] ) = true +starts( (1..5], [1..10] ) = false +starts( [1..5], (1..10] ) = false +starts( [1..10], [1..10] ) = true +starts( [1..10), [1..10] ) = true +starts( (1..10), (1..10) ) = true +---- + +// ---------------------------------------------------------------------------- + +=== started by() + +A started by B + +.Signatures +a. started by(range, point) +b. started by(range1, range2) + +.Evaluates to true if and only if +a. `range.start = point and range.start included` +b. `range1.start = range2.start and range1.start included = range2.start included and (range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)))` + +.Examples +[source,FEEL] +---- +started by( [1..10], 1 ) = true +started by( (1..10], 1 ) = false +started by( [1..10], 2 ) = false +started by( [1..10], [1..5] ) = true +started by( (1..10], (1..5] ) = true +started by( [1..10], (1..5] ) = false +started by( (1..10], [1..5] ) = false +started by( [1..10], [1..10] ) = true +started by( [1..10], [1..10) ) = true +started by( (1..10), (1..10) ) = true +---- + +// ---------------------------------------------------------------------------- + +=== coincides() + +A coincides B + +.Signatures +a. coincides(point1, point2) +b. coincides(range1, range2) + +.Evaluates to true if and only if +a. `point1 = point2` +b. `range1.start = range2.start and range1.start included = range2.start included and range1.end = range2.end and range1.end included = range2.end included` + +.Examples +[source,FEEL] +---- +coincides( 5, 5 ) = true +coincides( 3, 4 ) = false +coincides( [1..5], [1..5] ) = true +coincides( (1..5), [1..5] ) = false +coincides( [1..5], [2..6] ) = false +---- + +// ---------------------------------------------------------------------------- + +== Temporal functions + +Functions for general temporal operations. + +=== day of year( date ) + +Returns the Gregorian number of the day of the year + +.Parameters +* `date` of type `date` or `date and time` + +.Examples +[source,FEEL] +---- +day of year( date(2019, 9, 17) ) = 260 +---- + +// ---------------------------------------------------------------------------- + +=== day of year( date ) + +Returns the Gregorian day of the week, either of “Monday”, “Tuesday”, “Wednesday”, “Thursday”, “Friday”, “Saturday”, “Sunday” + +.Parameters +* `date` of type `date` or `date and time` + +.Examples +[source,FEEL] +---- +day of week( date(2019, 9, 17) ) = "Tuesday" +---- + +// ---------------------------------------------------------------------------- + +=== month of year( date ) + +Returns the Gregorian month, either of “January”, “February”, “March”, “April”, “May”, “June”, “July”, “August”, “September”, “October”, “November”, “December” + +.Parameters +* `date` of type `date` or `date and time` + +.Examples +[source,FEEL] +---- +month of year( date(2019, 9, 17) ) = "September" +---- + +// ---------------------------------------------------------------------------- + +=== month of year( date ) + +Returns the Gregorian week of the year accordingly to ISO 8601 + +.Parameters +* `date` of type `date` or `date and time` + +.Examples +[source,FEEL] +---- +week of year( date(2019, 9, 17) ) = 38 +week of year( date(2003, 12, 29) ) = 1 +week of year( date(2004, 1, 4) ) = 1 +week of year( date(2005, 1, 1) ) = 53 +week of year( date(2005, 1, 3) ) = 1 +week of year( date(2005, 1, 9) ) = 1 +---- + +// ---------------------------------------------------------------------------- + +== Sort function + +Function for sorting operations. + +=== sort(list, precedes) + +Returns a list of the same elements but ordered accordingly to the sorting function + +.Parameters +* `list` of type `list` +* `precedes` of type `function` + +.Examples +[source,FEEL] +---- +sort(list: [3,1,4,5,2], precedes: function(x,y) x < y) = [1,2,3,4,5] +---- + +// ---------------------------------------------------------------------------- + +== Context functions + +Function for context operations. + +=== get value(m, key) + +Returns the value from the context, for the specified entry key + +.Parameters +* `m` of type `context` +* `key` of type `string` + +.Examples +[source,FEEL] +---- +get value({key1 : "value1"}, "key1") = "value1" +get value({key1 : "value1"}, "unexistent-key") = null +---- + +// ---------------------------------------------------------------------------- + +=== get entries(m) + +Computes a list of key and value pair for the given context + +.Parameters +* `m` of type `context` + +.Examples +[source,FEEL] +---- +get entries({key1 : "value1", key2 : "value2"}) = [ { key : "key1", value : "value1" }, {key : "key2", value : "value2"} ] +---- + +// ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/pom.xml b/kie-dmn/kie-dmn-feel/pom.xml index e2a0efc57a2..d68ad8777b0 100644 --- a/kie-dmn/kie-dmn-feel/pom.xml +++ b/kie-dmn/kie-dmn-feel/pom.xml @@ -111,10 +111,27 @@ yasson test + + org.asciidoctor + asciidoctorj + test + + + + ${project.basedir}/src/test/resources + + + ${project.basedir}/../ + + FEELbuiltinfunctions.adoc + + false + + org.apache.felix diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/documentation/ADocFEELExamplesTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/documentation/ADocFEELExamplesTest.java new file mode 100644 index 00000000000..3406db087ee --- /dev/null +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/documentation/ADocFEELExamplesTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.kie.dmn.feel.documentation; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.OptionsBuilder; +import org.asciidoctor.ast.Block; +import org.asciidoctor.ast.ContentNode; +import org.asciidoctor.ast.Document; +import org.asciidoctor.ast.Section; +import org.asciidoctor.ast.StructuralNode; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; +import org.kie.dmn.feel.FEEL; +import org.kie.dmn.feel.lang.FEELProfile; +import org.kie.dmn.feel.parser.feel11.profiles.KieExtendedFEELProfile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ADocFEELExamplesTest { + + private static final Logger LOG = LoggerFactory.getLogger(ADocFEELExamplesTest.class); + private static final List profiles = new ArrayList<>(); + { + profiles.add(new KieExtendedFEELProfile()); + } + private final FEEL feel = FEEL.newInstance(profiles); + + /** + * Dev notes: the availability of the .adoc resource to this test and its refresh is governed by Maven. + * You might want to execute this test from CLI if the IDE is not able handle the maven build refresh properly, for example as: + * $ mvn test -Dtest=org.kie.dmn.feel.documentation.ADocFEELExamplesTest + */ + @Test + public void test() throws URISyntaxException { + Asciidoctor asciidoctor = Asciidoctor.Factory.create(); + URL resource = this.getClass().getResource("/FEELbuiltinfunctions.adoc"); + URI uri = resource.toURI(); + LOG.debug("{}", uri); + File src = new File(uri); + Document loadFile = asciidoctor.loadFile(src, OptionsBuilder.options().asMap()); + processBlock(loadFile); + } + + private void processBlock(StructuralNode block) { + List blocks = block.getBlocks(); + + for (int i = 0; i < blocks.size(); i++) { + final StructuralNode currentBlock = blocks.get(i); + if (currentBlock instanceof StructuralNode) { + if ("listing".equals(currentBlock.getContext())) { + Block b = (Block) currentBlock; + List lines = b.getLines(); + LOG.trace("{}", lines); + LOG.trace("{}", b.getAttributes()); + ContentNode parent = b.getParent(); + String sectionTitle = ""; + if (parent instanceof Section) { + Section section = (Section) parent; + sectionTitle = section.getTitle(); + } + if (b.getAttribute("language", "unknown").equals("FEEL")) { + for (String l : lines) { + String titled = sectionTitle + ": " + l; + LOG.info("checking DOC {}", titled); + Object FEELResult = feel.evaluate(l); + Assert.assertThat(titled, FEELResult, Matchers.is(true)); + } + } else { + LOG.trace("This block is not FEEL true predicate snippets: {}", b); + } + } else { + processBlock(currentBlock); + } + } + } + } + + +}