Functions

Mathematical Functions

You can use the operators MIN(param[,param,param...]), MAX(param[,param,param...]), ABS(param), and AVR(param[,param,param...]) if you need to determine the minimum, maximum, absolute or average value in a set of parameters.

For more advanced calculations, the following functions are also available:

  • EXP(<Computation> value) to calculate the exponential of a value

  • LN(<Computation> value) to calculate the natural logarithm of a value

  • LOG(<Computation> value, <Computation> base) to calculate the logarithm of a value

  • POW(<Computation> value, <Computation> power) to calculate a power

  • SQRT(<Computation> value) to calculate a square root

  • ROUND(<Computation> value) to round a number to the nearest integer

  • FLOOR(<Computation> value) to round down a number to the nearest integer

  • CEIL(<Computation> value) to round up a number to the nearest integer

  • CENTROID(<Computation> value [| <computation> weight], ...) to calculate the centroid of comma-separated pairs of value|weight. If no weight is specified, it is set to 1.

  • FCENTROID(<Computation> min, <Computation> max, <Computation> value [| <computation> weight], ...) to calculate the filtered centroid of comma-separated pairs of value|weight. When using the FCENTROID() function, only the values within min and max are used to calculate a CENTROID(). To specify infinity as a bound, leave the value of min or max empty. If no values match the filter, the default value is returned.

  • FMIN(<Computation> min, <Computation> max, <Computation> value [, <Computation> value, <Computation> value...]) to calculate the filtered minimum of comma-separated values. When using the FMIN() function, only the values within min and max are used to calculate a MIN(). To specify infinity as a bound, leave the value of min or max empty. If no values match the filter, the default value is returned.

  • FMAX(<Computation> min, <Computation> max, <Computation> value [, <Computation> value, <Computation> value...]) to calculate the filtered maximum of comma-separated values. When using the FMAX() function, only the values within min and max are used to calculate a MAX(). To specify infinity as a bound, leave the value of min or max empty. If no values match the filter, the default value is returned.

  • FSUM(<Computation> min, <Computation> max, <Computation> value [, <Computation> value, <Computation> value...]) to calculate the filtered sum of comma-separated values. When using the FSUM() function, only the values within min and max are used to calculate a SUM(). To specify infinity as a bound, leave the value of min or max empty. If no values match the filter, the default value is returned.

Using a measure if it is above a threshold, else use the threshold:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="MAX(10,VG)" />
</Measure>

Using the higher of two measures:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="MAX(LC,SLOC)" />
</Measure>

Using lower of three indicators:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="MIN(I.TESTABILITY, I.CHANGEABILITY, I.ANALISABILITY)" />
</Measure>

Example preventing division by 0:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="LC / MAX(STAT, 1)" />
</Measure>

Example retrieving the variation of a measure:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="ABS(DELTA_VALUE(LC))" />
</Measure>

Example using nested MIN and MAX functions:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="MIN(MAX(SLOC+(BLANK/2),1000),MAX(LC,1000))+2" />
</Measure>

Calculating the average of three indicators:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="AVR(I.TESTABILITY, I.CHANGEABILITY, I.ANALISABILITY)" />
</Measure>

Calculating the centroid of 3 with weight 3 and 2 with weight 100 (=2.03):

Note: this translates to (3x3 + 2x100) / (100+3)

<Measure measureId="MATH_CENTROID_3_3_2_100" defaultValue="0">
<Computation targetArtefactTypes="APPLICATION" result="CENTROID(3|3,2|100)"/>
</Measure>

Calculating the filtered centroid of TESTABILITY/STABILITY/MAINTAINABILITY:

Given the scale:

  • level: UNKNOWN, rank: -1

  • level: LEVELA, rank: 0

  • level: LEVELB, rank: 1

  • level: LEVELC, rank: 2

and given that I.TESTABILITY is UNKNOWN, I.STABILITY is LEVELB, I.MAINTAINABILITY is LEVELC

<Measure measureId="MATH_FCENTROID" defaultValue="0">
<Computation targetArtefactTypes="APPLICATION" result="FCENTROID(0,,I.TESTABILITY|3,I.STABILITY|2,I.MAINTAINABILITY)"/>
</Measure>

I.TESTABILITY is filtered out as it is not between the specified minimum and maximum.

The value is then computed as CENTROID(I.STABILITY|2,I.MAINTAINABILITY), which is (1x2 + 2) / (2+1).

Understanding filtered min, max and sum:

FMIN(,,-2,4,11) 
is equivalent to: FMIN(-Infinity,+Infinity,-2,4,11)
is equivalent to: MIN(-2,4,11)
FMIN(0,10,-2,4,11)
is equivalent to: MIN(4,11)
FMIN(0,1,-2,4,11)
is equivalent to: MIN(), which evaluates to null 
and leads to using the default value of the measure and marking 
it in the indicator tree with the status ERROR.
FMIN(2,,1,I.LC)
is equivalent to: FMIN(2,+Infinity,1,I.lC)
is equivalent to: MIN(I.LC)
resolves to: I.LC if LC >= 2, else default value
FSUM(,,1,2.5,2>1,3)
is evaluated as: 1 + 2.5 + 1 + 3
FSUM(2,4,1,2.5,2>1,3)
is evaluated as: 2.5 + 3
FSUM(6,,-1,I.LC,LC)
resolves to: I.LC if >= 6 or LC if >= 6

Conditional and Level-Related Functions

More advanced decisions can be made when using the following conditional and level-related functions:

  • You can use the IF(cond,val_yes,val_no) function to assign different values based on the result of a condition. Note that nested IF constructions are allowed, and an IF block can contain OR or AND operators (since 13-B).

    Tip

    A condition is simply a computation that returns 1 if true and 0 if false. For example,

    result="SLOC>50"

    returns 1 or 0 depending on the value of SLOC for the current artefact.

  • Use the CASE(measureId,case1:value1,case2:value2[,...][,DEFAULT:value]) function to assign different values to a measure based on the value of another measure. A fallback can be specified by using the DEFAULT case.

  • The NOT(computation) function returns 0 if the result of the computation is greater than or equal to 1, or 1 otherwise.

  • The RANK(scale_id,level_id) function provides a way to retrieve rank values from your model.

  • The FIND_RANK(scale_id,measure_id) function provides a way to retrieve a rank from your model by passing a measure and a scale.

  • The APP(measure_id) function provides a way to retrieve the value of a measure at application level from any artefact:

  • The PARENT(measure_id, [type]) and ANCESTOR(measure_id, [type]), functions provide a way to get a measure value for an artefact's parent or ancestor containing this measure. The concept is similar to that of the APP() function, but PARENT() only checks the artefact's direct parent and ANCESTOR() goes up the tree until finding an artefact (of the optionally specified type) that has the requested measure ID.

    Both functions have an equivalent filtered function to limit the scope of the values included in the search, using the syntax FPARENT(min,max,measure_id, [type]) and FANCESTOR(min,max,measure_id, [type]). Note that if min or max are omitted, they are automatically replaced by -Infinity and +Infinity respectively.

  • The IS_DP_OK(data_provider_name) function provides a way to find out if a Data Provider was executed successfully or not during the analysis. If the data provider was not executed or failed, the function returns 0. If the Data Provider was executed successfully, then the function returns 1.

  • The DP_STATUS(data_provider_name) function provides finer information about the execution status of a Data Provider than IS_DP_OK().

    • returns -1 if the DP was not run

    • returns 0 if the DP was successful

    • returns 1 if the DP returned some warnings

    • returns 2 if the DP reported errors

    • returns 3 if the DP stopped with a fatal error

  • The IS_ARTEFACT_TYPE(artefact_type) function provides a way to find out if an artefact is of the type specified. If the artefact is of the type specified, the function returns 1, else it returns 0.

Set a measure to 6 if SLOC is above a threshold, else set it to 4:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="2+IF(SLOC>50,4,2)" />
</Measure>

Set a measure to 6 if SLOC is above a value and below another one, else set it to 4:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="2+IF(SLOC>50 AND SLOC<100,4,2)" />
</Measure>

A nested IF construction:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="2+IF(I.LC>IF(SLOC>300,SLOC,MAX(250,300)),98,8)" />
</Measure>

Example using NOT:

<Measure measureId="OLD_LARGE_FILE" defaultValue="0">
<Computation targetArtefactTypes="FILE"
result="NOT(IS_NEW_ARTEFACT() AND SLOC<500)" />
</Measure>

An example of CASE() statement (on the metric :

<Measure measureId="EASE_OF_USE" defaultValue="0">
	<Computation targetArtefactTypes="APPLICATION"
				 result="CASE(FEEDBACK,BAD:0,GOOD:50,EXCELLENT:80,DEFAULT:-1)" />
</Measure>

Retrieving rank values using RANK(), given the following scale:

<Scale scaleId="SCALE_LINE">
<ScaleLevel levelId="LEVELA" bounds="];10]"  rank="0" />
<ScaleLevel levelId="LEVELB" bounds="]10;30]" rank="1" />
<ScaleLevel levelId="LEVELC" bounds="]30;60]" rank="2" />
<ScaleLevel levelId="LEVELD" bounds="]60;100]" rank="4" />
<ScaleLevel levelId="LEVELE" bounds="]100;[" rank="8" />
</Scale>

You can use the RANK function as follows to find the rank of LEVELD. The example below returns 4:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="RANK(SCALE_LINE,LEVELD)" />
</Measure>

For more information about scales, refer to the section called “Scales”.

Using RANK is useful when combined with conditions. The examples below are equivalent:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="IF(I.LC>4,1,0)" />
</Measure>
<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="IF(I.LC>RANK(SCALE_LINE,LEVELD),1,0)" />
</Measure>
<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="IF(I.LC>LEVELD,1,0)" />
</Measure>

In the last example, we use the short syntax for the RANK function: >LEVELD is only valid when used after an indicator. The rank retrieved is the rank of level LEVELD for the scale of the current artefact type for the indicator LC.

The FIND_RANK() function is mostly useful when using dynamic scales (see the section called “Dynamic Scales”). The example below assigns to TEST_COVERAGE_RANK the value of the rank for the value of COVERAGE on the scale DYN_SCALE_OK_KO:

<Measure measureId="OBJECTIVE" targetArtefactTypes="APPLICATION;FODLER;FILE;CLASS" defaultValue="0" />
<Measure measureId="COVERAGE" targetArtefactTypes="APPLICATION;FODLER;FILE;CLASS" defaultValue="0" />

<Scale scaleId="DYN_SCALE_OK_KO">
    <ScaleLevel levelId="DYN_OK" bounds="[;APP(OBJECTIVE)]"  rank="0" />
    <ScaleLevel levelId="DYN_KO" bounds="[APP(OBJECTIVE);]" rank="1" />
</Scale>

<Scale scaleId="SCALE_OK_KO">
    <ScaleLevel levelId="OK" bounds="[1;1]"  rank="0" />
    <ScaleLevel levelId="KO" bounds="[0;0]" rank="1" />
</Scale>

<Measure measureId="TEST_COVERAGE_RANK">
	<Computation targetArtefactTypes="APPLICATION"
				 result="FIND_RANK(DYN_SCALE_OK_KO,COVERAGE)" />
</Measure>

<Indicator 
    indicatorId="TEST_COVERAGE" 
    measureId="TEST_COVERAGE_RANK"
    scaleId="SCALE_OK_KO" />

Compute the percentage of lines of code present in the current artefact using the entire application as the reference, with APP():

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="(LC*100)/APP(LC)" />
</Measure>

Mark a method as risky if the parent class has changed, using PARENT():

<Measure measureId="RISKY" defaultValue="0">
<Computation targetArtefactTypes="FUNCTION"
result="PARENT(CHANGED,CLASS)" />
</Measure>

Set an artefact as critical if one of its containing folder is critical:

<Measure measureId="IS_CRITICAL" defaultValue="0">
<Computation targetArtefactTypes="FOLDER;CLASS;FUNCTION"
result="ANCESTOR(IS_CRITICAL,FOLDER)" />
</Measure>

Filtering with FPARENT():

IF(FPARENT(RANK(SCALE_LINE,LEVELG), RANK(SCALE_LINE,LEVELG), I.LC),1,2)
=> resolves as: IF(PARENT(I.LC)=RANK(SCALE_LINE,LEVELG),1,2)
=> return 1 if PARENT(I.LC) = LEVELG, otherwise 2

Filtering with FANCESTOR():

FANCESTOR(500,, LC, FOLDER)
=> returns LC for the first folder ancestor where LC >= 500

Find out if the Checkstyle Data Provider was executed successfully with IS_DP_OK:

<Measure measureId="RAN_CHECKSTYLE" defaultValue="0">
<Computation targetArtefactTypes="APPLICATION;FOLDER;FILE;FUNCTION"
result="IS_DP_OK(Checkstyle)" />
</Measure>

Do something if the artefact is a CHANGE_REQUEST, with IS_ARTEFACT_TYPE:

<ArtefactType id="ISSUE" heirs="BUG;CHANGE_REQUEST;HOTLINE;REGRESSION" />
<Measure measureId="IS_CR" defaultValue="0">
<Computation targetArtefactTypes="ISSUE"
result="IS_ARTEFACT_TYPE("CHANGE_REQUEST")" />
</Measure>

Temporal Functions

Temporal functions allow to retrieve and use values computed in the previous baseline. The available operators are:

  • PREVIOUS_VALUE(ID) to get the previous value of a measure or indicator

  • DELTA_VALUE(ID) to use the computed difference between the current value of a measure or indicator and its previous value

  • PREVIOUS_INFO(INFO_ID) allows retrieving the value of some artefact information in the previous version so it can be compared with the current artefact information (This is useful when combined with the EQUALS() or MATCHES() functions, as described in the section called “String Matching Functions”).

  • IS_NEW_ARTEFACT()tests whether the artefact is new for the current version of the project. It returns 1 if true, 0 if false.

Using the value of LC from the previous analysis:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="PREVIOUS_VALUE(LC)" />
</Measure>

Using the difference between the value of LC in the current analysis and its previous value:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="DELTA_VALUE(LC)" />
</Measure>

Obtaining the difference in ranking between two analyses for an artefact:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="DELTA_VALUE(RANK)" />
</Measure>

Defining a measure whose value is set to 1 when the artefact is new, else 0:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="IS_NEW_ARTEFACT()" />
</Measure>

Using IS_NEW_ARTEFACT as a condition operator:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="IF(IS_NEW_ARTEFACT(),3,4)" />
</Measure>

Note: IF(IS_NEW_ARTEFACT(),val_yes,val_no) is equivalent to IF(IS_NEW_ARTEFACT()>0,val_yes,val_no)

Date Functions

You can use the following operators to work with dates in your model:

  • DATE(yearParam,monthParam,dayParam) to convert year/month/day numbers to a date

  • DAYS(param) to pass a number as a number of days

  • TO_DAYS(dateDifference) to evaluate the number of dates between two dates

  • TODAY() to retrieve today's date at midnight

  • NOW() to retrieve today's exact date and time at the time of the analysis

  • VERSION_DATE() to retrieve the version's date. By default, this is the same value as the time of the analysis (NOW()), but users are allowed to specify a different date different from the current one.

Note: Dates are computed and stored internally as the number of milliseconds since January 1st 1970. However, calculations involving dates are designed to work with a number of days, not hours or minutes.

Converting to the date 28th July 1979:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="DATE(1979,07,28)" />
</Measure>

Converting to a date using measure IDs:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="DATE(YEAR_START+2,MONTH_START+MONTHS_SPENT,TARGET_DAY)" />
</Measure>

Find the number of days since the start of the project (the project attribute PROJECT_START_DATE) until today

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="TO_DAYS(TODAY()-PROJECT_START_DATE)" />
</Measure>

Add 4 days to May 19th 2012 to obtain May 23rd 2012:

<Measure measureId="EXAMPLE" defaultValue="0">
<Computation targetArtefactTypes="FILE;FUNCTION;CLASS"
result="DATE(2012,05,19)+DAYS(4)" />
</Measure>

Calculate whether an issue expires within a week of the analysis:

<Measure measureId="EXPIRES_THIS_WEEK" defaultValue="0">
<Computation targetArtefactTypes="BUG;CR"
result="IF(EXPIRY_DATE - DAYS(7) < VERSION_DATE(),1,0)" />
</Measure>

String Matching Functions

In order to extract the specific information that is added to artefacts by various Data Providers, you can use the INFO(info_tag) and ARTEFACT_NAME() functions. Both functions return a string containing the information requested. You can then use these string matching functions in your computations:

  • EQUALS('string','string'[, forceIgnoreCase])

  • CONTAINS('string','string'[, forceIgnoreCase])

  • STARTS_WITH('string','string'[, forceIgnoreCase])

  • ENDS_WITH('string','string'[, forceIgnoreCase])

  • MATCHES('string','regexp'[, forceIgnoreCase])

forceIgnoreCase is an optional boolean set to 1 by default. If you want to perform a case-sensitive search, use 0, instead.

Tip

You can also retrieve the previous value of some artefact information using the PREVIOUS_INFO() function, as described in the section called “Temporal Functions”

Here are some examples for these functions, based on an artefact with the following data:

<I n="LANGUAGES" v="Java, C#, C++, C"/>
<I n="AUTHOR" v="gabriel"/>
<I n="URL" v="http://www.my_url.com"/> [^]
<I n="ONE_LANGUAGE" v="JaVa"/>

EQUALS()

EQUALS(INFO(AUTHOR), 'gabriel')
=> 1
EQUALS(INFO(LANGUAGES), 'Java, C#, C++, C', 0)
=> 1

STARTS_WITH()

STARTS_WITH(INFO(URL), 'HTTP')
=> 1
STARTS_WITH(INFO(URL), 'HTTPS')
=> 0

ENDS_WITH()

ENDS_WITH(INFO(URL), '.COM')
=> 1
ENDS_WITH(INFO(URL), '.COM', 0)
=> 0 (note the case-sensitive flag)

CONTAINS()

CONTAINS(INFO(LANGUAGES), 'C++')
=> 1
CONTAINS(INFO(LANGUAGES), 'Cobol')
=> 0
CONTAINS(INFO(LANGUAGES), INFO(ONE_LANGUAGE), 1)
=> 1

MATCHES()

MATCHES(INFO(LANGUAGES), 'J.*', 0)
=> 1
MATCHES(INFO(LANGUAGES), '.*(, C\+\+).*', 0)
=> 1
MATCHES(INFO(LANGUAGES), '.*(, C\+\+\+).*', 0)
=> 0