diff --git a/src/main/java/eu/europa/ted/efx/exceptions/InvalidUsageException.java b/src/main/java/eu/europa/ted/efx/exceptions/InvalidUsageException.java
index ca5fd592..d91bb818 100644
--- a/src/main/java/eu/europa/ted/efx/exceptions/InvalidUsageException.java
+++ b/src/main/java/eu/europa/ted/efx/exceptions/InvalidUsageException.java
@@ -16,7 +16,9 @@
import org.antlr.v4.runtime.misc.ParseCancellationException;
/**
- * Exception thrown when field validation fails during EFX template processing.
+ * Exception thrown when an EFX construct is used incorrectly, such as referencing
+ * a non-withholdable field for privacy properties, or calling a template-only
+ * function in an expression or validation rule.
*/
@SuppressWarnings("squid:MaximumInheritanceDepth") // Necessary to integrate with ANTLR4 parser cancellation
public class InvalidUsageException extends ParseCancellationException {
@@ -26,7 +28,8 @@ public enum ErrorCode {
SHORTHAND_REQUIRES_FIELD_CONTEXT,
INVALID_NOTICE_SUBTYPE_RANGE_ORDER,
INVALID_NOTICE_SUBTYPE_TOKEN,
- FIELD_NOT_WITHHOLDABLE
+ FIELD_NOT_WITHHOLDABLE,
+ TEMPLATE_ONLY_FUNCTION
}
private static final String SHORTHAND_REQUIRES_CODE_OR_INDICATOR = "Indirect label reference shorthand #{%1$s}, requires a field of type 'code' or 'indicator'. Field %1$s is of type %2$s.";
@@ -34,6 +37,7 @@ public enum ErrorCode {
private static final String INVALID_NOTICE_SUBTYPE_RANGE_ORDER = "Notice subtype range '%s-%s' is not in ascending order.";
private static final String INVALID_NOTICE_SUBTYPE_TOKEN = "Invalid notice subtype token '%s'. Expected format: 'X' or 'X-Y'.";
private static final String FIELD_NOT_WITHHOLDABLE = "Field '%s' is always published and cannot be withheld from publication.";
+ private static final String TEMPLATE_ONLY_FUNCTION = "Function '%s' can only be used in templates, not in expressions or validation rules.";
private final ErrorCode errorCode;
@@ -65,4 +69,8 @@ public static InvalidUsageException invalidNoticeSubtypeToken(String token) {
public static InvalidUsageException fieldNotWithholdable(String fieldId) {
return new InvalidUsageException(ErrorCode.FIELD_NOT_WITHHOLDABLE, String.format(FIELD_NOT_WITHHOLDABLE, fieldId));
}
+
+ public static InvalidUsageException templateOnlyFunction(String functionName) {
+ return new InvalidUsageException(ErrorCode.TEMPLATE_ONLY_FUNCTION, String.format(TEMPLATE_ONLY_FUNCTION, functionName));
+ }
}
diff --git a/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java b/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java
index 5ab0e444..b5e6481b 100644
--- a/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java
+++ b/src/main/java/eu/europa/ted/efx/interfaces/ScriptGenerator.java
@@ -324,14 +324,139 @@ public NumericExpression composeNumericOperation(NumericExpression leftOperand,
// #region Numeric Functions ------------------------------------------------
+ /**
+ * Returns the target language script that counts the number of elements in a sequence.
+ *
+ * @param list The sequence whose elements are to be counted.
+ * @return A numeric expression representing the count.
+ */
public NumericExpression composeCountOperation(final SequenceExpression list);
+ /**
+ * Returns the target language script that converts a string to a number.
+ *
+ * @param text The string expression to convert.
+ * @return A numeric expression representing the converted value.
+ */
public NumericExpression composeToNumberConversion(StringExpression text);
+ /**
+ * Returns the target language script that converts a boolean to a number.
+ * Typically {@code TRUE} maps to {@code 1} and {@code FALSE} maps to {@code 0}.
+ *
+ * @param bool The boolean expression to convert.
+ * @return A numeric expression representing the converted value.
+ */
+ public NumericExpression composeToNumberConversion(BooleanExpression bool);
+
public NumericExpression composeSumOperation(NumericSequenceExpression list);
+ /**
+ * Returns the target language script that computes the minimum value in a numeric sequence.
+ *
+ * @param list The numeric sequence to find the minimum of.
+ * @return A numeric expression representing the minimum value.
+ */
+ public NumericExpression composeMinFunction(NumericSequenceExpression list);
+
+ /**
+ * Returns the target language script that computes the maximum value in a numeric sequence.
+ *
+ * @param list The numeric sequence to find the maximum of.
+ * @return A numeric expression representing the maximum value.
+ */
+ public NumericExpression composeMaxFunction(NumericSequenceExpression list);
+
+ /**
+ * Returns the target language script that computes the average of a numeric sequence.
+ *
+ * @param list The numeric sequence to average.
+ * @return A numeric expression representing the average value.
+ */
+ public NumericExpression composeAvgFunction(NumericSequenceExpression list);
+
public NumericExpression composeStringLengthCalculation(StringExpression text);
+ /**
+ * Returns the target language script that extracts the year component from a date.
+ *
+ * @param date The date expression to extract the year from.
+ * @return A numeric expression representing the year.
+ */
+ public NumericExpression composeYearFunction(DateExpression date);
+
+ /**
+ * Returns the target language script that extracts the month component from a date.
+ *
+ * @param date The date expression to extract the month from.
+ * @return A numeric expression representing the month (1-12).
+ */
+ public NumericExpression composeMonthFunction(DateExpression date);
+
+ /**
+ * Returns the target language script that extracts the day component from a date.
+ *
+ * @param date The date expression to extract the day from.
+ * @return A numeric expression representing the day of the month (1-31).
+ */
+ public NumericExpression composeDayFunction(DateExpression date);
+
+ /**
+ * Returns the target language script that extracts the hours component from a time.
+ *
+ * @param time The time expression to extract the hours from.
+ * @return A numeric expression representing the hours (0-23).
+ */
+ public NumericExpression composeHoursFunction(TimeExpression time);
+
+ /**
+ * Returns the target language script that extracts the minutes component from a time.
+ *
+ * @param time The time expression to extract the minutes from.
+ * @return A numeric expression representing the minutes (0-59).
+ */
+ public NumericExpression composeMinutesFunction(TimeExpression time);
+
+ /**
+ * Returns the target language script that extracts the seconds component from a time.
+ *
+ * @param time The time expression to extract the seconds from.
+ * @return A numeric expression representing the seconds (0-59).
+ */
+ public NumericExpression composeSecondsFunction(TimeExpression time);
+
+ /**
+ * Returns the target language script that computes the absolute value of a number.
+ *
+ * @param number The numeric expression whose absolute value is to be computed.
+ * @return A numeric expression representing the absolute value.
+ */
+ public NumericExpression composeAbsFunction(NumericExpression number);
+
+ /**
+ * Returns the target language script that rounds a number to the nearest integer.
+ *
+ * @param number The numeric expression to round.
+ * @return A numeric expression representing the rounded value.
+ */
+ public NumericExpression composeRoundFunction(NumericExpression number);
+
+ /**
+ * Returns the target language script that rounds a number down (towards negative infinity).
+ *
+ * @param number The numeric expression to round down.
+ * @return A numeric expression representing the rounded-down value.
+ */
+ public NumericExpression composeFloorFunction(NumericExpression number);
+
+ /**
+ * Returns the target language script that rounds a number up (towards positive infinity).
+ *
+ * @param number The numeric expression to round up.
+ * @return A numeric expression representing the rounded-up value.
+ */
+ public NumericExpression composeCeilingFunction(NumericExpression number);
+
// #endregion Numeric Functions -------------------------------------------
// #region String Functions -----------------------------------------------
@@ -355,8 +480,68 @@ public StringExpression composeSubstringExtraction(StringExpression text,
public StringExpression composeSubstringExtraction(StringExpression text, NumericExpression start,
NumericExpression length);
+ /**
+ * Returns the target language script that extracts the part of the text before the first
+ * occurrence of the delimiter. Returns an empty string if the delimiter is not found.
+ *
+ * @param text The text to search in.
+ * @param delimiter The delimiter to search for.
+ * @return The target language script for the substring before the delimiter.
+ */
+ public StringExpression composeSubstringBeforeFunction(StringExpression text,
+ StringExpression delimiter);
+
+ /**
+ * Returns the target language script that extracts the part of the text after the first
+ * occurrence of the delimiter. Returns an empty string if the delimiter is not found.
+ *
+ * @param text The text to search in.
+ * @param delimiter The delimiter to search for.
+ * @return The target language script for the substring after the delimiter.
+ */
+ public StringExpression composeSubstringAfterFunction(StringExpression text,
+ StringExpression delimiter);
+
+ /**
+ * Returns the target language script that converts a number to its string representation.
+ *
+ * @param number The numeric expression to convert.
+ * @return A string expression representing the converted value.
+ */
public StringExpression composeToStringConversion(NumericExpression number);
+ /**
+ * Returns the target language script that converts a boolean to its string representation.
+ *
+ * @param bool The boolean expression to convert.
+ * @return A string expression representing the converted value.
+ */
+ public StringExpression composeToStringConversion(BooleanExpression bool);
+
+ /**
+ * Returns the target language script that converts a date to its string representation.
+ *
+ * @param date The date expression to convert.
+ * @return A string expression representing the converted value.
+ */
+ public StringExpression composeToStringConversion(DateExpression date);
+
+ /**
+ * Returns the target language script that converts a time to its string representation.
+ *
+ * @param time The time expression to convert.
+ * @return A string expression representing the converted value.
+ */
+ public StringExpression composeToStringConversion(TimeExpression time);
+
+ /**
+ * Returns the target language script that converts a duration to its string representation.
+ *
+ * @param duration The duration expression to convert.
+ * @return A string expression representing the converted value.
+ */
+ public StringExpression composeToStringConversion(DurationExpression duration);
+
/**
* Returns the target language script that converts the given text to upper case.
*
@@ -379,6 +564,115 @@ public StringExpression composeSubstringExtraction(StringExpression text, Numeri
*/
public StringExpression composeToLowerCaseConversion(StringExpression text);
+ /**
+ * Returns the target language script that strips leading/trailing whitespace and collapses
+ * internal whitespace sequences to a single space.
+ *
+ * @param text The text to normalize.
+ * @return The target language script that normalizes whitespace in the text.
+ */
+ public StringExpression composeNormalizeSpaceFunction(StringExpression text);
+
+ /**
+ * Returns the target language script that removes leading and trailing whitespace from the text.
+ *
+ * @param text The text to trim.
+ * @return The target language script that trims whitespace from both ends.
+ */
+ public StringExpression composeTrimFunction(StringExpression text);
+
+ /**
+ * Returns the target language script that removes leading whitespace from the text.
+ *
+ * @param text The text to trim.
+ * @return The target language script that trims leading whitespace.
+ */
+ public StringExpression composeTrimLeftFunction(StringExpression text);
+
+ /**
+ * Returns the target language script that removes trailing whitespace from the text.
+ *
+ * @param text The text to trim.
+ * @return The target language script that trims trailing whitespace.
+ */
+ public StringExpression composeTrimRightFunction(StringExpression text);
+
+ /**
+ * Returns the target language script that pads the text on the left with the given character
+ * until it reaches the specified length. If the text is already at least the specified length,
+ * it is returned unchanged.
+ *
+ * @param text The text to pad.
+ * @param length The desired minimum length.
+ * @param padChar The character to pad with.
+ * @return The target language script that left-pads the text.
+ */
+ public StringExpression composePadLeftFunction(StringExpression text, NumericExpression length,
+ StringExpression padChar);
+
+ /**
+ * Returns the target language script that pads the text on the right with the given character
+ * until it reaches the specified length. If the text is already at least the specified length,
+ * it is returned unchanged.
+ *
+ * @param text The text to pad.
+ * @param length The desired minimum length.
+ * @param padChar The character to pad with.
+ * @return The target language script that right-pads the text.
+ */
+ public StringExpression composePadRightFunction(StringExpression text, NumericExpression length,
+ StringExpression padChar);
+
+ /**
+ * Returns the target language script that repeats the text the specified number of times.
+ *
+ * @param text The text to repeat.
+ * @param count The number of repetitions.
+ * @return The target language script that repeats the text.
+ */
+ public StringExpression composeRepeatFunction(StringExpression text, NumericExpression count);
+
+ /**
+ * Returns the target language script that replaces all occurrences of a literal search string
+ * with the replacement string.
+ *
+ * @param text The text to search in.
+ * @param search The literal string to search for.
+ * @param replacement The replacement string.
+ * @return The target language script that performs literal replacement.
+ */
+ public StringExpression composeReplaceFunction(StringExpression text, StringExpression search,
+ StringExpression replacement);
+
+ /**
+ * Returns the target language script that replaces all matches of a regular expression pattern
+ * with the replacement string. The pattern uses the EFX regex profile.
+ *
+ * @param text The text to search in.
+ * @param pattern The regex pattern to match.
+ * @param replacement The replacement string (may use capture group references).
+ * @return The target language script that performs regex replacement.
+ */
+ public StringExpression composeReplaceRegexFunction(StringExpression text,
+ StringExpression pattern, StringExpression replacement);
+
+ /**
+ * Composes a URL-encoding function call in the target language.
+ *
+ * @param text The string to URL-encode.
+ * @return The target language script that URL-encodes the string.
+ */
+ public StringExpression composeUrlEncodeFunction(StringExpression text);
+
+ /**
+ * Composes a capitalize-first function call in the target language.
+ * Converts the first character of the string to upper case.
+ *
+ * @param text The string whose first character to capitalize.
+ * @return The target language script that capitalizes the first character.
+ */
+ public StringExpression composeCapitalizeFirstFunction(StringExpression text);
+
/**
* Gets the target language script that retrieves the preferred language ID
* out of the languages available in the given field.
@@ -416,6 +710,15 @@ public StringExpression composeSubstringExtraction(StringExpression text, Numeri
public BooleanExpression composeExistsCondition(PathExpression reference);
+ /**
+ * Returns the target language script that converts a number to a boolean.
+ * Typically {@code 0} maps to {@code FALSE} and any non-zero value maps to {@code TRUE}.
+ *
+ * @param number The numeric expression to convert.
+ * @return A boolean expression representing the converted value.
+ */
+ public BooleanExpression composeToBooleanConversion(NumericExpression number);
+
/**
* Uniqueness check for EFX 1 syntax.
*
@@ -452,13 +755,45 @@ public BooleanExpression composeUniqueValueCondition(TimeExpression needle,
public BooleanExpression composeUniqueValueCondition(DurationExpression needle,
DurationSequenceExpression haystack);
+ /**
+ * Returns the target language script that checks whether two sequences contain the same
+ * elements, regardless of order.
+ *
+ * @param one The first sequence.
+ * @param two The second sequence.
+ * @return A boolean expression that is true when the two sequences are equal.
+ */
public BooleanExpression composeSequenceEqualFunction(SequenceExpression one,
SequenceExpression two);
+ /**
+ * Returns the target language script that checks whether a sequence is empty
+ * (contains no elements).
+ *
+ * @param sequence The sequence to check.
+ * @return A boolean expression that is true when the sequence is empty.
+ */
+ public BooleanExpression composeEmptySequenceCondition(SequenceExpression sequence);
+
+ /**
+ * Returns the target language script that checks whether all values in a sequence are
+ * distinct (i.e. the sequence has no duplicate values).
+ *
+ * @param sequence The sequence to check.
+ * @return A boolean expression that is true when all values are distinct.
+ */
+ public BooleanExpression composeIsDistinctCondition(SequenceExpression sequence);
+
// #endregion Boolean Functions --------------------------------------------
// #region Date Functions ---------------------------------------------------
+ /**
+ * Returns the target language script that converts a string to a date.
+ *
+ * @param pop The string expression to convert.
+ * @return A date expression representing the converted value.
+ */
public DateExpression composeToDateConversion(StringExpression pop);
public DateExpression composeAddition(final DateExpression date,
@@ -478,14 +813,34 @@ public DateExpression composeSubtraction(final DateExpression date,
// #region Time Functions ---------------------------------------------------
+ /**
+ * Returns the target language script that converts a string to a time.
+ *
+ * @param pop The string expression to convert.
+ * @return A time expression representing the converted value.
+ */
public TimeExpression composeToTimeConversion(StringExpression pop);
// #endregion Time Functions ------------------------------------------------
// #region Duration Functions -----------------------------------------------
+ /**
+ * Returns the target language script that converts a string to a day-time duration
+ * (e.g. {@code "P3DT4H"} for 3 days and 4 hours).
+ *
+ * @param text The string expression to convert.
+ * @return A duration expression representing the converted value.
+ */
public DurationExpression composeToDayTimeDurationConversion(StringExpression text);
+ /**
+ * Returns the target language script that converts a string to a year-month duration
+ * (e.g. {@code "P2Y3M"} for 2 years and 3 months).
+ *
+ * @param text The string expression to convert.
+ * @return A duration expression representing the converted value.
+ */
public DurationExpression composeToYearMonthDurationConversion(StringExpression text);
public DurationExpression composeSubtraction(DateExpression startDate, DateExpression endDate);
@@ -493,6 +848,57 @@ public DateExpression composeSubtraction(final DateExpression date,
public StringExpression composeNumberFormatting(NumericExpression number,
StringExpression format);
+ /**
+ * Formats a date using a short locale-aware format (e.g. 15/02/2026).
+ *
+ * @param date The date expression to format.
+ * @return A string expression representing the formatted date.
+ */
+ public StringExpression composeFormatDateShort(DateExpression date);
+
+ /**
+ * Formats a date using a medium locale-aware format with abbreviated month name
+ * (e.g. 15 Feb 2026).
+ *
+ * @param date The date expression to format.
+ * @return A string expression representing the formatted date.
+ */
+ public StringExpression composeFormatDateMedium(DateExpression date);
+
+ /**
+ * Formats a date using a long locale-aware format with full month name
+ * (e.g. 15 February 2026).
+ *
+ * @param date The date expression to format.
+ * @return A string expression representing the formatted date.
+ */
+ public StringExpression composeFormatDateLong(DateExpression date);
+
+ /**
+ * Formats a time using a short locale-aware format (e.g. 14:30 CET).
+ *
+ * @param time The time expression to format.
+ * @return A string expression representing the formatted time.
+ */
+ public StringExpression composeFormatTimeShort(TimeExpression time);
+
+ /**
+ * Formats a time using a medium locale-aware format with seconds (e.g. 14:30:00).
+ *
+ * @param time The time expression to format.
+ * @return A string expression representing the formatted time.
+ */
+ public StringExpression composeFormatTimeMedium(TimeExpression time);
+
+ /**
+ * Formats a time using a long locale-aware format with seconds and timezone
+ * (e.g. 14:30:00 CET).
+ *
+ * @param time The time expression to format.
+ * @return A string expression representing the formatted time.
+ */
+ public StringExpression composeFormatTimeLong(TimeExpression time);
+
public DurationExpression composeMultiplication(final NumericExpression number,
final DurationExpression duration);
@@ -506,18 +912,148 @@ public DurationExpression composeSubtraction(final DurationExpression left,
// #region Sequence Functions --------------------------------------------
+ /**
+ * Returns the target language script that removes duplicate values from a sequence,
+ * preserving only distinct values.
+ *
+ * @param The type of the sequence expression.
+ * @param list The sequence to remove duplicates from.
+ * @param listType The class of the sequence expression type.
+ * @return A sequence containing only the distinct values.
+ */
public T composeDistinctValuesFunction(
T list, Class listType);
+ /**
+ * Returns the target language script that computes the union of two sequences
+ * (all values from both, with duplicates removed).
+ *
+ * @param The type of the sequence expression.
+ * @param listOne The first sequence.
+ * @param listTwo The second sequence.
+ * @param listType The class of the sequence expression type.
+ * @return A sequence containing the union of both sequences.
+ */
public T composeUnionFunction(T listOne,
T listTwo, Class listType);
+ /**
+ * Returns the target language script that computes the intersection of two sequences
+ * (only values present in both).
+ *
+ * @param The type of the sequence expression.
+ * @param listOne The first sequence.
+ * @param listTwo The second sequence.
+ * @param listType The class of the sequence expression type.
+ * @return A sequence containing only the values present in both sequences.
+ */
public T composeIntersectFunction(T listOne,
T listTwo, Class listType);
+ /**
+ * Returns the target language script that computes the difference of two sequences
+ * (values in the first sequence that are not in the second).
+ *
+ * @param The type of the sequence expression.
+ * @param listOne The first sequence.
+ * @param listTwo The second sequence.
+ * @param listType The class of the sequence expression type.
+ * @return A sequence containing values from the first sequence not present in the second.
+ */
public T composeExceptFunction(T listOne,
T listTwo, Class listType);
+ /**
+ * Returns the target language script that sorts a sequence in ascending order.
+ *
+ * @param The type of the sequence expression.
+ * @param list The sequence to sort.
+ * @param listType The class of the sequence expression type.
+ * @return A sorted sequence.
+ */
+ public T composeSortFunction(T list, Class listType);
+
+ /**
+ * Returns the target language script that reverses the order of elements in a sequence.
+ *
+ * @param The type of the sequence expression.
+ * @param list The sequence to reverse.
+ * @param listType The class of the sequence expression type.
+ * @return A reversed sequence.
+ */
+ public T composeReverseFunction(T list, Class listType);
+
+ /**
+ * Returns the target language script that extracts a contiguous subsequence starting
+ * at the given position, through the end of the sequence.
+ *
+ * @param The type of the sequence expression.
+ * @param list The source sequence.
+ * @param start The 1-based starting position.
+ * @param listType The class of the sequence expression type.
+ * @return A subsequence from the starting position to the end.
+ */
+ public T composeSubsequenceFunction(T list,
+ NumericExpression start, Class listType);
+
+ /**
+ * Returns the target language script that extracts a contiguous subsequence of the
+ * given length, starting at the given position.
+ *
+ * @param The type of the sequence expression.
+ * @param list The source sequence.
+ * @param start The 1-based starting position.
+ * @param length The maximum number of elements to extract.
+ * @param listType The class of the sequence expression type.
+ * @return A subsequence of the given length starting at the given position.
+ */
+ public T composeSubsequenceFunction(T list,
+ NumericExpression start, NumericExpression length, Class listType);
+
+ /**
+ * Returns the target language script that finds the 1-based position of the first occurrence of a
+ * value within a sequence. Returns 0 if the value is not found.
+ *
+ * @param list The sequence to search in.
+ * @param value The value to search for.
+ * @return A numeric expression with the 1-based position of the first occurrence, or 0 if not
+ * found.
+ */
+ public NumericExpression composeIndexOfFunction(SequenceExpression list,
+ ScalarExpression value);
+
+ /**
+ * Returns the target language script that splits a string into a sequence of substrings
+ * using the given literal delimiter.
+ *
+ * @param text The text to split.
+ * @param delimiter The literal delimiter to split on.
+ * @return A string sequence expression with the split parts.
+ */
+ public StringSequenceExpression composeSplitFunction(StringExpression text,
+ StringExpression delimiter);
+
+ /**
+ * Returns the target language script that finds the 1-based position of the first occurrence
+ * of a substring within a string. Returns 0 if the substring is not found.
+ *
+ * @param text The text to search in.
+ * @param substring The substring to search for.
+ * @return A numeric expression with the 1-based position, or 0 if not found.
+ */
+ public NumericExpression composeIndexOfSubstringFunction(StringExpression text,
+ StringExpression substring);
+
+ /**
+ * Returns the target language script that retrieves the element at a given position
+ * in a sequence.
+ *
+ * @param The scalar type of the elements in the sequence.
+ * @param list The sequence to index into.
+ * @param index The 1-based position of the element to retrieve.
+ * @param type The class of the scalar expression type.
+ * @return The element at the given position.
+ */
public T composeIndexer(SequenceExpression list,
NumericExpression index, Class type);
diff --git a/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java b/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
index 1484dd70..894b5afc 100644
--- a/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
+++ b/src/main/java/eu/europa/ted/efx/sdk1/EfxExpressionTranslatorV1.java
@@ -315,20 +315,20 @@ public void exitSingleExpression(SingleExpressionContext ctx) {
@Override
public void exitParenthesizedBooleanExpression(
- EfxParser.ParenthesizedBooleanExpressionContext ctx) {
+ ParenthesizedBooleanExpressionContext ctx) {
this.stack.push(this.script.composeParenthesizedExpression(
this.stack.pop(BooleanExpression.class), BooleanExpression.class));
}
@Override
- public void exitLogicalAndCondition(EfxParser.LogicalAndConditionContext ctx) {
+ public void exitLogicalAndCondition(LogicalAndConditionContext ctx) {
BooleanExpression right = this.stack.pop(BooleanExpression.class);
BooleanExpression left = this.stack.pop(BooleanExpression.class);
this.stack.push(this.script.composeLogicalAnd(left, right));
}
@Override
- public void exitLogicalOrCondition(EfxParser.LogicalOrConditionContext ctx) {
+ public void exitLogicalOrCondition(LogicalOrConditionContext ctx) {
BooleanExpression right = this.stack.pop(BooleanExpression.class);
BooleanExpression left = this.stack.pop(BooleanExpression.class);
this.stack.push(this.script.composeLogicalOr(left, right));
@@ -393,7 +393,7 @@ public void exitDurationComparison(DurationComparisonContext ctx) {
// #region Boolean expressions - Conditions --------------------------------
@Override
- public void exitEmptinessCondition(EfxParser.EmptinessConditionContext ctx) {
+ public void exitEmptinessCondition(EmptinessConditionContext ctx) {
StringExpression expression = this.stack.pop(StringExpression.class);
String operator = ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER) ? "!=" : "==";
this.stack.push(this.script.composeComparisonOperation(expression, operator,
@@ -401,7 +401,7 @@ public void exitEmptinessCondition(EfxParser.EmptinessConditionContext ctx) {
}
@Override
- public void exitPresenceCondition(EfxParser.PresenceConditionContext ctx) {
+ public void exitPresenceCondition(PresenceConditionContext ctx) {
PathExpression reference = this.stack.pop(PathExpression.class);
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
this.stack.push(this.script.composeLogicalNot(this.script.composeExistsCondition(reference)));
@@ -411,7 +411,7 @@ public void exitPresenceCondition(EfxParser.PresenceConditionContext ctx) {
}
@Override
- public void exitUniqueValueCondition(EfxParser.UniqueValueConditionContext ctx) {
+ public void exitUniqueValueCondition(UniqueValueConditionContext ctx) {
PathExpression haystack = this.stack.pop(PathExpression.class);
PathExpression needle = this.stack.pop(haystack.getClass());
@@ -424,7 +424,7 @@ public void exitUniqueValueCondition(EfxParser.UniqueValueConditionContext ctx)
}
@Override
- public void exitLikePatternCondition(EfxParser.LikePatternConditionContext ctx) {
+ public void exitLikePatternCondition(LikePatternConditionContext ctx) {
StringExpression expression = this.stack.pop(StringExpression.class);
BooleanExpression condition = this.script.composePatternMatchCondition(expression, ctx.pattern.getText());
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
@@ -438,7 +438,7 @@ public void exitLikePatternCondition(EfxParser.LikePatternConditionContext ctx)
// #region Boolean expressions - List membership conditions -----------------
@Override
- public void exitStringInListCondition(EfxParser.StringInListConditionContext ctx) {
+ public void exitStringInListCondition(StringInListConditionContext ctx) {
this.exitInListCondition(ctx.modifier, StringExpression.class, StringSequenceExpression.class);
}
@@ -501,14 +501,14 @@ public void exitQuantifiedExpression(QuantifiedExpressionContext ctx) {
// #region Numeric expressions ----------------------------------------------
@Override
- public void exitAdditionExpression(EfxParser.AdditionExpressionContext ctx) {
+ public void exitAdditionExpression(AdditionExpressionContext ctx) {
NumericExpression right = this.stack.pop(NumericExpression.class);
NumericExpression left = this.stack.pop(NumericExpression.class);
this.stack.push(this.script.composeNumericOperation(left, ctx.operator.getText(), right));
}
@Override
- public void exitMultiplicationExpression(EfxParser.MultiplicationExpressionContext ctx) {
+ public void exitMultiplicationExpression(MultiplicationExpressionContext ctx) {
NumericExpression right = this.stack.pop(NumericExpression.class);
NumericExpression left = this.stack.pop(NumericExpression.class);
this.stack.push(this.script.composeNumericOperation(left, ctx.operator.getText(), right));
@@ -930,7 +930,7 @@ public void exitSimpleNodeReference(SimpleNodeReferenceContext ctx) {
}
@Override
- public void exitSimpleFieldReference(EfxParser.SimpleFieldReferenceContext ctx) {
+ public void exitSimpleFieldReference(SimpleFieldReferenceContext ctx) {
this.stack.push(
symbols.getRelativePathOfField(ctx.FieldId().getText(), this.efxContext.symbol()));
}
@@ -943,14 +943,14 @@ public void enterAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
}
@Override
- public void exitAbsoluteFieldReference(EfxParser.AbsoluteFieldReferenceContext ctx) {
+ public void exitAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.pop();
}
}
@Override
- public void enterAbsoluteNodeReference(EfxParser.AbsoluteNodeReferenceContext ctx) {
+ public void enterAbsoluteNodeReference(AbsoluteNodeReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.push(null);
}
@@ -976,7 +976,7 @@ public void exitNodeReferenceWithPredicate(NodeReferenceWithPredicateContext ctx
}
@Override
- public void exitFieldReferenceWithPredicate(EfxParser.FieldReferenceWithPredicateContext ctx) {
+ public void exitFieldReferenceWithPredicate(FieldReferenceWithPredicateContext ctx) {
if (ctx.predicate() != null) {
BooleanExpression predicate = this.stack.pop(BooleanExpression.class);
PathExpression fieldReference = this.stack.pop(PathExpression.class);
@@ -992,7 +992,7 @@ public void exitFieldReferenceWithPredicate(EfxParser.FieldReferenceWithPredicat
* @param ctx The predicate context
*/
@Override
- public void enterPredicate(EfxParser.PredicateContext ctx) {
+ public void enterPredicate(PredicateContext ctx) {
var parent = ctx.getParent();
if (parent instanceof NodeReferenceWithPredicateContext) {
final String nodeId = getNodeId((NodeReferenceWithPredicateContext) parent);
@@ -1009,7 +1009,7 @@ public void enterPredicate(EfxParser.PredicateContext ctx) {
* After the predicate is parsed we need to switch back to the previous context.
*/
@Override
- public void exitPredicate(EfxParser.PredicateContext ctx) {
+ public void exitPredicate(PredicateContext ctx) {
this.efxContext.pop();
}
@@ -1026,7 +1026,7 @@ public void exitFieldReferenceWithAxis(FieldReferenceWithAxisContext ctx) {
// #region External References ----------------------------------------------
@Override
- public void exitNoticeReference(EfxParser.NoticeReferenceContext ctx) {
+ public void exitNoticeReference(NoticeReferenceContext ctx) {
this.stack.push(this.script.composeExternalReference(this.stack.pop(StringExpression.class)));
}
@@ -1039,7 +1039,7 @@ public void enterFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext
}
@Override
- public void exitFieldReferenceInOtherNotice(EfxParser.FieldReferenceInOtherNoticeContext ctx) {
+ public void exitFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext ctx) {
if (ctx.noticeReference() != null) {
PathExpression field = this.stack.pop(PathExpression.class);
PathExpression notice = this.stack.pop(PathExpression.class);
diff --git a/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java b/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
index bad88c44..1d21346b 100644
--- a/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
+++ b/src/main/java/eu/europa/ted/efx/sdk1/EfxTemplateTranslatorV1.java
@@ -56,23 +56,7 @@
import eu.europa.ted.efx.model.types.EfxDataType;
import eu.europa.ted.efx.model.variables.Variable;
import eu.europa.ted.efx.model.variables.Variables;
-import eu.europa.ted.efx.sdk1.EfxParser.AssetIdContext;
-import eu.europa.ted.efx.sdk1.EfxParser.AssetTypeContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ContextDeclarationBlockContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ExpressionTemplateContext;
-import eu.europa.ted.efx.sdk1.EfxParser.LabelTemplateContext;
-import eu.europa.ted.efx.sdk1.EfxParser.LabelTypeContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandBtLabelReferenceContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandFieldLabelReferenceContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandFieldValueReferenceFromContextFieldContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandIndirectLabelReferenceContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandIndirectLabelReferenceFromContextFieldContext;
-import eu.europa.ted.efx.sdk1.EfxParser.ShorthandLabelReferenceFromContextContext;
-import eu.europa.ted.efx.sdk1.EfxParser.StandardExpressionBlockContext;
-import eu.europa.ted.efx.sdk1.EfxParser.StandardLabelReferenceContext;
-import eu.europa.ted.efx.sdk1.EfxParser.TemplateFileContext;
-import eu.europa.ted.efx.sdk1.EfxParser.TemplateLineContext;
-import eu.europa.ted.efx.sdk1.EfxParser.TextTemplateContext;
+import eu.europa.ted.efx.sdk1.EfxParser.*;
/**
* The EfxTemplateTranslator extends the {@link EfxExpressionTranslatorV1} to provide additional
diff --git a/src/main/java/eu/europa/ted/efx/sdk1/xpath/XPathScriptGeneratorV1.java b/src/main/java/eu/europa/ted/efx/sdk1/xpath/XPathScriptGeneratorV1.java
index 5ce7c9fb..6f696442 100644
--- a/src/main/java/eu/europa/ted/efx/sdk1/xpath/XPathScriptGeneratorV1.java
+++ b/src/main/java/eu/europa/ted/efx/sdk1/xpath/XPathScriptGeneratorV1.java
@@ -20,6 +20,8 @@
import eu.europa.ted.efx.interfaces.TranslatorOptions;
import eu.europa.ted.efx.model.expressions.Expression;
import eu.europa.ted.efx.model.expressions.PathExpression;
+import eu.europa.ted.efx.model.expressions.scalar.NumericExpression;
+import eu.europa.ted.efx.model.expressions.scalar.StringExpression;
import eu.europa.ted.efx.model.types.EfxDataType;
import eu.europa.ted.efx.xpath.XPathScriptGenerator;
@@ -30,6 +32,12 @@ public XPathScriptGeneratorV1(TranslatorOptions translatorOptions) {
super(translatorOptions);
}
+ @Override
+ public StringExpression composeToStringConversion(NumericExpression number) {
+ String formatString = this.translatorOptions.getDecimalFormat().adaptFormatString("0.##########");
+ return new StringExpression("format-number(" + number.getScript() + ", '" + formatString + "')");
+ }
+
/***
* This method is overridden to workaround a limitation of EFX 1.
*
diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java
index 6c23495e..8f626f8f 100644
--- a/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java
+++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2.java
@@ -60,7 +60,6 @@
import eu.europa.ted.efx.model.expressions.scalar.BooleanExpression;
import eu.europa.ted.efx.model.expressions.scalar.DateExpression;
import eu.europa.ted.efx.model.expressions.scalar.DurationExpression;
-import eu.europa.ted.efx.model.expressions.scalar.MultilingualStringPath;
import eu.europa.ted.efx.model.expressions.scalar.NumericExpression;
import eu.europa.ted.efx.model.expressions.scalar.ScalarExpression;
import eu.europa.ted.efx.model.expressions.scalar.ScalarPath;
@@ -105,6 +104,8 @@ public class EfxExpressionTranslatorV2 extends EfxBaseListener
private static final String NOT_MODIFIER =
EfxLexer.VOCABULARY.getLiteralName(EfxLexer.Not).replaceAll("^'|'$", "");
+ private static final String NO_MODIFIER =
+ EfxLexer.VOCABULARY.getLiteralName(EfxLexer.No).replaceAll("^'|'$", "");
private static final String BEGIN_EXPRESSION_BLOCK = "{";
private static final String END_EXPRESSION_BLOCK = "}";
@@ -237,7 +238,7 @@ protected String getFieldId(LinkedFieldReferenceContext ctx) {
return this.getLinkedFieldId(baseFieldId, ctx.linkedFieldProperty());
}
- protected String getFieldId(EfxParser.FieldMentionContext ctx) {
+ protected String getFieldId(FieldMentionContext ctx) {
if (ctx == null) {
return null;
}
@@ -380,20 +381,20 @@ public void exitSingleExpression(SingleExpressionContext ctx) {
@Override
public void exitParenthesizedBooleanExpression(
- EfxParser.ParenthesizedBooleanExpressionContext ctx) {
+ ParenthesizedBooleanExpressionContext ctx) {
this.stack.push(this.script.composeParenthesizedExpression(
this.stack.pop(BooleanExpression.class), BooleanExpression.class));
}
@Override
- public void exitLogicalAndCondition(EfxParser.LogicalAndConditionContext ctx) {
+ public void exitLogicalAndCondition(LogicalAndConditionContext ctx) {
BooleanExpression right = this.stack.pop(BooleanExpression.class);
BooleanExpression left = this.stack.pop(BooleanExpression.class);
this.stack.push(this.script.composeLogicalAnd(left, right));
}
@Override
- public void exitLogicalOrCondition(EfxParser.LogicalOrConditionContext ctx) {
+ public void exitLogicalOrCondition(LogicalOrConditionContext ctx) {
BooleanExpression right = this.stack.pop(BooleanExpression.class);
BooleanExpression left = this.stack.pop(BooleanExpression.class);
this.stack.push(this.script.composeLogicalOr(left, right));
@@ -448,15 +449,14 @@ public void exitDurationComparison(DurationComparisonContext ctx) {
// #region Boolean expressions - Conditions --------------------------------
@Override
- public void exitEmptinessCondition(EfxParser.EmptinessConditionContext ctx) {
+ public void exitStringEmptyFunction(StringEmptyFunctionContext ctx) {
StringExpression expression = this.stack.pop(StringExpression.class);
- String operator = ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER) ? "!=" : "==";
- this.stack.push(this.script.composeComparisonOperation(expression, operator,
+ this.stack.push(this.script.composeComparisonOperation(expression, "==",
this.script.getStringLiteralFromUnquotedString("")));
}
@Override
- public void exitPresenceCondition(EfxParser.PresenceConditionContext ctx) {
+ public void exitPresenceCondition(PresenceConditionContext ctx) {
PathExpression reference = this.stack.pop(PathExpression.class);
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
this.stack.push(this.script.composeLogicalNot(this.script.composeExistsCondition(reference)));
@@ -466,7 +466,7 @@ public void exitPresenceCondition(EfxParser.PresenceConditionContext ctx) {
}
@Override
- public void exitStringUniqueValueCondition(EfxParser.StringUniqueValueConditionContext ctx) {
+ public void exitStringUniqueValueCondition(StringUniqueValueConditionContext ctx) {
StringSequenceExpression haystack = this.stack.pop(StringSequenceExpression.class);
StringExpression needle = this.stack.pop(StringExpression.class);
@@ -479,7 +479,7 @@ public void exitStringUniqueValueCondition(EfxParser.StringUniqueValueConditionC
}
@Override
- public void exitNumericUniqueValueCondition(EfxParser.NumericUniqueValueConditionContext ctx) {
+ public void exitNumericUniqueValueCondition(NumericUniqueValueConditionContext ctx) {
NumericSequenceExpression haystack = this.stack.pop(NumericSequenceExpression.class);
NumericExpression needle = this.stack.pop(NumericExpression.class);
@@ -492,7 +492,7 @@ public void exitNumericUniqueValueCondition(EfxParser.NumericUniqueValueConditio
}
@Override
- public void exitBooleanUniqueValueCondition(EfxParser.BooleanUniqueValueConditionContext ctx) {
+ public void exitBooleanUniqueValueCondition(BooleanUniqueValueConditionContext ctx) {
BooleanSequenceExpression haystack = this.stack.pop(BooleanSequenceExpression.class);
BooleanExpression needle = this.stack.pop(BooleanExpression.class);
@@ -505,7 +505,7 @@ public void exitBooleanUniqueValueCondition(EfxParser.BooleanUniqueValueConditio
}
@Override
- public void exitDateUniqueValueCondition(EfxParser.DateUniqueValueConditionContext ctx) {
+ public void exitDateUniqueValueCondition(DateUniqueValueConditionContext ctx) {
DateSequenceExpression haystack = this.stack.pop(DateSequenceExpression.class);
DateExpression needle = this.stack.pop(DateExpression.class);
@@ -518,7 +518,7 @@ public void exitDateUniqueValueCondition(EfxParser.DateUniqueValueConditionConte
}
@Override
- public void exitTimeUniqueValueCondition(EfxParser.TimeUniqueValueConditionContext ctx) {
+ public void exitTimeUniqueValueCondition(TimeUniqueValueConditionContext ctx) {
TimeSequenceExpression haystack = this.stack.pop(TimeSequenceExpression.class);
TimeExpression needle = this.stack.pop(TimeExpression.class);
@@ -531,7 +531,7 @@ public void exitTimeUniqueValueCondition(EfxParser.TimeUniqueValueConditionConte
}
@Override
- public void exitDurationUniqueValueCondition(EfxParser.DurationUniqueValueConditionContext ctx) {
+ public void exitDurationUniqueValueCondition(DurationUniqueValueConditionContext ctx) {
DurationSequenceExpression haystack = this.stack.pop(DurationSequenceExpression.class);
DurationExpression needle = this.stack.pop(DurationExpression.class);
@@ -544,7 +544,99 @@ public void exitDurationUniqueValueCondition(EfxParser.DurationUniqueValueCondit
}
@Override
- public void exitLikePatternCondition(EfxParser.LikePatternConditionContext ctx) {
+ public void exitStringSequenceEmptinessCondition(
+ StringSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(StringSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitBooleanSequenceEmptinessCondition(
+ BooleanSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(BooleanSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitNumericSequenceEmptinessCondition(
+ NumericSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(NumericSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitDateSequenceEmptinessCondition(
+ DateSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(DateSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitTimeSequenceEmptinessCondition(
+ TimeSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(TimeSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitDurationSequenceEmptinessCondition(
+ DurationSequenceEmptinessConditionContext ctx) {
+ exitSequenceEmptinessCondition(DurationSequenceExpression.class, ctx.modifier);
+ }
+
+ private void exitSequenceEmptinessCondition(
+ Class sequenceType, Token modifier) {
+ final T sequence = this.stack.pop(sequenceType);
+ BooleanExpression condition = this.script.composeEmptySequenceCondition(sequence);
+ if (modifier != null && modifier.getText().equals(NOT_MODIFIER)) {
+ condition = this.script.composeLogicalNot(condition);
+ }
+ this.stack.push(condition);
+ }
+
+ @Override
+ public void exitStringSequenceDistinctCondition(
+ StringSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(StringSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitBooleanSequenceDistinctCondition(
+ BooleanSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(BooleanSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitNumericSequenceDistinctCondition(
+ NumericSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(NumericSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitDateSequenceDistinctCondition(
+ DateSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(DateSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitTimeSequenceDistinctCondition(
+ TimeSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(TimeSequenceExpression.class, ctx.modifier);
+ }
+
+ @Override
+ public void exitDurationSequenceDistinctCondition(
+ DurationSequenceDistinctConditionContext ctx) {
+ exitSequenceDistinctCondition(DurationSequenceExpression.class, ctx.modifier);
+ }
+
+ private void exitSequenceDistinctCondition(
+ Class sequenceType, Token modifier) {
+ final T sequence = this.stack.pop(sequenceType);
+ BooleanExpression condition = this.script.composeIsDistinctCondition(sequence);
+ if (modifier == null) {
+ condition = this.script.composeLogicalNot(condition);
+ }
+ this.stack.push(condition);
+ }
+
+ @Override
+ public void exitLikePatternCondition(LikePatternConditionContext ctx) {
StringExpression expression = this.stack.pop(StringExpression.class);
BooleanExpression condition = this.script.composePatternMatchCondition(expression, ctx.pattern.getText());
if (ctx.modifier != null && ctx.modifier.getText().equals(NOT_MODIFIER)) {
@@ -558,7 +650,7 @@ public void exitLikePatternCondition(EfxParser.LikePatternConditionContext ctx)
// #region Boolean expressions - List membership conditions -----------------
@Override
- public void exitStringInListCondition(EfxParser.StringInListConditionContext ctx) {
+ public void exitStringInListCondition(StringInListConditionContext ctx) {
this.exitInListCondition(ctx.modifier, StringExpression.class, StringSequenceExpression.class);
}
@@ -629,14 +721,14 @@ public void exitQuantifiedExpression(QuantifiedExpressionContext ctx) {
// #region Numeric expressions ----------------------------------------------
@Override
- public void exitAdditionExpression(EfxParser.AdditionExpressionContext ctx) {
+ public void exitAdditionExpression(AdditionExpressionContext ctx) {
NumericExpression right = this.stack.pop(NumericExpression.class);
NumericExpression left = this.stack.pop(NumericExpression.class);
this.stack.push(this.script.composeNumericOperation(left, ctx.operator.getText(), right));
}
@Override
- public void exitMultiplicationExpression(EfxParser.MultiplicationExpressionContext ctx) {
+ public void exitMultiplicationExpression(MultiplicationExpressionContext ctx) {
NumericExpression right = this.stack.pop(NumericExpression.class);
NumericExpression left = this.stack.pop(NumericExpression.class);
this.stack.push(this.script.composeNumericOperation(left, ctx.operator.getText(), right));
@@ -1130,7 +1222,7 @@ public void exitSimpleNodeReference(SimpleNodeReferenceContext ctx) {
}
@Override
- public void exitSimpleFieldReference(EfxParser.SimpleFieldReferenceContext ctx) {
+ public void exitSimpleFieldReference(SimpleFieldReferenceContext ctx) {
this.stack.push(
symbols.getRelativePathOfField(ctx.fieldId.getText(), this.efxContext.symbol()));
}
@@ -1153,14 +1245,14 @@ public void enterAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
}
@Override
- public void exitAbsoluteFieldReference(EfxParser.AbsoluteFieldReferenceContext ctx) {
+ public void exitAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.pop();
}
}
@Override
- public void enterAbsoluteNodeReference(EfxParser.AbsoluteNodeReferenceContext ctx) {
+ public void enterAbsoluteNodeReference(AbsoluteNodeReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.push(null);
}
@@ -1186,7 +1278,7 @@ public void exitNodeReferenceWithPredicate(NodeReferenceWithPredicateContext ctx
}
@Override
- public void exitFieldReferenceWithPredicate(EfxParser.FieldReferenceWithPredicateContext ctx) {
+ public void exitFieldReferenceWithPredicate(FieldReferenceWithPredicateContext ctx) {
if (ctx.predicate() != null) {
BooleanExpression predicate = this.stack.pop(BooleanExpression.class);
PathExpression fieldReference = this.stack.pop(PathExpression.class);
@@ -1202,7 +1294,7 @@ public void exitFieldReferenceWithPredicate(EfxParser.FieldReferenceWithPredicat
* @param ctx The predicate context
*/
@Override
- public void enterPredicate(EfxParser.PredicateContext ctx) {
+ public void enterPredicate(PredicateContext ctx) {
var parent = ctx.getParent();
if (parent instanceof NodeReferenceWithPredicateContext) {
final String nodeId = getNodeId((NodeReferenceWithPredicateContext) parent);
@@ -1219,7 +1311,7 @@ public void enterPredicate(EfxParser.PredicateContext ctx) {
* After the predicate is parsed we need to switch back to the previous context.
*/
@Override
- public void exitPredicate(EfxParser.PredicateContext ctx) {
+ public void exitPredicate(PredicateContext ctx) {
this.efxContext.pop();
}
@@ -1236,7 +1328,7 @@ public void exitFieldReferenceWithAxis(FieldReferenceWithAxisContext ctx) {
// #region External References ----------------------------------------------
@Override
- public void exitNoticeReference(EfxParser.NoticeReferenceContext ctx) {
+ public void exitNoticeReference(NoticeReferenceContext ctx) {
this.stack.push(this.script.composeExternalReference(this.stack.pop(StringExpression.class)));
}
@@ -1249,7 +1341,7 @@ public void enterFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext
}
@Override
- public void exitFieldReferenceInOtherNotice(EfxParser.FieldReferenceInOtherNoticeContext ctx) {
+ public void exitFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext ctx) {
if (ctx.noticeReference() != null) {
PathExpression field = this.stack.pop(PathExpression.class);
PathExpression notice = this.stack.pop(PathExpression.class);
@@ -1649,6 +1741,11 @@ public void exitNotFunction(NotFunctionContext ctx) {
this.stack.push(this.script.composeLogicalNot(this.stack.pop(BooleanExpression.class)));
}
+ @Override
+ public void exitBooleanFromNumberFunction(BooleanFromNumberFunctionContext ctx) {
+ this.stack.push(this.script.composeToBooleanConversion(this.stack.pop(NumericExpression.class)));
+ }
+
@Override
public void exitContainsFunction(ContainsFunctionContext ctx) {
final StringExpression needle = this.stack.pop(StringExpression.class);
@@ -1902,21 +1999,91 @@ public void exitCountDurationsFunction(CountDurationsFunctionContext ctx) {
}
@Override
- public void exitNumberFunction(NumberFunctionContext ctx) {
+ public void exitNumberFromStringFunction(NumberFromStringFunctionContext ctx) {
this.stack.push(this.script.composeToNumberConversion(this.stack.pop(StringExpression.class)));
}
+ @Override
+ public void exitNumberFromBooleanFunction(NumberFromBooleanFunctionContext ctx) {
+ this.stack.push(this.script.composeToNumberConversion(this.stack.pop(BooleanExpression.class)));
+ }
+
@Override
public void exitSumFunction(SumFunctionContext ctx) {
this.stack.push(this.script.composeSumOperation(this.stack.pop(NumericSequenceExpression.class)));
}
+ @Override
+ public void exitMinFunction(MinFunctionContext ctx) {
+ this.stack.push(this.script.composeMinFunction(this.stack.pop(NumericSequenceExpression.class)));
+ }
+
+ @Override
+ public void exitMaxFunction(MaxFunctionContext ctx) {
+ this.stack.push(this.script.composeMaxFunction(this.stack.pop(NumericSequenceExpression.class)));
+ }
+
+ @Override
+ public void exitAverageFunction(AverageFunctionContext ctx) {
+ this.stack.push(this.script.composeAvgFunction(this.stack.pop(NumericSequenceExpression.class)));
+ }
+
@Override
public void exitStringLengthFunction(StringLengthFunctionContext ctx) {
this.stack
.push(this.script.composeStringLengthCalculation(this.stack.pop(StringExpression.class)));
}
+ @Override
+ public void exitYearFromDateFunction(YearFromDateFunctionContext ctx) {
+ this.stack.push(this.script.composeYearFunction(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitMonthFromDateFunction(MonthFromDateFunctionContext ctx) {
+ this.stack.push(this.script.composeMonthFunction(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitDayFromDateFunction(DayFromDateFunctionContext ctx) {
+ this.stack.push(this.script.composeDayFunction(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitHoursFromTimeFunction(HoursFromTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeHoursFunction(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitMinutesFromTimeFunction(MinutesFromTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeMinutesFunction(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitSecondsFromTimeFunction(SecondsFromTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeSecondsFunction(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitAbsoluteFunction(AbsoluteFunctionContext ctx) {
+ this.stack.push(this.script.composeAbsFunction(this.stack.pop(NumericExpression.class)));
+ }
+
+ @Override
+ public void exitRoundFunction(RoundFunctionContext ctx) {
+ this.stack.push(this.script.composeRoundFunction(this.stack.pop(NumericExpression.class)));
+ }
+
+ @Override
+ public void exitRoundDownFunction(RoundDownFunctionContext ctx) {
+ this.stack.push(this.script.composeFloorFunction(this.stack.pop(NumericExpression.class)));
+ }
+
+ @Override
+ public void exitRoundUpFunction(RoundUpFunctionContext ctx) {
+ this.stack.push(this.script.composeCeilingFunction(this.stack.pop(NumericExpression.class)));
+ }
+
// #endregion Numeric functions ---------------------------------------------
// #region String functions -------------------------------------------------
@@ -1935,10 +2102,44 @@ public void exitSubstringFunction(SubstringFunctionContext ctx) {
}
@Override
- public void exitToStringFunction(ToStringFunctionContext ctx) {
+ public void exitSubstringBeforeFunction(SubstringBeforeFunctionContext ctx) {
+ final StringExpression delimiter = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeSubstringBeforeFunction(text, delimiter));
+ }
+
+ @Override
+ public void exitSubstringAfterFunction(SubstringAfterFunctionContext ctx) {
+ final StringExpression delimiter = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeSubstringAfterFunction(text, delimiter));
+ }
+
+ @Override
+ public void exitNumberToStringFunction(NumberToStringFunctionContext ctx) {
this.stack.push(this.script.composeToStringConversion(this.stack.pop(NumericExpression.class)));
}
+ @Override
+ public void exitBooleanToStringFunction(BooleanToStringFunctionContext ctx) {
+ this.stack.push(this.script.composeToStringConversion(this.stack.pop(BooleanExpression.class)));
+ }
+
+ @Override
+ public void exitDateToStringFunction(DateToStringFunctionContext ctx) {
+ this.stack.push(this.script.composeToStringConversion(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitTimeToStringFunction(TimeToStringFunctionContext ctx) {
+ this.stack.push(this.script.composeToStringConversion(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitDurationToStringFunction(DurationToStringFunctionContext ctx) {
+ this.stack.push(this.script.composeToStringConversion(this.stack.pop(DurationExpression.class)));
+ }
+
@Override
public void exitConcatFunction(ConcatFunctionContext ctx) {
if (this.stack.empty() || ctx.stringExpression().isEmpty()) {
@@ -1960,6 +2161,51 @@ public void exitFormatNumberFunction(FormatNumberFunctionContext ctx) {
this.stack.push(this.script.composeNumberFormatting(number, format));
}
+ @Override
+ public void exitFormatShortDateFunction(FormatShortDateFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-short");
+ }
+
+ @Override
+ public void exitFormatShortTimeFunction(FormatShortTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-short");
+ }
+
+ @Override
+ public void exitFormatMediumDateFunction(FormatMediumDateFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-medium");
+ }
+
+ @Override
+ public void exitFormatMediumTimeFunction(FormatMediumTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-medium");
+ }
+
+ @Override
+ public void exitFormatLongDateFunction(FormatLongDateFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-long");
+ }
+
+ @Override
+ public void exitFormatLongTimeFunction(FormatLongTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-long");
+ }
+
+ @Override
+ public void exitFormatShortDateTimeFunction(FormatShortDateTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-short");
+ }
+
+ @Override
+ public void exitFormatMediumDateTimeFunction(FormatMediumDateTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-medium");
+ }
+
+ @Override
+ public void exitFormatLongDateTimeFunction(FormatLongDateTimeFunctionContext ctx) {
+ throw InvalidUsageException.templateOnlyFunction("format-long");
+ }
+
// #region New in EFX-2 -----------------------------------------------------
@Override
@@ -1972,6 +2218,77 @@ public void exitLowerCaseFunction(LowerCaseFunctionContext ctx) {
this.stack.push(this.script.composeToLowerCaseConversion(this.stack.pop(StringExpression.class)));
}
+ @Override
+ public void exitNormalizeSpaceFunction(NormalizeSpaceFunctionContext ctx) {
+ this.stack.push(this.script.composeNormalizeSpaceFunction(this.stack.pop(StringExpression.class)));
+ }
+
+ @Override
+ public void exitTrimFunction(TrimFunctionContext ctx) {
+ this.stack.push(this.script.composeTrimFunction(this.stack.pop(StringExpression.class)));
+ }
+
+ @Override
+ public void exitTrimLeftFunction(TrimLeftFunctionContext ctx) {
+ this.stack.push(this.script.composeTrimLeftFunction(this.stack.pop(StringExpression.class)));
+ }
+
+ @Override
+ public void exitTrimRightFunction(TrimRightFunctionContext ctx) {
+ this.stack.push(this.script.composeTrimRightFunction(this.stack.pop(StringExpression.class)));
+ }
+
+ @Override
+ public void exitPadLeftFunction(PadLeftFunctionContext ctx) {
+ final StringExpression padChar = this.stack.pop(StringExpression.class);
+ final NumericExpression length = this.stack.pop(NumericExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composePadLeftFunction(text, length, padChar));
+ }
+
+ @Override
+ public void exitPadRightFunction(PadRightFunctionContext ctx) {
+ final StringExpression padChar = this.stack.pop(StringExpression.class);
+ final NumericExpression length = this.stack.pop(NumericExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composePadRightFunction(text, length, padChar));
+ }
+
+ @Override
+ public void exitRepeatFunction(RepeatFunctionContext ctx) {
+ final NumericExpression count = this.stack.pop(NumericExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeRepeatFunction(text, count));
+ }
+
+ @Override
+ public void exitReplaceFunction(ReplaceFunctionContext ctx) {
+ final StringExpression replacement = this.stack.pop(StringExpression.class);
+ final StringExpression search = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeReplaceFunction(text, search, replacement));
+ }
+
+ @Override
+ public void exitReplaceRegexFunction(ReplaceRegexFunctionContext ctx) {
+ final StringExpression replacement = this.stack.pop(StringExpression.class);
+ final StringExpression pattern = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeReplaceRegexFunction(text, pattern, replacement));
+ }
+
+ @Override
+ public void exitUrlEncodeFunction(UrlEncodeFunctionContext ctx) {
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeUrlEncodeFunction(text));
+ }
+
+ @Override
+ public void exitCapitalizeFirstFunction(CapitalizeFirstFunctionContext ctx) {
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeCapitalizeFirstFunction(text));
+ }
+
@Override
public void exitStringJoinFunction(StringJoinFunctionContext ctx) {
final StringExpression separator = this.stack.pop(StringExpression.class);
@@ -1981,12 +2298,12 @@ public void exitStringJoinFunction(StringJoinFunctionContext ctx) {
@Override
public void exitPreferredLanguageFunction(PreferredLanguageFunctionContext ctx) {
- this.stack.push(this.script.getPreferredLanguage(this.stack.pop(MultilingualStringPath.class)));
+ throw InvalidUsageException.templateOnlyFunction("preferred-language");
}
@Override
public void exitPreferredLanguageTextFunction(PreferredLanguageTextFunctionContext ctx) {
- this.stack.push(this.script.getTextInPreferredLanguage(this.stack.pop(MultilingualStringPath.class)));
+ throw InvalidUsageException.templateOnlyFunction("preferred-language-text");
}
@Override
@@ -2213,6 +2530,186 @@ private void exitExceptFunction(Class listType
// #endregion Except ---------------------------------------------------------
+ // #region Sort --------------------------------------------------------------
+
+ @Override
+ public void exitStringSortFunction(StringSortFunctionContext ctx) {
+ exitSortFunction(StringSequenceExpression.class);
+ }
+
+ @Override
+ public void exitBooleanSortFunction(BooleanSortFunctionContext ctx) {
+ exitSortFunction(BooleanSequenceExpression.class);
+ }
+
+ @Override
+ public void exitNumericSortFunction(NumericSortFunctionContext ctx) {
+ exitSortFunction(NumericSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDateSortFunction(DateSortFunctionContext ctx) {
+ exitSortFunction(DateSequenceExpression.class);
+ }
+
+ @Override
+ public void exitTimeSortFunction(TimeSortFunctionContext ctx) {
+ exitSortFunction(TimeSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDurationSortFunction(DurationSortFunctionContext ctx) {
+ exitSortFunction(DurationSequenceExpression.class);
+ }
+
+ private void exitSortFunction(Class listType) {
+ final T list = this.stack.pop(listType);
+ this.stack.push(this.script.composeSortFunction(list, listType));
+ }
+
+ // #endregion Sort -----------------------------------------------------------
+
+ // #region Reverse ----------------------------------------------------------
+
+ @Override
+ public void exitStringReverseFunction(StringReverseFunctionContext ctx) {
+ exitReverseFunction(StringSequenceExpression.class);
+ }
+
+ @Override
+ public void exitBooleanReverseFunction(BooleanReverseFunctionContext ctx) {
+ exitReverseFunction(BooleanSequenceExpression.class);
+ }
+
+ @Override
+ public void exitNumericReverseFunction(NumericReverseFunctionContext ctx) {
+ exitReverseFunction(NumericSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDateReverseFunction(DateReverseFunctionContext ctx) {
+ exitReverseFunction(DateSequenceExpression.class);
+ }
+
+ @Override
+ public void exitTimeReverseFunction(TimeReverseFunctionContext ctx) {
+ exitReverseFunction(TimeSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDurationReverseFunction(DurationReverseFunctionContext ctx) {
+ exitReverseFunction(DurationSequenceExpression.class);
+ }
+
+ private void exitReverseFunction(Class listType) {
+ final T list = this.stack.pop(listType);
+ this.stack.push(this.script.composeReverseFunction(list, listType));
+ }
+
+ // #endregion Reverse --------------------------------------------------------
+
+ // #region Subsequence ------------------------------------------------------
+
+ @Override
+ public void exitStringSubsequenceFunction(StringSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, StringSequenceExpression.class);
+ }
+
+ @Override
+ public void exitSplitFunction(SplitFunctionContext ctx) {
+ final StringExpression delimiter = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeSplitFunction(text, delimiter));
+ }
+
+ @Override
+ public void exitBooleanSubsequenceFunction(BooleanSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, BooleanSequenceExpression.class);
+ }
+
+ @Override
+ public void exitNumericSubsequenceFunction(NumericSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, NumericSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDateSubsequenceFunction(DateSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, DateSequenceExpression.class);
+ }
+
+ @Override
+ public void exitTimeSubsequenceFunction(TimeSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, TimeSequenceExpression.class);
+ }
+
+ @Override
+ public void exitDurationSubsequenceFunction(DurationSubsequenceFunctionContext ctx) {
+ exitSubsequenceFunction(ctx.length != null, DurationSequenceExpression.class);
+ }
+
+ private void exitSubsequenceFunction(boolean hasLength,
+ Class listType) {
+ final NumericExpression length =
+ hasLength ? this.stack.pop(NumericExpression.class) : null;
+ final NumericExpression start = this.stack.pop(NumericExpression.class);
+ final T list = this.stack.pop(listType);
+ if (length != null) {
+ this.stack.push(this.script.composeSubsequenceFunction(list, start, length, listType));
+ } else {
+ this.stack.push(this.script.composeSubsequenceFunction(list, start, listType));
+ }
+ }
+
+ // #endregion Subsequence ----------------------------------------------------
+
+ // #region Index-of ----------------------------------------------------------
+
+ @Override
+ public void exitIndexOfStringFunction(IndexOfStringFunctionContext ctx) {
+ exitIndexOfFunction(StringSequenceExpression.class, StringExpression.class);
+ }
+
+ @Override
+ public void exitIndexOfSubstringFunction(IndexOfSubstringFunctionContext ctx) {
+ final StringExpression substring = this.stack.pop(StringExpression.class);
+ final StringExpression text = this.stack.pop(StringExpression.class);
+ this.stack.push(this.script.composeIndexOfSubstringFunction(text, substring));
+ }
+
+ @Override
+ public void exitIndexOfBooleanFunction(IndexOfBooleanFunctionContext ctx) {
+ exitIndexOfFunction(BooleanSequenceExpression.class, BooleanExpression.class);
+ }
+
+ @Override
+ public void exitIndexOfNumericFunction(IndexOfNumericFunctionContext ctx) {
+ exitIndexOfFunction(NumericSequenceExpression.class, NumericExpression.class);
+ }
+
+ @Override
+ public void exitIndexOfDateFunction(IndexOfDateFunctionContext ctx) {
+ exitIndexOfFunction(DateSequenceExpression.class, DateExpression.class);
+ }
+
+ @Override
+ public void exitIndexOfTimeFunction(IndexOfTimeFunctionContext ctx) {
+ exitIndexOfFunction(TimeSequenceExpression.class, TimeExpression.class);
+ }
+
+ @Override
+ public void exitIndexOfDurationFunction(IndexOfDurationFunctionContext ctx) {
+ exitIndexOfFunction(DurationSequenceExpression.class, DurationExpression.class);
+ }
+
+ private void exitIndexOfFunction(
+ Class listType, Class valueType) {
+ final S value = this.stack.pop(valueType);
+ final T list = this.stack.pop(listType);
+ this.stack.push(this.script.composeIndexOfFunction(list, value));
+ }
+
+ // #endregion Index-of -------------------------------------------------------
+
// #endregion Sequence Functions --------------------------------------------
// #region Helpers ----------------------------------------------------------
@@ -2368,7 +2865,7 @@ public void exitSingleExpression(SingleExpressionContext ctx) {
}
@Override
- public void enterPredicate(EfxParser.PredicateContext ctx) {
+ public void enterPredicate(PredicateContext ctx) {
var parent = ctx.getParent();
if (parent instanceof NodeReferenceWithPredicateContext) {
final String nodeId = getNodeId((NodeReferenceWithPredicateContext) parent);
@@ -2382,7 +2879,7 @@ public void enterPredicate(EfxParser.PredicateContext ctx) {
}
@Override
- public void exitPredicate(EfxParser.PredicateContext ctx) {
+ public void exitPredicate(PredicateContext ctx) {
this.efxContext.pop();
}
@@ -2394,14 +2891,14 @@ public void enterAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
}
@Override
- public void exitAbsoluteFieldReference(EfxParser.AbsoluteFieldReferenceContext ctx) {
+ public void exitAbsoluteFieldReference(AbsoluteFieldReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.pop();
}
}
@Override
- public void enterAbsoluteNodeReference(EfxParser.AbsoluteNodeReferenceContext ctx) {
+ public void enterAbsoluteNodeReference(AbsoluteNodeReferenceContext ctx) {
if (ctx.Slash() != null) {
this.efxContext.push(null);
}
@@ -2422,7 +2919,7 @@ public void enterFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext
}
@Override
- public void exitFieldReferenceInOtherNotice(EfxParser.FieldReferenceInOtherNoticeContext ctx) {
+ public void exitFieldReferenceInOtherNotice(FieldReferenceInOtherNoticeContext ctx) {
if (ctx.noticeReference() != null) {
this.efxContext.pop();
}
diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java
index d2a2a318..8f9b0bcc 100644
--- a/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java
+++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2.java
@@ -64,26 +64,7 @@
import eu.europa.ted.efx.model.rules.ValidationStage;
import eu.europa.ted.efx.model.types.FieldTypes;
import eu.europa.ted.efx.model.variables.Variable;
-import eu.europa.ted.efx.sdk2.EfxParser.AnyNoticeTypesContext;
-import eu.europa.ted.efx.sdk2.EfxParser.AsClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.AssertClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ConditionalRuleContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ContextDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ContextVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.FallbackRuleContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ForClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.GlobalVariableDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.InClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.OtherwiseAssertClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.OtherwiseReportClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StageVariableDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ReportClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.RuleSetContext;
-import eu.europa.ted.efx.sdk2.EfxParser.SimpleRuleContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ValidationStageContext;
-import eu.europa.ted.efx.sdk2.EfxParser.VariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.WhenClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.WithClauseContext;
+import eu.europa.ted.efx.sdk2.EfxParser.*;
/**
* EFX Rules translator for SDK version 2.
@@ -219,37 +200,37 @@ private void exitVariableInitializer(String variableName,
@Override
public void exitStringVariableInitializer(
- EfxParser.StringVariableInitializerContext ctx) {
+ StringVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), StringExpression.class);
}
@Override
public void exitBooleanVariableInitializer(
- EfxParser.BooleanVariableInitializerContext ctx) {
+ BooleanVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), BooleanExpression.class);
}
@Override
public void exitNumericVariableInitializer(
- EfxParser.NumericVariableInitializerContext ctx) {
+ NumericVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), NumericExpression.class);
}
@Override
public void exitDateVariableInitializer(
- EfxParser.DateVariableInitializerContext ctx) {
+ DateVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), DateExpression.class);
}
@Override
public void exitTimeVariableInitializer(
- EfxParser.TimeVariableInitializerContext ctx) {
+ TimeVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), TimeExpression.class);
}
@Override
public void exitDurationVariableInitializer(
- EfxParser.DurationVariableInitializerContext ctx) {
+ DurationVariableInitializerContext ctx) {
this.exitVariableInitializer(ctx.variableName.getText(), DurationExpression.class);
}
@@ -269,37 +250,37 @@ private void exitSequenceVariableInitializer(String variableName,
@Override
public void exitStringSequenceVariableInitializer(
- EfxParser.StringSequenceVariableInitializerContext ctx) {
+ StringSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), StringSequenceExpression.class);
}
@Override
public void exitBooleanSequenceVariableInitializer(
- EfxParser.BooleanSequenceVariableInitializerContext ctx) {
+ BooleanSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), BooleanSequenceExpression.class);
}
@Override
public void exitNumericSequenceVariableInitializer(
- EfxParser.NumericSequenceVariableInitializerContext ctx) {
+ NumericSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), NumericSequenceExpression.class);
}
@Override
public void exitDateSequenceVariableInitializer(
- EfxParser.DateSequenceVariableInitializerContext ctx) {
+ DateSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), DateSequenceExpression.class);
}
@Override
public void exitTimeSequenceVariableInitializer(
- EfxParser.TimeSequenceVariableInitializerContext ctx) {
+ TimeSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), TimeSequenceExpression.class);
}
@Override
public void exitDurationSequenceVariableInitializer(
- EfxParser.DurationSequenceVariableInitializerContext ctx) {
+ DurationSequenceVariableInitializerContext ctx) {
this.exitSequenceVariableInitializer(ctx.variableName.getText(), DurationSequenceExpression.class);
}
@@ -699,7 +680,7 @@ public void exitGlobalVariableDeclaration(GlobalVariableDeclarationContext ctx)
*/
@Override
public void exitStageVariableDeclaration(
- EfxParser.StageVariableDeclarationContext ctx) {
+ StageVariableDeclarationContext ctx) {
if (!this.stack.empty()) {
Variable variable = this.stack.pop(Variable.class);
this.stack.declareIdentifier(variable);
@@ -797,7 +778,7 @@ public void exitRuleSet(RuleSetContext ctx) {
*/
@Override
public void exitVariableInitializer(
- EfxParser.VariableInitializerContext ctx) {
+ VariableInitializerContext ctx) {
Variable variable = this.stack.pop(Variable.class);
this.stack.declareIdentifier(variable);
}
@@ -811,37 +792,37 @@ public void exitVariableInitializer(
* These create simple Variable objects with empty expressions just for type tracking.
*/
@Override
- public void exitStringVariableInitializer(EfxParser.StringVariableInitializerContext ctx) {
+ public void exitStringVariableInitializer(StringVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
StringExpression.empty(), StringExpression.empty()));
}
@Override
- public void exitBooleanVariableInitializer(EfxParser.BooleanVariableInitializerContext ctx) {
+ public void exitBooleanVariableInitializer(BooleanVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
BooleanExpression.empty(), BooleanExpression.empty()));
}
@Override
- public void exitNumericVariableInitializer(EfxParser.NumericVariableInitializerContext ctx) {
+ public void exitNumericVariableInitializer(NumericVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
NumericExpression.empty(), NumericExpression.empty()));
}
@Override
- public void exitDateVariableInitializer(EfxParser.DateVariableInitializerContext ctx) {
+ public void exitDateVariableInitializer(DateVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
DateExpression.empty(), DateExpression.empty()));
}
@Override
- public void exitTimeVariableInitializer(EfxParser.TimeVariableInitializerContext ctx) {
+ public void exitTimeVariableInitializer(TimeVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
TimeExpression.empty(), TimeExpression.empty()));
}
@Override
- public void exitDurationVariableInitializer(EfxParser.DurationVariableInitializerContext ctx) {
+ public void exitDurationVariableInitializer(DurationVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
DurationExpression.empty(), DurationExpression.empty()));
}
@@ -849,37 +830,37 @@ public void exitDurationVariableInitializer(EfxParser.DurationVariableInitialize
// Sequence variable initializers for type tracking
@Override
- public void exitStringSequenceVariableInitializer(EfxParser.StringSequenceVariableInitializerContext ctx) {
+ public void exitStringSequenceVariableInitializer(StringSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new StringSequenceExpression(""), new StringSequenceExpression("")));
}
@Override
- public void exitBooleanSequenceVariableInitializer(EfxParser.BooleanSequenceVariableInitializerContext ctx) {
+ public void exitBooleanSequenceVariableInitializer(BooleanSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new BooleanSequenceExpression(""), new BooleanSequenceExpression("")));
}
@Override
- public void exitNumericSequenceVariableInitializer(EfxParser.NumericSequenceVariableInitializerContext ctx) {
+ public void exitNumericSequenceVariableInitializer(NumericSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new NumericSequenceExpression(""), new NumericSequenceExpression("")));
}
@Override
- public void exitDateSequenceVariableInitializer(EfxParser.DateSequenceVariableInitializerContext ctx) {
+ public void exitDateSequenceVariableInitializer(DateSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new DateSequenceExpression(""), new DateSequenceExpression("")));
}
@Override
- public void exitTimeSequenceVariableInitializer(EfxParser.TimeSequenceVariableInitializerContext ctx) {
+ public void exitTimeSequenceVariableInitializer(TimeSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new TimeSequenceExpression(""), new TimeSequenceExpression("")));
}
@Override
- public void exitDurationSequenceVariableInitializer(EfxParser.DurationSequenceVariableInitializerContext ctx) {
+ public void exitDurationSequenceVariableInitializer(DurationSequenceVariableInitializerContext ctx) {
this.stack.push(new Variable(ctx.variableName.getText(),
new DurationSequenceExpression(""), new DurationSequenceExpression("")));
}
diff --git a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
index 6080bb4e..95a3ac0e 100644
--- a/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
+++ b/src/main/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2.java
@@ -90,83 +90,8 @@
import eu.europa.ted.efx.model.variables.Template;
import eu.europa.ted.efx.model.variables.Variable;
import eu.europa.ted.efx.model.variables.Variables;
-import eu.europa.ted.efx.sdk2.EfxParser.AssetIdContext;
-import eu.europa.ted.efx.sdk2.EfxParser.AssetTypeContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.BooleanVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ChooseTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ComputedLabelReferenceContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ContextDeclarationBlockContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ContextDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ContextVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DateVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DictionaryDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DictionaryIndexClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DictionaryKeyClauseContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.DurationVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ExpressionTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.VariableDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.IndentationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.InvokeTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LabelTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LabelTypeContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedExpressionBlockContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedExpressionTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedLabelBlockContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedLabelTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedTextBlockContext;
-import eu.europa.ted.efx.sdk2.EfxParser.LinkedTextTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NavigationSectionContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.NumericVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.SecondaryTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandBtLabelReferenceContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandFieldLabelReferenceContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandFieldValueReferenceFromContextFieldContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandIndirectLabelReferenceContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandIndirectLabelReferenceFromContextFieldContext;
-import eu.europa.ted.efx.sdk2.EfxParser.ShorthandLabelReferenceFromContextContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StandardExpressionBlockContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StandardLabelReferenceContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.StringVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.SummarySectionContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TemplateDefinitionContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TemplateDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TemplateFileContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TemplateLineContext;
-import eu.europa.ted.efx.sdk2.EfxParser.VariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TextTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeSequenceParameterDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeSequenceFunctionDeclarationContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeSequenceVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.TimeVariableInitializerContext;
-import eu.europa.ted.efx.sdk2.EfxParser.WhenDisplayTemplateContext;
-import eu.europa.ted.efx.sdk2.EfxParser.WhenInvokeTemplateContext;
+import eu.europa.ted.efx.model.expressions.scalar.MultilingualStringPath;
+import eu.europa.ted.efx.sdk2.EfxParser.*;
/**
* The EfxTemplateTranslator extends the {@link EfxExpressionTranslatorV2} to provide additional
@@ -910,11 +835,9 @@ public void exitDictionaryKeyClause(DictionaryKeyClauseContext ctx) {
public void exitStandardExpressionBlock(StandardExpressionBlockContext ctx) {
var expression = this.stack.pop(Expression.class);
- // TODO: Review this in EFX-2
-
- // This is a hack to make sure that the date and time expressions are rendered in the correct
- // format. We had to do this because EFX 1 does not support the format-date() and format-time()
- // functions.
+ // Implicit formatting: date and time expressions in template blocks are automatically
+ // formatted using format-short for display.
+ // Users can override by using explicit format-short/format-medium/format-long in the expression.
if (TypedExpression.class.isAssignableFrom(expression.getClass())) {
if (EfxDataType.Date.class.isAssignableFrom(((TypedExpression) expression).getDataType())) {
@@ -926,19 +849,19 @@ public void exitStandardExpressionBlock(StandardExpressionBlockContext ctx) {
this.script.composeIteratorList(
List.of(this.script.composeIteratorExpression(loopVariable.declarationExpression,
new DateSequenceExpression(expression.getScript())))),
- new StringExpression("format-date($item, '[D01]/[M01]/[Y0001]')"),
+ this.script.composeFormatDateShort(new DateExpression(loopVariable.referenceExpression.getScript())),
StringSequenceExpression.class);
} else if (EfxDataType.Time.class.isAssignableFrom(((TypedExpression) expression).getDataType())) {
var loopVariable = new Variable("item",
- this.script.composeVariableDeclaration("item", DateExpression.class), DateExpression.empty(),
- this.script.composeVariableReference("item", DateExpression.class));
+ this.script.composeVariableDeclaration("item", TimeExpression.class), TimeExpression.empty(),
+ this.script.composeVariableReference("item", TimeExpression.class));
expression = this.script.composeForExpression(
this.script.composeIteratorList(
List.of(this.script.composeIteratorExpression(loopVariable.declarationExpression,
new TimeSequenceExpression(expression.getScript())))),
- new StringExpression("format-time($item, '[H01]:[m01] [Z]')"),
+ this.script.composeFormatTimeShort(new TimeExpression(loopVariable.referenceExpression.getScript())),
StringSequenceExpression.class);
}
}
@@ -946,6 +869,84 @@ public void exitStandardExpressionBlock(StandardExpressionBlockContext ctx) {
this.stack.push(expression);
}
+ // #region Formatting functions (template-only) --------------------------------
+
+ @Override
+ public void exitFormatShortDateFunction(FormatShortDateFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatDateShort(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitFormatShortTimeFunction(FormatShortTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatTimeShort(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitFormatShortDateTimeFunction(FormatShortDateTimeFunctionContext ctx) {
+ TimeExpression time = this.stack.pop(TimeExpression.class);
+ DateExpression date = this.stack.pop(DateExpression.class);
+ this.stack.push(this.script.composeStringConcatenation(List.of(
+ this.script.composeFormatDateShort(date),
+ this.script.getStringLiteralFromUnquotedString(" "),
+ this.script.composeFormatTimeShort(time))));
+ }
+
+ @Override
+ public void exitFormatMediumDateFunction(FormatMediumDateFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatDateMedium(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitFormatMediumTimeFunction(FormatMediumTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatTimeMedium(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitFormatMediumDateTimeFunction(FormatMediumDateTimeFunctionContext ctx) {
+ TimeExpression time = this.stack.pop(TimeExpression.class);
+ DateExpression date = this.stack.pop(DateExpression.class);
+ this.stack.push(this.script.composeStringConcatenation(List.of(
+ this.script.composeFormatDateMedium(date),
+ this.script.getStringLiteralFromUnquotedString(" "),
+ this.script.composeFormatTimeMedium(time))));
+ }
+
+ @Override
+ public void exitFormatLongDateFunction(FormatLongDateFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatDateLong(this.stack.pop(DateExpression.class)));
+ }
+
+ @Override
+ public void exitFormatLongTimeFunction(FormatLongTimeFunctionContext ctx) {
+ this.stack.push(this.script.composeFormatTimeLong(this.stack.pop(TimeExpression.class)));
+ }
+
+ @Override
+ public void exitFormatLongDateTimeFunction(FormatLongDateTimeFunctionContext ctx) {
+ TimeExpression time = this.stack.pop(TimeExpression.class);
+ DateExpression date = this.stack.pop(DateExpression.class);
+ this.stack.push(this.script.composeStringConcatenation(List.of(
+ this.script.composeFormatDateLong(date),
+ this.script.getStringLiteralFromUnquotedString(" "),
+ this.script.composeFormatTimeLong(time))));
+ }
+
+ // #endregion Formatting functions ---------------------------------------------
+
+ // #region Preferred language functions ----------------------------------------
+
+ @Override
+ public void exitPreferredLanguageFunction(PreferredLanguageFunctionContext ctx) {
+ this.stack.push(this.script.getPreferredLanguage(this.stack.pop(MultilingualStringPath.class)));
+ }
+
+ @Override
+ public void exitPreferredLanguageTextFunction(PreferredLanguageTextFunctionContext ctx) {
+ this.stack.push(this.script.getTextInPreferredLanguage(this.stack.pop(MultilingualStringPath.class)));
+ }
+
+ // #endregion Preferred language functions -------------------------------------
+
/***
* Handles the $value shorthand syntax which renders the value of the field declared as context in
* the current line of the template.
diff --git a/src/main/java/eu/europa/ted/efx/xpath/XPathScriptGenerator.java b/src/main/java/eu/europa/ted/efx/xpath/XPathScriptGenerator.java
index 8a37d8c0..4ad7741e 100644
--- a/src/main/java/eu/europa/ted/efx/xpath/XPathScriptGenerator.java
+++ b/src/main/java/eu/europa/ted/efx/xpath/XPathScriptGenerator.java
@@ -347,6 +347,11 @@ public BooleanExpression composeExistsCondition(PathExpression reference) {
return new BooleanExpression(reference.getScript());
}
+ @Override
+ public BooleanExpression composeToBooleanConversion(NumericExpression number) {
+ return new BooleanExpression("boolean(" + number.getScript() + ")");
+ }
+
/**
* EFX 1 uniqueness check - kept for backward compatibility.
* EFX 2 uses the typed overloads below.
@@ -441,6 +446,17 @@ public BooleanExpression composeSequenceEqualFunction(SequenceExpression one,
return new BooleanExpression("deep-equal(sort(" + one.getScript() + "), sort(" + two.getScript() + "))");
}
+ @Override
+ public BooleanExpression composeEmptySequenceCondition(SequenceExpression sequence) {
+ return new BooleanExpression("empty(" + sequence.getScript() + ")");
+ }
+
+ @Override
+ public BooleanExpression composeIsDistinctCondition(SequenceExpression sequence) {
+ return new BooleanExpression(
+ "count(" + sequence.getScript() + ") = count(distinct-values(" + sequence.getScript() + "))");
+ }
+
//#endregion Boolean functions ----------------------------------------------
//#region Numeric functions -------------------------------------------------
@@ -455,16 +471,86 @@ public NumericExpression composeToNumberConversion(StringExpression text) {
return new NumericExpression("number(" + text.getScript() + ")");
}
+ @Override
+ public NumericExpression composeToNumberConversion(BooleanExpression bool) {
+ return new NumericExpression("number(" + bool.getScript() + ")");
+ }
+
@Override
public NumericExpression composeSumOperation(NumericSequenceExpression nodeSet) {
return new NumericExpression("sum(" + nodeSet.getScript() + ")");
}
+ @Override
+ public NumericExpression composeMinFunction(NumericSequenceExpression list) {
+ return new NumericExpression("min(" + list.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeMaxFunction(NumericSequenceExpression list) {
+ return new NumericExpression("max(" + list.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeAvgFunction(NumericSequenceExpression list) {
+ return new NumericExpression("avg(" + list.getScript() + ")");
+ }
+
@Override
public NumericExpression composeStringLengthCalculation(StringExpression text) {
return new NumericExpression("string-length(" + text.getScript() + ")");
}
+ @Override
+ public NumericExpression composeYearFunction(DateExpression date) {
+ return new NumericExpression("year-from-date(" + date.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeMonthFunction(DateExpression date) {
+ return new NumericExpression("month-from-date(" + date.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeDayFunction(DateExpression date) {
+ return new NumericExpression("day-from-date(" + date.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeHoursFunction(TimeExpression time) {
+ return new NumericExpression("hours-from-time(" + time.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeMinutesFunction(TimeExpression time) {
+ return new NumericExpression("minutes-from-time(" + time.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeSecondsFunction(TimeExpression time) {
+ return new NumericExpression("seconds-from-time(" + time.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeAbsFunction(NumericExpression number) {
+ return new NumericExpression("abs(" + number.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeRoundFunction(NumericExpression number) {
+ return new NumericExpression("round(" + number.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeFloorFunction(NumericExpression number) {
+ return new NumericExpression("floor(" + number.getScript() + ")");
+ }
+
+ @Override
+ public NumericExpression composeCeilingFunction(NumericExpression number) {
+ return new NumericExpression("ceiling(" + number.getScript() + ")");
+ }
+
@Override
public NumericExpression composeNumericOperation(NumericExpression leftOperand, String operator,
NumericExpression rightOperand) {
@@ -489,10 +575,43 @@ public StringExpression composeSubstringExtraction(StringExpression text,
return new StringExpression("substring(" + text.getScript() + ", " + start.getScript() + ")");
}
+ @Override
+ public StringExpression composeSubstringBeforeFunction(StringExpression text,
+ StringExpression delimiter) {
+ return new StringExpression(
+ "substring-before(" + text.getScript() + ", " + delimiter.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeSubstringAfterFunction(StringExpression text,
+ StringExpression delimiter) {
+ return new StringExpression(
+ "substring-after(" + text.getScript() + ", " + delimiter.getScript() + ")");
+ }
+
@Override
public StringExpression composeToStringConversion(NumericExpression number) {
- String formatString = this.translatorOptions.getDecimalFormat().adaptFormatString("0.##########");
- return new StringExpression("format-number(" + number.getScript() + ", '" + formatString + "')");
+ return new StringExpression("string(" + number.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeToStringConversion(BooleanExpression bool) {
+ return new StringExpression("string(" + bool.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeToStringConversion(DateExpression date) {
+ return new StringExpression("string(" + date.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeToStringConversion(TimeExpression time) {
+ return new StringExpression("string(" + time.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeToStringConversion(DurationExpression duration) {
+ return new StringExpression("string(" + duration.getScript() + ")");
}
@Override
@@ -505,6 +624,91 @@ public StringExpression composeToLowerCaseConversion(StringExpression text) {
return new StringExpression("lower-case(" + text.getScript() + ")");
}
+ @Override
+ public StringExpression composeNormalizeSpaceFunction(StringExpression text) {
+ return new StringExpression("normalize-space(" + text.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeTrimFunction(StringExpression text) {
+ return new StringExpression(
+ "replace(replace(" + text.getScript() + ", '^\\s+', ''), '\\s+$', '')");
+ }
+
+ @Override
+ public StringExpression composeTrimLeftFunction(StringExpression text) {
+ return new StringExpression("replace(" + text.getScript() + ", '^\\s+', '')");
+ }
+
+ @Override
+ public StringExpression composeTrimRightFunction(StringExpression text) {
+ return new StringExpression("replace(" + text.getScript() + ", '\\s+$', '')");
+ }
+
+ @Override
+ public StringExpression composePadLeftFunction(StringExpression text, NumericExpression length,
+ StringExpression padChar) {
+ String len = length.getScript();
+ String ch = padChar.getScript();
+ String padding = "string-join(for $__i in 1 to " + len + " return " + ch + ", '')";
+ return new StringExpression("(for $__s in " + text.getScript()
+ + " return concat(substring(" + padding + ", 1, " + len
+ + " - string-length($__s)), $__s))");
+ }
+
+ @Override
+ public StringExpression composePadRightFunction(StringExpression text, NumericExpression length,
+ StringExpression padChar) {
+ String len = length.getScript();
+ String ch = padChar.getScript();
+ String padding = "string-join(for $__i in 1 to " + len + " return " + ch + ", '')";
+ return new StringExpression("(for $__s in " + text.getScript()
+ + " return concat($__s, substring(" + padding + ", 1, " + len
+ + " - string-length($__s))))");
+ }
+
+ @Override
+ public StringExpression composeRepeatFunction(StringExpression text, NumericExpression count) {
+ return new StringExpression("(for $__s in " + text.getScript()
+ + " return string-join(for $__i in 1 to " + count.getScript()
+ + " return $__s, ''))");
+ }
+
+ @Override
+ public StringExpression composeReplaceFunction(StringExpression text, StringExpression search,
+ StringExpression replacement) {
+ // Escape regex metacharacters in the search string for literal matching
+ String escapedSearch = "replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1')";
+ // Escape replacement-string semantics: \ must become \\, $ must become \$
+ // (order matters: escape backslashes first, then dollars)
+ String escapedReplacement = "replace(replace(" + replacement.getScript()
+ + ", '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')";
+ // Bind text and search to avoid double evaluation; guard against empty search at runtime
+ return new StringExpression("(for $__s in " + search.getScript() + ", $__t in "
+ + text.getScript() + " return if ($__s = '') then $__t else replace($__t, "
+ + escapedSearch + ", " + escapedReplacement + "))");
+ }
+
+ @Override
+ public StringExpression composeReplaceRegexFunction(StringExpression text,
+ StringExpression pattern, StringExpression replacement) {
+ return new StringExpression(
+ "replace(" + text.getScript() + ", " + pattern.getScript() + ", "
+ + replacement.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeUrlEncodeFunction(StringExpression text) {
+ return new StringExpression("encode-for-uri(" + text.getScript() + ")");
+ }
+
+ @Override
+ public StringExpression composeCapitalizeFirstFunction(StringExpression text) {
+ return new StringExpression(
+ "(for $__s in " + text.getScript()
+ + " return concat(upper-case(substring($__s, 1, 1)), substring($__s, 2)))");
+ }
+
@Override
public StringExpression composeStringConcatenation(List list) {
return new StringExpression(
@@ -524,6 +728,38 @@ public StringExpression composeNumberFormatting(NumericExpression number,
return new StringExpression("format-number(" + number.getScript() + ", " + formatString + ")");
}
+ @Override
+ public StringExpression composeFormatDateShort(DateExpression date) {
+ return new StringExpression("format-date(" + date.getScript() + ", '[D01]/[M01]/[Y0001]')");
+ }
+
+ @Override
+ public StringExpression composeFormatDateMedium(DateExpression date) {
+ String lang = this.translatorOptions.getPrimaryLanguage2LetterCode();
+ return new StringExpression("format-date(" + date.getScript() + ", '[D01] [MNn,3-3] [Y0001]', '" + lang + "', (), ())");
+ }
+
+ @Override
+ public StringExpression composeFormatDateLong(DateExpression date) {
+ String lang = this.translatorOptions.getPrimaryLanguage2LetterCode();
+ return new StringExpression("format-date(" + date.getScript() + ", '[D01] [MNn] [Y0001]', '" + lang + "', (), ())");
+ }
+
+ @Override
+ public StringExpression composeFormatTimeShort(TimeExpression time) {
+ return new StringExpression("format-time(" + time.getScript() + ", '[H01]:[m01] [Z]')");
+ }
+
+ @Override
+ public StringExpression composeFormatTimeMedium(TimeExpression time) {
+ return new StringExpression("format-time(" + time.getScript() + ", '[H01]:[m01]:[s01]')");
+ }
+
+ @Override
+ public StringExpression composeFormatTimeLong(TimeExpression time) {
+ return new StringExpression("format-time(" + time.getScript() + ", '[H01]:[m01]:[s01] [Z]')");
+ }
+
@Override
public StringLiteral getStringLiteralFromUnquotedString(String value) {
return new StringLiteral("'" + value + "'");
@@ -632,6 +868,57 @@ public T composeExceptFunction(T listOne, T listT
return Expression.instantiate("distinct-values(for $L1 in " + listOne.getScript() + " return if (every $L2 in " + listTwo.getScript() + " satisfies $L1 != $L2) then $L1 else ())", listType);
}
+ @Override
+ public T composeSortFunction(T list, Class listType) {
+ return Expression.instantiate("sort(" + list.getScript() + ")", listType);
+ }
+
+ @Override
+ public T composeReverseFunction(T list, Class listType) {
+ return Expression.instantiate("reverse(" + list.getScript() + ")", listType);
+ }
+
+ @Override
+ public T composeSubsequenceFunction(T list,
+ NumericExpression start, Class listType) {
+ return Expression.instantiate(
+ "subsequence(" + list.getScript() + ", " + start.getScript() + ")", listType);
+ }
+
+ @Override
+ public T composeSubsequenceFunction(T list,
+ NumericExpression start, NumericExpression length, Class listType) {
+ return Expression.instantiate(
+ "subsequence(" + list.getScript() + ", " + start.getScript() + ", " + length.getScript()
+ + ")",
+ listType);
+ }
+
+ @Override
+ public NumericExpression composeIndexOfFunction(SequenceExpression list,
+ ScalarExpression value) {
+ return new NumericExpression(
+ "index-of(" + list.getScript() + ", " + value.getScript() + ")[1]");
+ }
+
+ @Override
+ public NumericExpression composeIndexOfSubstringFunction(StringExpression text,
+ StringExpression substring) {
+ return new NumericExpression(
+ "(for $__s in " + text.getScript() + ", $__sub in " + substring.getScript()
+ + " return if (contains($__s, $__sub)) then string-length(substring-before($__s, $__sub)) + 1 else 0)");
+ }
+
+ @Override
+ public StringSequenceExpression composeSplitFunction(StringExpression text,
+ StringExpression delimiter) {
+ // XPath's tokenize() uses regex, so we escape regex metacharacters in the delimiter
+ // to ensure literal matching.
+ String escapedDelim =
+ "replace(" + delimiter.getScript() + ", '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1')";
+ return new StringSequenceExpression("tokenize(" + text.getScript() + ", " + escapedDelim + ")");
+ }
+
//#endregion Duration functions ---------------------------------------------
@Override
diff --git a/src/test/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2Test.java b/src/test/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2Test.java
index 3a6acc49..c608ed41 100644
--- a/src/test/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2Test.java
+++ b/src/test/java/eu/europa/ted/efx/sdk2/EfxExpressionTranslatorV2Test.java
@@ -19,6 +19,7 @@
import org.junit.jupiter.api.Test;
import eu.europa.ted.efx.EfxTestsBase;
import eu.europa.ted.efx.exceptions.InvalidIdentifierException;
+import eu.europa.ted.efx.exceptions.InvalidUsageException;
import eu.europa.ted.efx.exceptions.TypeMismatchException;
class EfxExpressionTranslatorV2Test extends EfxTestsBase {
@@ -52,18 +53,6 @@ void testInListCondition() {
"'x' not in ('a', 'b', 'c')");
}
- @Test
- void testEmptinessCondition() {
- testExpressionTranslationWithContext("PathNode/TextField/normalize-space(text()) = ''",
- "ND-Root", "BT-00-Text is empty");
- }
-
- @Test
- void testEmptinessCondition_WithNot() {
- testExpressionTranslationWithContext("PathNode/TextField/normalize-space(text()) != ''",
- "ND-Root", "BT-00-Text is not empty");
- }
-
@Test
void testPresenceCondition() {
testExpressionTranslationWithContext("PathNode/TextField", "ND-Root", "BT-00-Text is present");
@@ -170,22 +159,18 @@ void testLikePatternCondition_WithTextMultilingualField() {
"{ND-Root} ${every text:$lang in BT-00-Text-Multilingual/@languageID satisfies BT-00-Text-Multilingual[BT-00-Text-Multilingual/@languageID == $lang] like '[0-9]*'}");
}
- @Test
- void testPreferredLanguageFunction() {
- testExpressionTranslation("PathNode/TextMultilingualField[./@languageID = efx:preferred-language(.)]/normalize-space(text())",
- "{ND-Root} ${BT-00-Text-Multilingual[BT-00-Text-Multilingual/@languageID == preferred-language(BT-00-Text-Multilingual)]}");
- }
-
- @Test
- void testPreferredLanguageFunction_InPredicate() {
- testExpressionTranslation("efx:preferred-language(PathNode/TextMultilingualField)",
- "{ND-Root} ${preferred-language(BT-00-Text-Multilingual)}");
+ @Test
+ void testPreferredLanguage_ThrowsInExpressionContext() {
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
+ () -> translateExpressionWithContext("ND-Root", "preferred-language(BT-00-Text-Multilingual)"));
+ assertEquals(InvalidUsageException.ErrorCode.TEMPLATE_ONLY_FUNCTION, exception.getErrorCode());
}
- @Test
- void testPreferredLanguageTextFunction() {
- testExpressionTranslation("efx:preferred-language-text(PathNode/TextMultilingualField)",
- "{ND-Root} ${preferred-language-text(BT-00-Text-Multilingual)}");
+ @Test
+ void testPreferredLanguageText_ThrowsInExpressionContext() {
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
+ () -> translateExpressionWithContext("ND-Root", "preferred-language-text(BT-00-Text-Multilingual)"));
+ assertEquals(InvalidUsageException.ErrorCode.TEMPLATE_ONLY_FUNCTION, exception.getErrorCode());
}
@Test
@@ -587,7 +572,7 @@ void testStringsFromStringIteration_UsingLiterals() {
@Test
void testStringsSequenceFromIteration_UsingMultipleIterators() {
testExpressionTranslationWithContext(
- "'a' = (for $x in ('a','b','c'), $y in (1,2), $z in PathNode/IndicatorField return concat($x, format-number($y, '0,##########'), 'text'))",
+ "'a' = (for $x in ('a','b','c'), $y in (1,2), $z in PathNode/IndicatorField return concat($x, string($y), 'text'))",
"ND-Root",
"'a' in (for text:$x in ('a', 'b', 'c'), number:$y in (1, 2), indicator:$z in BT-00-Indicator return concat($x, string($y), 'text'))");
}
@@ -1278,6 +1263,17 @@ void testNotFunction() {
() -> translateExpressionWithContext("BT-00-Text", "not('text')"));
}
+ @Test
+ void testBooleanFromNumberFunction() {
+ testExpressionTranslationWithContext("boolean(0)", "ND-Root", "indicator(0)");
+ }
+
+ @Test
+ void testBooleanFromNumberFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("boolean(PathNode/NumberField/number())", "ND-Root",
+ "indicator(BT-00-Number)");
+ }
+
@Test
void testContainsFunction() {
testExpressionTranslationWithContext(
@@ -1356,9 +1352,10 @@ void testComputedProperty_wasWithheld() {
@Test
void testComputedProperty_wasWithheld_onNonWithholdableField() {
- assertThrows(ParseCancellationException.class,
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
() -> translateExpressionWithContext("BT-00-Text",
"BT-00-Text:wasWithheld"));
+ assertEquals(InvalidUsageException.ErrorCode.FIELD_NOT_WITHHOLDABLE, exception.getErrorCode());
}
@Test
@@ -1376,9 +1373,10 @@ void testComputedProperty_isWithheld() {
@Test
void testComputedProperty_isWithheld_onNonWithholdableField() {
- assertThrows(ParseCancellationException.class,
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
() -> translateExpressionWithContext("BT-00-Text",
"BT-00-Text:isWithheld"));
+ assertEquals(InvalidUsageException.ErrorCode.FIELD_NOT_WITHHOLDABLE, exception.getErrorCode());
}
@Test
@@ -1415,9 +1413,10 @@ void testComputedProperty_isDisclosed() {
@Test
void testComputedProperty_isDisclosed_onNonWithholdableField() {
- assertThrows(ParseCancellationException.class,
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
() -> translateExpressionWithContext("BT-00-Text",
"BT-00-Text:isDisclosed"));
+ assertEquals(InvalidUsageException.ErrorCode.FIELD_NOT_WITHHOLDABLE, exception.getErrorCode());
}
@Test
@@ -1433,9 +1432,10 @@ void testComputedProperty_isMasked() {
@Test
void testComputedProperty_isMasked_onNonWithholdableField() {
- assertThrows(ParseCancellationException.class,
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
() -> translateExpressionWithContext("BT-00-Text",
"BT-00-Text:isMasked"));
+ assertEquals(InvalidUsageException.ErrorCode.FIELD_NOT_WITHHOLDABLE, exception.getErrorCode());
}
@Test
@@ -1451,7 +1451,7 @@ void testComputedProperty_isMasked_numericField() {
@Test
void testComputedProperty_isMasked_repeatingFieldFromContext() {
- assertThrows(ParseCancellationException.class,
+ assertThrows(TypeMismatchException.class,
() -> translateExpressionWithContext("ND-Root",
"BT-00-Text-In-Repeatable-Node:isMasked"));
}
@@ -1513,9 +1513,10 @@ void testMetadataProperty_privacyCode() {
@Test
void testMetadataProperty_privacyCode_onNonWithholdableField() {
- assertThrows(ParseCancellationException.class,
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
() -> translateExpressionWithContext("BT-00-Text",
"BT-00-Text:privacyCode"));
+ assertEquals(InvalidUsageException.ErrorCode.FIELD_NOT_WITHHOLDABLE, exception.getErrorCode());
}
// #endregion: Boolean functions
@@ -1541,6 +1542,17 @@ void testNumberFunction() {
"ND-Root", "number(BT-00-Text)");
}
+ @Test
+ void testNumberFromBooleanFunction() {
+ testExpressionTranslationWithContext("number(true())", "ND-Root", "number(TRUE)");
+ }
+
+ @Test
+ void testNumberFromBooleanFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("number(PathNode/IndicatorField)", "ND-Root",
+ "number(BT-00-Indicator)");
+ }
+
@Test
void testSumFunction_UsingFieldReference() {
testExpressionTranslationWithContext("sum(PathNode/NumberField/number())", "ND-Root",
@@ -1553,6 +1565,30 @@ void testSumFunction_UsingNumericSequenceFromIteration() {
"ND-Root", "sum(for number:$v in BT-00-Number return $v +1)");
}
+ @Test
+ void testMinFunction_UsingFieldReference() {
+ testExpressionTranslationWithContext("min(PathNode/NumberField/number())", "ND-Root",
+ "min(BT-00-Number)");
+ }
+
+ @Test
+ void testMaxFunction_UsingFieldReference() {
+ testExpressionTranslationWithContext("max(PathNode/NumberField/number())", "ND-Root",
+ "max(BT-00-Number)");
+ }
+
+ @Test
+ void testAverageFunction_UsingFieldReference() {
+ testExpressionTranslationWithContext("avg(PathNode/NumberField/number())", "ND-Root",
+ "average(BT-00-Number)");
+ }
+
+ @Test
+ void testAverageFunction_UsingNumericSequenceFromIteration() {
+ testExpressionTranslationWithContext("avg(for $v in PathNode/NumberField/number() return $v + 1)",
+ "ND-Root", "average(for number:$v in BT-00-Number return $v +1)");
+ }
+
@Test
void testStringLengthFunction() {
testExpressionTranslationWithContext(
@@ -1560,328 +1596,818 @@ void testStringLengthFunction() {
"string-length(BT-00-Text)");
}
- // #endregion: Numeric functions
-
- // #region: String functions ------------------------------------------------
+ @Test
+ void testYearFromDateFunction() {
+ testExpressionTranslationWithContext("year-from-date(xs:date('2024-03-15Z'))", "ND-Root",
+ "year(date('2024-03-15Z'))");
+ }
@Test
- void testSubstringFunction() {
+ void testYearFromDateFunction_WithFieldReference() {
testExpressionTranslationWithContext(
- "substring(PathNode/TextField/normalize-space(text()), 1, 3)", "ND-Root",
- "substring(BT-00-Text, 1, 3)");
- testExpressionTranslationWithContext("substring(PathNode/TextField/normalize-space(text()), 4)",
- "ND-Root", "substring(BT-00-Text, 4)");
+ "year-from-date(PathNode/StartDateField/xs:date(text()))", "ND-Root",
+ "year(BT-00-StartDate)");
}
@Test
- void testUpperCaseFunction() {
- testExpressionTranslation(
- "upper-case(PathNode/TextField/normalize-space(text()))",
- "{ND-Root} ${upper-case(BT-00-Text)}");
+ void testMonthFromDateFunction() {
+ testExpressionTranslationWithContext("month-from-date(xs:date('2024-03-15Z'))", "ND-Root",
+ "month(date('2024-03-15Z'))");
}
@Test
- void testLowerCaseFunction() {
- testExpressionTranslation(
- "lower-case(PathNode/TextField/normalize-space(text()))",
- "{ND-Root} ${lower-case(BT-00-Text)}");
+ void testMonthFromDateFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "month-from-date(PathNode/StartDateField/xs:date(text()))", "ND-Root",
+ "month(BT-00-StartDate)");
}
@Test
- void testToStringFunction() {
- testExpressionTranslationWithContext("format-number(123, '0,##########')", "ND-Root",
- "string(123)");
+ void testDayFromDateFunction() {
+ testExpressionTranslationWithContext("day-from-date(xs:date('2024-03-15Z'))", "ND-Root",
+ "day(date('2024-03-15Z'))");
}
@Test
- void testConcatFunction() {
- testExpressionTranslationWithContext("concat('abc', 'def')", "ND-Root", "concat('abc', 'def')");
- };
+ void testDayFromDateFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "day-from-date(PathNode/StartDateField/xs:date(text()))", "ND-Root",
+ "day(BT-00-StartDate)");
+ }
@Test
- void testStringJoinFunction_withLiterals() {
- testExpressionTranslationWithContext("string-join(('abc','def'), ',')", "ND-Root",
- "string-join(('abc', 'def'), ',')");
+ void testHoursFromTimeFunction() {
+ testExpressionTranslationWithContext("hours-from-time(xs:time('14:30:00Z'))", "ND-Root",
+ "hours(time('14:30:00Z'))");
}
@Test
- void testStringJoinFunction_withFieldReference() {
- testExpressionTranslationWithContext("string-join(PathNode/TextField/normalize-space(text()), ',')", "ND-Root",
- "string-join(BT-00-Text, ',')");
+ void testHoursFromTimeFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "hours-from-time(PathNode/StartTimeField/xs:time(text()))", "ND-Root",
+ "hours(BT-00-StartTime)");
}
@Test
- void testFormatNumberFunction() {
- testExpressionTranslationWithContext("format-number(PathNode/NumberField/number(), '# ##0,00')",
- "ND-Root", "format-number(BT-00-Number, '#,##0.00')");
+ void testMinutesFromTimeFunction() {
+ testExpressionTranslationWithContext("minutes-from-time(xs:time('14:30:00Z'))", "ND-Root",
+ "minutes(time('14:30:00Z'))");
}
- // #endregion: String functions
-
- // #region: Date functions --------------------------------------------------
+ @Test
+ void testMinutesFromTimeFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "minutes-from-time(PathNode/StartTimeField/xs:time(text()))", "ND-Root",
+ "minutes(BT-00-StartTime)");
+ }
@Test
- void testDateFromStringFunction() {
- testExpressionTranslationWithContext("xs:date(PathNode/TextField/normalize-space(text()))",
- "ND-Root", "date(BT-00-Text)");
+ void testSecondsFromTimeFunction() {
+ testExpressionTranslationWithContext("seconds-from-time(xs:time('14:30:45Z'))", "ND-Root",
+ "seconds(time('14:30:45Z'))");
}
- // #endregion: Date functions
+ @Test
+ void testSecondsFromTimeFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "seconds-from-time(PathNode/StartTimeField/xs:time(text()))", "ND-Root",
+ "seconds(BT-00-StartTime)");
+ }
- // #region: Time functions --------------------------------------------------
+ @Test
+ void testAbsoluteFunction() {
+ testExpressionTranslationWithContext("abs(-5)", "ND-Root", "absolute(-5)");
+ }
@Test
- void testTimeFromStringFunction() {
- testExpressionTranslationWithContext("xs:time(PathNode/TextField/normalize-space(text()))",
- "ND-Root", "time(BT-00-Text)");
+ void testAbsoluteFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("abs(PathNode/NumberField/number())", "ND-Root",
+ "absolute(BT-00-Number)");
}
- // #endregion: Time functions
+ @Test
+ void testRoundFunction() {
+ testExpressionTranslationWithContext("round(3.7)", "ND-Root", "round(3.7)");
+ }
- // #region Duration functions
@Test
- void testDayTimeDurationFromStringFunction() {
- testExpressionTranslationWithContext("xs:yearMonthDuration(PathNode/TextField/normalize-space(text()))",
- "ND-Root", "year-month-duration(BT-00-Text)");
+ void testRoundFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("round(PathNode/NumberField/number())", "ND-Root",
+ "round(BT-00-Number)");
}
@Test
- void testYearMonthDurationFromStringFunction() {
- testExpressionTranslationWithContext("xs:dayTimeDuration(PathNode/TextField/normalize-space(text()))",
- "ND-Root", "day-time-duration(BT-00-Text)");
+ void testRoundDownFunction() {
+ testExpressionTranslationWithContext("floor(3.7)", "ND-Root", "round-down(3.7)");
}
- // #endregion Duration functions
-
- // #region: Sequence Functions ----------------------------------------------
-
@Test
- void testDistinctValuesFunction_WithStringSequences() {
- testExpressionTranslationWithContext("distinct-values(('one','two','one'))", "ND-Root",
- "distinct-values(('one', 'two', 'one'))");
+ void testRoundDownFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("floor(PathNode/NumberField/number())", "ND-Root",
+ "round-down(BT-00-Number)");
}
@Test
- void testDistinctValuesFunction_WithNumberSequences() {
- testExpressionTranslationWithContext("distinct-values((1,2,3,2,3,4))", "ND-Root",
- "distinct-values((1, 2, 3, 2, 3, 4))");
+ void testRoundUpFunction() {
+ testExpressionTranslationWithContext("ceiling(3.2)", "ND-Root", "round-up(3.2)");
}
@Test
- void testDistinctValuesFunction_WithDateSequences() {
- testExpressionTranslationWithContext(
- "distinct-values((xs:date('2018-01-01Z'),xs:date('2020-01-01Z'),xs:date('2018-01-01Z'),xs:date('2022-01-02Z')))",
- "ND-Root", "distinct-values((2018-01-01Z, 2020-01-01Z, 2018-01-01Z, 2022-01-02Z))");
+ void testRoundUpFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("ceiling(PathNode/NumberField/number())", "ND-Root",
+ "round-up(BT-00-Number)");
}
+ // #endregion: Numeric functions
+
+ // #region: String functions ------------------------------------------------
+
@Test
- void testDistinctValuesFunction_WithTimeSequences() {
+ void testSubstringFunction() {
testExpressionTranslationWithContext(
- "distinct-values((xs:time('12:00:00Z'),xs:time('13:00:00Z'),xs:time('12:00:00Z'),xs:time('14:00:00Z')))",
- "ND-Root", "distinct-values((12:00:00Z, 13:00:00Z, 12:00:00Z, 14:00:00Z))");
+ "substring(PathNode/TextField/normalize-space(text()), 1, 3)", "ND-Root",
+ "substring(BT-00-Text, 1, 3)");
+ testExpressionTranslationWithContext("substring(PathNode/TextField/normalize-space(text()), 4)",
+ "ND-Root", "substring(BT-00-Text, 4)");
}
@Test
- void testDistinctValuesFunction_WithDurationSequences() {
- testExpressionTranslationWithContext("distinct-values((xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')))",
- "ND-Root", "distinct-values((P1W, P2D, P2D, P5D))");
+ void testUpperCaseFunction() {
+ testExpressionTranslation(
+ "upper-case(PathNode/TextField/normalize-space(text()))",
+ "{ND-Root} ${upper-case(BT-00-Text)}");
}
@Test
- void testDistinctValuesFunction_WithBooleanSequences() {
- testExpressionTranslationWithContext("distinct-values((true(),false(),false(),false()))",
- "ND-Root", "distinct-values((TRUE, FALSE, FALSE, NEVER))");
+ void testLowerCaseFunction() {
+ testExpressionTranslation(
+ "lower-case(PathNode/TextField/normalize-space(text()))",
+ "{ND-Root} ${lower-case(BT-00-Text)}");
}
@Test
- void testDistinctValuesFunction_WithFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(PathNode/TextField/normalize-space(text()))", "ND-Root",
- "distinct-values(BT-00-Text)");
+ void testNormalizeSpaceFunction() {
+ testExpressionTranslation(
+ "normalize-space(PathNode/TextField/normalize-space(text()))",
+ "{ND-Root} ${normalize-space(BT-00-Text)}");
}
- // #region: Union
-
@Test
- void testUnionFunction_WithStringSequences() {
- testExpressionTranslationWithContext("distinct-values((('one','two'), ('two','three','four')))",
- "ND-Root", "value-union(('one', 'two'), ('two', 'three', 'four'))");
+ void testNormalizeSpaceFunction_WithLiteral() {
+ testExpressionTranslationWithContext("normalize-space(' hello world ')", "ND-Root",
+ "normalize-space(' hello world ')");
}
@Test
- void testUnionFunction_WithNumberSequences() {
- testExpressionTranslationWithContext("distinct-values(((1,2,3), (2,3,4)))", "ND-Root",
- "value-union((1, 2, 3), (2, 3, 4))");
+ void testTrimFunction() {
+ testExpressionTranslation(
+ "replace(replace(PathNode/TextField/normalize-space(text()), '^\\s+', ''), '\\s+$', '')",
+ "{ND-Root} ${trim(BT-00-Text)}");
}
@Test
- void testUnionFunction_WithDateSequences() {
- testExpressionTranslationWithContext(
- "distinct-values(((xs:date('2018-01-01Z'),xs:date('2020-01-01Z')), (xs:date('2018-01-01Z'),xs:date('2022-01-02Z'))))",
- "ND-Root", "value-union((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ void testTrimFunction_WithLiteral() {
+ testExpressionTranslationWithContext("replace(replace(' hello ', '^\\s+', ''), '\\s+$', '')",
+ "ND-Root", "trim(' hello ')");
}
@Test
- void testUnionFunction_WithTimeSequences() {
- testExpressionTranslationWithContext(
- "distinct-values(((xs:time('12:00:00Z'),xs:time('13:00:00Z')), (xs:time('12:00:00Z'),xs:time('14:00:00Z'))))",
- "ND-Root", "value-union((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ void testTrimLeftFunction() {
+ testExpressionTranslation(
+ "replace(PathNode/TextField/normalize-space(text()), '^\\s+', '')",
+ "{ND-Root} ${trim-left(BT-00-Text)}");
}
@Test
- void testUnionFunction_WithDurationSequences() {
- testExpressionTranslationWithContext("distinct-values(((xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')), (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D'))))",
- "ND-Root", "value-union((P1W, P2D), (P2D, P5D))");
+ void testTrimLeftFunction_WithLiteral() {
+ testExpressionTranslationWithContext("replace(' hello ', '^\\s+', '')", "ND-Root",
+ "trim-left(' hello ')");
}
@Test
- void testUnionFunction_WithBooleanSequences() {
- testExpressionTranslationWithContext("distinct-values(((true(),false()), (false(),false())))",
- "ND-Root", "value-union((TRUE, FALSE), (FALSE, NEVER))");
+ void testTrimRightFunction() {
+ testExpressionTranslation(
+ "replace(PathNode/TextField/normalize-space(text()), '\\s+$', '')",
+ "{ND-Root} ${trim-right(BT-00-Text)}");
}
@Test
- void testUnionFunction_WithFieldReferences() {
- testExpressionTranslationWithContext(
- "distinct-values((PathNode/TextField/normalize-space(text()), PathNode/TextField/normalize-space(text())))", "ND-Root",
- "value-union(BT-00-Text, BT-00-Text)");
+ void testTrimRightFunction_WithLiteral() {
+ testExpressionTranslationWithContext("replace(' hello ', '\\s+$', '')", "ND-Root",
+ "trim-right(' hello ')");
}
@Test
- void testUnionFunction_WithTypeMismatch() {
+ void testTrimFunction_WithRepeatableField_Throws() {
assertThrows(ParseCancellationException.class,
- () -> translateExpressionWithContext("ND-Root", "value-union(BT-00-Text, BT-00-Number)"));
+ () -> translateExpressionWithContext("ND-Root", "trim(BT-00-Repeatable-Text)"));
}
- // #endregion: Union
-
- // #region: Intersect
-
@Test
- void testIntersectFunction_WithStringSequences() {
+ void testPadLeftFunction() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in ('one','two') return if (some $L2 in ('two','three','four') satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
- "value-intersect(('one', 'two'), ('two', 'three', 'four'))");
+ "(for $__s in '42' return concat(substring(string-join(for $__i in 1 to 5 return '0', ''), 1, 5 - string-length($__s)), $__s))",
+ "ND-Root", "pad-left('42', 5, '0')");
}
@Test
- void testIntersectFunction_WithNumberSequences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in (1,2,3) return if (some $L2 in (2,3,4) satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
- "value-intersect((1, 2, 3), (2, 3, 4))");
+ void testPadLeftFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in PathNode/TextField/normalize-space(text()) return concat(substring(string-join(for $__i in 1 to 10 return ' ', ''), 1, 10 - string-length($__s)), $__s))",
+ "{ND-Root} ${pad-left(BT-00-Text, 10, ' ')}");
}
@Test
- void testIntersectFunction_WithDateSequences() {
+ void testPadRightFunction() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (xs:date('2018-01-01Z'),xs:date('2020-01-01Z')) return if (some $L2 in (xs:date('2018-01-01Z'),xs:date('2022-01-02Z')) satisfies $L1 = $L2) then $L1 else ())",
- "ND-Root", "value-intersect((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ "(for $__s in '42' return concat($__s, substring(string-join(for $__i in 1 to 5 return '0', ''), 1, 5 - string-length($__s))))",
+ "ND-Root", "pad-right('42', 5, '0')");
}
@Test
- void testIntersectFunction_WithTimeSequences() {
- testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (xs:time('12:00:00Z'),xs:time('13:00:00Z')) return if (some $L2 in (xs:time('12:00:00Z'),xs:time('14:00:00Z')) satisfies $L1 = $L2) then $L1 else ())",
- "ND-Root", "value-intersect((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ void testPadRightFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in PathNode/TextField/normalize-space(text()) return concat($__s, substring(string-join(for $__i in 1 to 10 return ' ', ''), 1, 10 - string-length($__s))))",
+ "{ND-Root} ${pad-right(BT-00-Text, 10, ' ')}");
}
@Test
- void testIntersectFunction_WithDurationSequences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in (xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')) return if (some $L2 in (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')) satisfies $L1 = $L2) then $L1 else ())",
- "ND-Root", "value-intersect((P1W, P2D), (P2D, P5D))");
+ void testRepeatFunction() {
+ testExpressionTranslationWithContext(
+ "(for $__s in 'abc' return string-join(for $__i in 1 to 3 return $__s, ''))",
+ "ND-Root", "repeat('abc', 3)");
}
@Test
- void testIntersectFunction_WithBooleanSequences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in (true(),false()) return if (some $L2 in (false(),false()) satisfies $L1 = $L2) then $L1 else ())",
- "ND-Root", "value-intersect((TRUE, FALSE), (FALSE, NEVER))");
+ void testRepeatFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in PathNode/TextField/normalize-space(text()) return string-join(for $__i in 1 to 4 return $__s, ''))",
+ "{ND-Root} ${repeat(BT-00-Text, 4)}");
}
@Test
- void testIntersectFunction_WithFieldReferences() {
+ void testReplaceFunction() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in PathNode/TextField/normalize-space(text()) return if (some $L2 in PathNode/TextField/normalize-space(text()) satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
- "value-intersect(BT-00-Text, BT-00-Text)");
+ "(for $__s in 'world', $__t in 'hello world' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('there', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('hello world', 'world', 'there')");
}
@Test
- void testIntersectFunction_WithTypeMismatch() {
- assertThrows(ParseCancellationException.class, () -> translateExpressionWithContext("ND-Root",
- "value-intersect(BT-00-Text, BT-00-Number)"));
+ void testReplaceFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in '-', $__t in PathNode/TextField/normalize-space(text()) return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('_', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "{ND-Root} ${replace(BT-00-Text, '-', '_')}");
}
- // #endregion: Intersect
-
- // #region: Except
-
@Test
- void testExceptFunction_WithStringSequences() {
+ void testReplaceFunction_WithRegexMetacharSearch() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in ('one','two') return if (every $L2 in ('two','three','four') satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
- "value-except(('one', 'two'), ('two', 'three', 'four'))");
+ "(for $__s in '.', $__t in 'a.b.c' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('-', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('a.b.c', '.', '-')");
}
@Test
- void testExceptFunction_WithNumberSequences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in (1,2,3) return if (every $L2 in (2,3,4) satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
- "value-except((1, 2, 3), (2, 3, 4))");
+ void testReplaceFunction_WithDollarInSearch() {
+ testExpressionTranslationWithContext(
+ "(for $__s in '$', $__t in 'a$b' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('X', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('a$b', '$', 'X')");
}
@Test
- void testExceptFunction_WithDateSequences() {
+ void testReplaceFunction_WithDollarInReplacement() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (xs:date('2018-01-01Z'),xs:date('2020-01-01Z')) return if (every $L2 in (xs:date('2018-01-01Z'),xs:date('2022-01-02Z')) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ "(for $__s in 'b', $__t in 'ab' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('$1', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('ab', 'b', '$1')");
}
@Test
- void testExceptFunction_WithTimeSequences() {
+ void testReplaceFunction_WithBackslashInReplacement() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (xs:time('12:00:00Z'),xs:time('13:00:00Z')) return if (every $L2 in (xs:time('12:00:00Z'),xs:time('14:00:00Z')) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ "(for $__s in 'b', $__t in 'ab' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('\\\\', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('ab', 'b', '\\\\')");
}
@Test
- void testExceptFunction_WithDurationSequences() {
+ void testReplaceFunction_WithEmptySearch() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')) return if (every $L2 in (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except((P1W, P2D), (P2D, P5D))");
+ "(for $__s in '', $__t in 'text' return if ($__s = '') then $__t else replace($__t, replace($__s, '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'), replace(replace('x', '\\\\', '\\\\\\\\'), '\\$', '\\\\\\$')))",
+ "ND-Root", "replace('text', '', 'x')");
}
@Test
- void testExceptFunction_WithBooleanSequences() {
+ void testReplaceRegexFunction() {
testExpressionTranslationWithContext(
- "distinct-values(for $L1 in (true(),false()) return if (every $L2 in (false(),false()) satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
- "value-except((TRUE, FALSE), (FALSE, NEVER))");
+ "replace('hello 123 world', '[0-9]+', 'NUM')",
+ "ND-Root", "replace-regex('hello 123 world', '[0-9]+', 'NUM')");
}
@Test
- void testExceptFunction_WithTextFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/TextField/normalize-space(text()) return if (every $L2 in PathNode/TextField/normalize-space(text()) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except(BT-00-Text, BT-00-Text)");
+ void testReplaceRegexFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "replace(PathNode/TextField/normalize-space(text()), '\\s+', ' ')",
+ "{ND-Root} ${replace-regex(BT-00-Text, '\\s+', ' ')}");
}
@Test
- void testExceptFunction_WithNumberFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/IntegerField/number() return if (every $L2 in PathNode/IntegerField/number() satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except(BT-00-Integer, BT-00-Integer)");
+ void testUrlEncodeFunction() {
+ testExpressionTranslationWithContext(
+ "encode-for-uri('hello world')",
+ "ND-Root", "url-encode('hello world')");
}
@Test
- void testExceptFunction_WithBooleanFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/IndicatorField return if (every $L2 in PathNode/IndicatorField satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except(BT-00-Indicator, BT-00-Indicator)");
+ void testUrlEncodeFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "encode-for-uri(PathNode/TextField/normalize-space(text()))",
+ "{ND-Root} ${url-encode(BT-00-Text)}");
}
- @Test
- void testExceptFunction_WithDateFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/StartDateField/xs:date(text()) return if (every $L2 in PathNode/StartDateField/xs:date(text()) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except(BT-00-StartDate, BT-00-StartDate)");
+ @Test
+ void testUrlEncodeFunction_WithRepeatableField_Throws() {
+ assertThrows(ParseCancellationException.class,
+ () -> translateExpression("{ND-Root} ${url-encode(ND-Root)}"));
}
@Test
- void testExceptFunction_WithTimeFieldReferences() {
- testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/StartTimeField/xs:time(text()) return if (every $L2 in PathNode/StartTimeField/xs:time(text()) satisfies $L1 != $L2) then $L1 else ())",
- "ND-Root", "value-except(BT-00-StartTime, BT-00-StartTime)");
+ void testCapitalizeFirstFunction() {
+ testExpressionTranslationWithContext(
+ "(for $__s in 'hello' return concat(upper-case(substring($__s, 1, 1)), substring($__s, 2)))",
+ "ND-Root", "capitalize-first('hello')");
+ }
+
+ @Test
+ void testCapitalizeFirstFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in PathNode/TextField/normalize-space(text()) return concat(upper-case(substring($__s, 1, 1)), substring($__s, 2)))",
+ "{ND-Root} ${capitalize-first(BT-00-Text)}");
+ }
+
+ @Test
+ void testCapitalizeFirstFunction_WithRepeatableField_Throws() {
+ assertThrows(ParseCancellationException.class,
+ () -> translateExpression("{ND-Root} ${capitalize-first(ND-Root)}"));
+ }
+
+ @Test
+ void testSplitFunction() {
+ testExpressionTranslationWithContext(
+ "tokenize('a,b,c', replace(',', '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'))",
+ "ND-Root", "split('a,b,c', ',')");
+ }
+
+ @Test
+ void testSplitFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "tokenize(PathNode/TextField/normalize-space(text()), replace(';', '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'))",
+ "{ND-Root} ${split(BT-00-Text, ';')}");
+ }
+
+ @Test
+ void testSplitFunction_WithRegexMetacharDelimiter() {
+ testExpressionTranslationWithContext(
+ "tokenize('a.b.c', replace('.', '([.\\\\?*+{}\\[\\]()^$|])', '\\\\$1'))",
+ "ND-Root", "split('a.b.c', '.')");
+ }
+
+ @Test
+ void testSubstringBeforeFunction() {
+ testExpressionTranslationWithContext("substring-before('hello-world', '-')", "ND-Root",
+ "substring-before('hello-world', '-')");
+ }
+
+ @Test
+ void testSubstringBeforeFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "substring-before(PathNode/TextField/normalize-space(text()), '-')",
+ "{ND-Root} ${substring-before(BT-00-Text, '-')}");
+ }
+
+ @Test
+ void testSubstringAfterFunction() {
+ testExpressionTranslationWithContext("substring-after('hello-world', '-')", "ND-Root",
+ "substring-after('hello-world', '-')");
+ }
+
+ @Test
+ void testSubstringAfterFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "substring-after(PathNode/TextField/normalize-space(text()), '-')",
+ "{ND-Root} ${substring-after(BT-00-Text, '-')}");
+ }
+
+ @Test
+ void testIndexOfSubstringFunction() {
+ testExpressionTranslationWithContext(
+ "(for $__s in 'hello world', $__sub in 'world' return if (contains($__s, $__sub)) then string-length(substring-before($__s, $__sub)) + 1 else 0)",
+ "ND-Root", "index-of-substring('hello world', 'world')");
+ }
+
+ @Test
+ void testIndexOfSubstringFunction_WithFieldReference() {
+ testExpressionTranslation(
+ "(for $__s in PathNode/TextField/normalize-space(text()), $__sub in '-' return if (contains($__s, $__sub)) then string-length(substring-before($__s, $__sub)) + 1 else 0)",
+ "{ND-Root} ${index-of-substring(BT-00-Text, '-')}");
+ }
+
+ @Test
+ void testNumberToStringFunction() {
+ testExpressionTranslationWithContext("string(123)", "ND-Root", "string(123)");
+ }
+
+ @Test
+ void testNumberToStringFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("string(PathNode/NumberField/number())", "ND-Root",
+ "string(BT-00-Number)");
+ }
+
+ @Test
+ void testBooleanToStringFunction() {
+ testExpressionTranslationWithContext("string(true())", "ND-Root", "string(TRUE)");
+ }
+
+ @Test
+ void testBooleanToStringFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("string(PathNode/IndicatorField)", "ND-Root",
+ "string(BT-00-Indicator)");
+ }
+
+ @Test
+ void testDateToStringFunction() {
+ testExpressionTranslationWithContext("string(xs:date('2024-01-15Z'))", "ND-Root",
+ "string(2024-01-15Z)");
+ }
+
+ @Test
+ void testDateToStringFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("string(PathNode/StartDateField/xs:date(text()))", "ND-Root",
+ "string(BT-00-StartDate)");
+ }
+
+ @Test
+ void testTimeToStringFunction() {
+ testExpressionTranslationWithContext("string(xs:time('14:30:00Z'))", "ND-Root",
+ "string(14:30:00Z)");
+ }
+
+ @Test
+ void testTimeToStringFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("string(PathNode/StartTimeField/xs:time(text()))", "ND-Root",
+ "string(BT-00-StartTime)");
+ }
+
+ @Test
+ void testDurationToStringFunction() {
+ testExpressionTranslationWithContext("string(xs:dayTimeDuration('P30D'))", "ND-Root",
+ "string(P30D)");
+ }
+
+ @Test
+ void testDurationToStringFunction_WithFieldReference() {
+ testExpressionTranslationWithContext(
+ "string((for $F in PathNode/MeasureField return (if ($F/@unitCode='WEEK') then xs:dayTimeDuration(concat('P', $F/number() * 7, 'D')) else if ($F/@unitCode='DAY') then xs:dayTimeDuration(concat('P', $F/number(), 'D')) else if ($F/@unitCode='YEAR') then xs:yearMonthDuration(concat('P', $F/number(), 'Y')) else if ($F/@unitCode='MONTH') then xs:yearMonthDuration(concat('P', $F/number(), 'M')) else ())))",
+ "ND-Root", "string(BT-00-Measure)");
+ }
+
+ // text() variants - verify that 'text' keyword works as alias for 'string' conversion
+ @Test
+ void testTextFromNumberFunction() {
+ testExpressionTranslationWithContext("string(123)", "ND-Root", "text(123)");
+ }
+
+ @Test
+ void testTextFromBooleanFunction() {
+ testExpressionTranslationWithContext("string(true())", "ND-Root", "text(TRUE)");
+ }
+
+ @Test
+ void testTextFromDateFunction() {
+ testExpressionTranslationWithContext("string(xs:date('2024-01-15Z'))", "ND-Root",
+ "text(2024-01-15Z)");
+ }
+
+ @Test
+ void testTextFromTimeFunction() {
+ testExpressionTranslationWithContext("string(xs:time('14:30:00Z'))", "ND-Root",
+ "text(14:30:00Z)");
+ }
+
+ @Test
+ void testTextFromDurationFunction() {
+ testExpressionTranslationWithContext("string(xs:dayTimeDuration('P30D'))", "ND-Root",
+ "text(P30D)");
+ }
+
+ @Test
+ void testConcatFunction() {
+ testExpressionTranslationWithContext("concat('abc', 'def')", "ND-Root", "concat('abc', 'def')");
+ };
+
+ @Test
+ void testStringJoinFunction_withLiterals() {
+ testExpressionTranslationWithContext("string-join(('abc','def'), ',')", "ND-Root",
+ "string-join(('abc', 'def'), ',')");
+ }
+
+ @Test
+ void testStringJoinFunction_withFieldReference() {
+ testExpressionTranslationWithContext("string-join(PathNode/TextField/normalize-space(text()), ',')", "ND-Root",
+ "string-join(BT-00-Text, ',')");
+ }
+
+ @Test
+ void testFormatNumberFunction() {
+ testExpressionTranslationWithContext("format-number(PathNode/NumberField/number(), '# ##0,00')",
+ "ND-Root", "format-number(BT-00-Number, '#,##0.00')");
+ }
+
+ @Test
+ void testFormatShort_ThrowsInExpressionContext() {
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
+ () -> translateExpressionWithContext("ND-Root", "format-short(date('2026-02-15'))"));
+ assertEquals(InvalidUsageException.ErrorCode.TEMPLATE_ONLY_FUNCTION, exception.getErrorCode());
+ }
+
+ @Test
+ void testFormatMedium_ThrowsInExpressionContext() {
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
+ () -> translateExpressionWithContext("ND-Root", "format-medium(date('2026-02-15'))"));
+ assertEquals(InvalidUsageException.ErrorCode.TEMPLATE_ONLY_FUNCTION, exception.getErrorCode());
+ }
+
+ @Test
+ void testFormatLong_ThrowsInExpressionContext() {
+ InvalidUsageException exception = assertThrows(InvalidUsageException.class,
+ () -> translateExpressionWithContext("ND-Root", "format-long(date('2026-02-15'))"));
+ assertEquals(InvalidUsageException.ErrorCode.TEMPLATE_ONLY_FUNCTION, exception.getErrorCode());
+ }
+
+ // #endregion: String functions
+
+ // #region: Date functions --------------------------------------------------
+
+ @Test
+ void testDateFromStringFunction() {
+ testExpressionTranslationWithContext("xs:date(PathNode/TextField/normalize-space(text()))",
+ "ND-Root", "date(BT-00-Text)");
+ }
+
+ // #endregion: Date functions
+
+ // #region: Time functions --------------------------------------------------
+
+ @Test
+ void testTimeFromStringFunction() {
+ testExpressionTranslationWithContext("xs:time(PathNode/TextField/normalize-space(text()))",
+ "ND-Root", "time(BT-00-Text)");
+ }
+
+ // #endregion: Time functions
+
+ // #region Duration functions
+
+ @Test
+ void testDayTimeDurationFromStringFunction() {
+ testExpressionTranslationWithContext("xs:yearMonthDuration(PathNode/TextField/normalize-space(text()))",
+ "ND-Root", "year-month-duration(BT-00-Text)");
+ }
+
+ @Test
+ void testYearMonthDurationFromStringFunction() {
+ testExpressionTranslationWithContext("xs:dayTimeDuration(PathNode/TextField/normalize-space(text()))",
+ "ND-Root", "day-time-duration(BT-00-Text)");
+ }
+
+ // #endregion Duration functions
+
+ // #region: Sequence Functions ----------------------------------------------
+
+ @Test
+ void testDistinctValuesFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("distinct-values(('one','two','one'))", "ND-Root",
+ "distinct-values(('one', 'two', 'one'))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("distinct-values((1,2,3,2,3,4))", "ND-Root",
+ "distinct-values((1, 2, 3, 2, 3, 4))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values((xs:date('2018-01-01Z'),xs:date('2020-01-01Z'),xs:date('2018-01-01Z'),xs:date('2022-01-02Z')))",
+ "ND-Root", "distinct-values((2018-01-01Z, 2020-01-01Z, 2018-01-01Z, 2022-01-02Z))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values((xs:time('12:00:00Z'),xs:time('13:00:00Z'),xs:time('12:00:00Z'),xs:time('14:00:00Z')))",
+ "ND-Root", "distinct-values((12:00:00Z, 13:00:00Z, 12:00:00Z, 14:00:00Z))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext("distinct-values((xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')))",
+ "ND-Root", "distinct-values((P1W, P2D, P2D, P5D))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("distinct-values((true(),false(),false(),false()))",
+ "ND-Root", "distinct-values((TRUE, FALSE, FALSE, NEVER))");
+ }
+
+ @Test
+ void testDistinctValuesFunction_WithFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(PathNode/TextField/normalize-space(text()))", "ND-Root",
+ "distinct-values(BT-00-Text)");
+ }
+
+ // #region: Union
+
+ @Test
+ void testUnionFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("distinct-values((('one','two'), ('two','three','four')))",
+ "ND-Root", "value-union(('one', 'two'), ('two', 'three', 'four'))");
+ }
+
+ @Test
+ void testUnionFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("distinct-values(((1,2,3), (2,3,4)))", "ND-Root",
+ "value-union((1, 2, 3), (2, 3, 4))");
+ }
+
+ @Test
+ void testUnionFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(((xs:date('2018-01-01Z'),xs:date('2020-01-01Z')), (xs:date('2018-01-01Z'),xs:date('2022-01-02Z'))))",
+ "ND-Root", "value-union((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ }
+
+ @Test
+ void testUnionFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(((xs:time('12:00:00Z'),xs:time('13:00:00Z')), (xs:time('12:00:00Z'),xs:time('14:00:00Z'))))",
+ "ND-Root", "value-union((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ }
+
+ @Test
+ void testUnionFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext("distinct-values(((xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')), (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D'))))",
+ "ND-Root", "value-union((P1W, P2D), (P2D, P5D))");
+ }
+
+ @Test
+ void testUnionFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("distinct-values(((true(),false()), (false(),false())))",
+ "ND-Root", "value-union((TRUE, FALSE), (FALSE, NEVER))");
+ }
+
+ @Test
+ void testUnionFunction_WithFieldReferences() {
+ testExpressionTranslationWithContext(
+ "distinct-values((PathNode/TextField/normalize-space(text()), PathNode/TextField/normalize-space(text())))", "ND-Root",
+ "value-union(BT-00-Text, BT-00-Text)");
+ }
+
+ @Test
+ void testUnionFunction_WithTypeMismatch() {
+ assertThrows(ParseCancellationException.class,
+ () -> translateExpressionWithContext("ND-Root", "value-union(BT-00-Text, BT-00-Number)"));
+ }
+
+ // #endregion: Union
+
+ // #region: Intersect
+
+ @Test
+ void testIntersectFunction_WithStringSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in ('one','two') return if (some $L2 in ('two','three','four') satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
+ "value-intersect(('one', 'two'), ('two', 'three', 'four'))");
+ }
+
+ @Test
+ void testIntersectFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in (1,2,3) return if (some $L2 in (2,3,4) satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
+ "value-intersect((1, 2, 3), (2, 3, 4))");
+ }
+
+ @Test
+ void testIntersectFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (xs:date('2018-01-01Z'),xs:date('2020-01-01Z')) return if (some $L2 in (xs:date('2018-01-01Z'),xs:date('2022-01-02Z')) satisfies $L1 = $L2) then $L1 else ())",
+ "ND-Root", "value-intersect((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ }
+
+ @Test
+ void testIntersectFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (xs:time('12:00:00Z'),xs:time('13:00:00Z')) return if (some $L2 in (xs:time('12:00:00Z'),xs:time('14:00:00Z')) satisfies $L1 = $L2) then $L1 else ())",
+ "ND-Root", "value-intersect((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ }
+
+ @Test
+ void testIntersectFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in (xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')) return if (some $L2 in (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')) satisfies $L1 = $L2) then $L1 else ())",
+ "ND-Root", "value-intersect((P1W, P2D), (P2D, P5D))");
+ }
+
+ @Test
+ void testIntersectFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in (true(),false()) return if (some $L2 in (false(),false()) satisfies $L1 = $L2) then $L1 else ())",
+ "ND-Root", "value-intersect((TRUE, FALSE), (FALSE, NEVER))");
+ }
+
+ @Test
+ void testIntersectFunction_WithFieldReferences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in PathNode/TextField/normalize-space(text()) return if (some $L2 in PathNode/TextField/normalize-space(text()) satisfies $L1 = $L2) then $L1 else ())", "ND-Root",
+ "value-intersect(BT-00-Text, BT-00-Text)");
+ }
+
+ @Test
+ void testIntersectFunction_WithTypeMismatch() {
+ assertThrows(ParseCancellationException.class, () -> translateExpressionWithContext("ND-Root",
+ "value-intersect(BT-00-Text, BT-00-Number)"));
+ }
+
+ // #endregion: Intersect
+
+ // #region: Except
+
+ @Test
+ void testExceptFunction_WithStringSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in ('one','two') return if (every $L2 in ('two','three','four') satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
+ "value-except(('one', 'two'), ('two', 'three', 'four'))");
+ }
+
+ @Test
+ void testExceptFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in (1,2,3) return if (every $L2 in (2,3,4) satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
+ "value-except((1, 2, 3), (2, 3, 4))");
+ }
+
+ @Test
+ void testExceptFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (xs:date('2018-01-01Z'),xs:date('2020-01-01Z')) return if (every $L2 in (xs:date('2018-01-01Z'),xs:date('2022-01-02Z')) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except((2018-01-01Z, 2020-01-01Z), (2018-01-01Z, 2022-01-02Z))");
+ }
+
+ @Test
+ void testExceptFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (xs:time('12:00:00Z'),xs:time('13:00:00Z')) return if (every $L2 in (xs:time('12:00:00Z'),xs:time('14:00:00Z')) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except((12:00:00Z, 13:00:00Z), (12:00:00Z, 14:00:00Z))");
+ }
+
+ @Test
+ void testExceptFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (xs:dayTimeDuration('P7D'),xs:dayTimeDuration('P2D')) return if (every $L2 in (xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except((P1W, P2D), (P2D, P5D))");
+ }
+
+ @Test
+ void testExceptFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext(
+ "distinct-values(for $L1 in (true(),false()) return if (every $L2 in (false(),false()) satisfies $L1 != $L2) then $L1 else ())", "ND-Root",
+ "value-except((TRUE, FALSE), (FALSE, NEVER))");
+ }
+
+ @Test
+ void testExceptFunction_WithTextFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/TextField/normalize-space(text()) return if (every $L2 in PathNode/TextField/normalize-space(text()) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except(BT-00-Text, BT-00-Text)");
+ }
+
+ @Test
+ void testExceptFunction_WithNumberFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/IntegerField/number() return if (every $L2 in PathNode/IntegerField/number() satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except(BT-00-Integer, BT-00-Integer)");
+ }
+
+ @Test
+ void testExceptFunction_WithBooleanFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/IndicatorField return if (every $L2 in PathNode/IndicatorField satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except(BT-00-Indicator, BT-00-Indicator)");
+ }
+
+ @Test
+ void testExceptFunction_WithDateFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/StartDateField/xs:date(text()) return if (every $L2 in PathNode/StartDateField/xs:date(text()) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except(BT-00-StartDate, BT-00-StartDate)");
+ }
+
+ @Test
+ void testExceptFunction_WithTimeFieldReferences() {
+ testExpressionTranslationWithContext("distinct-values(for $L1 in PathNode/StartTimeField/xs:time(text()) return if (every $L2 in PathNode/StartTimeField/xs:time(text()) satisfies $L1 != $L2) then $L1 else ())",
+ "ND-Root", "value-except(BT-00-StartTime, BT-00-StartTime)");
}
@Test
@@ -1898,6 +2424,209 @@ void testExceptFunction_WithTypeMismatch() {
// #endregion: Except
+ // #region: Sort
+
+ @Test
+ void testSortFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("sort(('banana','apple','cherry'))", "ND-Root",
+ "sort(('banana', 'apple', 'cherry'))");
+ }
+
+ @Test
+ void testSortFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("sort((3,1,2))", "ND-Root",
+ "sort((3, 1, 2))");
+ }
+
+ @Test
+ void testSortFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "sort((xs:date('2022-01-01Z'),xs:date('2018-01-01Z'),xs:date('2020-01-01Z')))",
+ "ND-Root", "sort((2022-01-01Z, 2018-01-01Z, 2020-01-01Z))");
+ }
+
+ @Test
+ void testSortFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "sort((xs:time('14:00:00Z'),xs:time('12:00:00Z'),xs:time('13:00:00Z')))",
+ "ND-Root", "sort((14:00:00Z, 12:00:00Z, 13:00:00Z))");
+ }
+
+ @Test
+ void testSortFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext("sort((xs:dayTimeDuration('P5D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P7D')))",
+ "ND-Root", "sort((P5D, P2D, P1W))");
+ }
+
+ @Test
+ void testSortFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("sort((true(),false(),true()))",
+ "ND-Root", "sort((TRUE, FALSE, TRUE))");
+ }
+
+ @Test
+ void testSortFunction_WithFieldReferences() {
+ testExpressionTranslationWithContext("sort(PathNode/TextField/normalize-space(text()))", "ND-Root",
+ "sort(BT-00-Text)");
+ }
+
+ @Test
+ void testSortFunction_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "sort(PathNode/RepeatableTextField/normalize-space(text()))", "ND-Root",
+ "sort(BT-00-Repeatable-Text)");
+ }
+
+ // #endregion: Sort
+
+ // #region: Reverse
+
+ @Test
+ void testReverseFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("reverse(('banana','apple','cherry'))", "ND-Root",
+ "reverse(('banana', 'apple', 'cherry'))");
+ }
+
+ @Test
+ void testReverseFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("reverse((3,1,2))", "ND-Root",
+ "reverse((3, 1, 2))");
+ }
+
+ @Test
+ void testReverseFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "reverse((xs:date('2022-01-01Z'),xs:date('2018-01-01Z'),xs:date('2020-01-01Z')))",
+ "ND-Root", "reverse((2022-01-01Z, 2018-01-01Z, 2020-01-01Z))");
+ }
+
+ @Test
+ void testReverseFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "reverse((xs:time('14:00:00Z'),xs:time('12:00:00Z'),xs:time('13:00:00Z')))",
+ "ND-Root", "reverse((14:00:00Z, 12:00:00Z, 13:00:00Z))");
+ }
+
+ @Test
+ void testReverseFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext("reverse((xs:dayTimeDuration('P5D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P7D')))",
+ "ND-Root", "reverse((P5D, P2D, P1W))");
+ }
+
+ @Test
+ void testReverseFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("reverse((true(),false(),true()))",
+ "ND-Root", "reverse((TRUE, FALSE, TRUE))");
+ }
+
+ @Test
+ void testReverseFunction_WithFieldReferences() {
+ testExpressionTranslationWithContext("reverse(PathNode/TextField/normalize-space(text()))", "ND-Root",
+ "reverse(BT-00-Text)");
+ }
+
+ @Test
+ void testReverseFunction_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "reverse(PathNode/RepeatableTextField/normalize-space(text()))", "ND-Root",
+ "reverse(BT-00-Repeatable-Text)");
+ }
+
+ // #endregion: Reverse
+
+ // #region: Subsequence
+
+ @Test
+ void testSubsequenceFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("subsequence(('a','b','c','d'), 2)", "ND-Root",
+ "subsequence(('a', 'b', 'c', 'd'), 2)");
+ }
+
+ @Test
+ void testSubsequenceFunction_WithStringSequences_AndLength() {
+ testExpressionTranslationWithContext("subsequence(('a','b','c','d'), 2, 2)", "ND-Root",
+ "subsequence(('a', 'b', 'c', 'd'), 2, 2)");
+ }
+
+ @Test
+ void testSubsequenceFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("subsequence((10,20,30,40), 2, 2)", "ND-Root",
+ "subsequence((10, 20, 30, 40), 2, 2)");
+ }
+
+ @Test
+ void testSubsequenceFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "subsequence((xs:date('2022-01-01Z'),xs:date('2023-01-01Z'),xs:date('2024-01-01Z')), 1, 2)",
+ "ND-Root", "subsequence((2022-01-01Z, 2023-01-01Z, 2024-01-01Z), 1, 2)");
+ }
+
+ @Test
+ void testSubsequenceFunction_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "subsequence(PathNode/RepeatableTextField/normalize-space(text()), 2)", "ND-Root",
+ "subsequence(BT-00-Repeatable-Text, 2)");
+ }
+
+ @Test
+ void testSubsequenceFunction_WithRepeatableFieldReference_AndLength() {
+ testExpressionTranslationWithContext(
+ "subsequence(PathNode/RepeatableTextField/normalize-space(text()), 1, 3)", "ND-Root",
+ "subsequence(BT-00-Repeatable-Text, 1, 3)");
+ }
+
+ // #endregion: Subsequence
+
+ // #region: Index-of
+
+ @Test
+ void testIndexOfFunction_WithStringSequences() {
+ testExpressionTranslationWithContext("index-of(('a','b','c','b'), 'b')[1]", "ND-Root",
+ "index-of(('a', 'b', 'c', 'b'), 'b')");
+ }
+
+ @Test
+ void testIndexOfFunction_WithNumberSequences() {
+ testExpressionTranslationWithContext("index-of((10,20,30,20), 20)[1]", "ND-Root",
+ "index-of((10, 20, 30, 20), 20)");
+ }
+
+ @Test
+ void testIndexOfFunction_WithDateSequences() {
+ testExpressionTranslationWithContext(
+ "index-of((xs:date('2022-01-01Z'),xs:date('2023-01-01Z'),xs:date('2022-01-01Z')), xs:date('2022-01-01Z'))[1]",
+ "ND-Root", "index-of((2022-01-01Z, 2023-01-01Z, 2022-01-01Z), 2022-01-01Z)");
+ }
+
+ @Test
+ void testIndexOfFunction_WithBooleanSequences() {
+ testExpressionTranslationWithContext("index-of((true(),false(),true()), true())[1]",
+ "ND-Root", "index-of((TRUE, FALSE, TRUE), TRUE)");
+ }
+
+ @Test
+ void testIndexOfFunction_WithTimeSequences() {
+ testExpressionTranslationWithContext(
+ "index-of((xs:time('14:00:00Z'),xs:time('12:00:00Z'),xs:time('14:00:00Z')), xs:time('14:00:00Z'))[1]",
+ "ND-Root", "index-of((14:00:00Z, 12:00:00Z, 14:00:00Z), 14:00:00Z)");
+ }
+
+ @Test
+ void testIndexOfFunction_WithDurationSequences() {
+ testExpressionTranslationWithContext(
+ "index-of((xs:dayTimeDuration('P5D'),xs:dayTimeDuration('P2D'),xs:dayTimeDuration('P5D')), xs:dayTimeDuration('P5D'))[1]",
+ "ND-Root", "index-of((P5D, P2D, P5D), P5D)");
+ }
+
+ @Test
+ void testIndexOfFunction_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "index-of(PathNode/RepeatableTextField/normalize-space(text()), 'hello')[1]", "ND-Root",
+ "index-of(BT-00-Repeatable-Text, 'hello')");
+ }
+
+ // #endregion: Index-of
+
// #region: Compare sequences
@Test
@@ -1948,6 +2677,173 @@ void testSequenceEqualFunction_WithFieldReferences() {
"sequence-equal(BT-00-Text, BT-00-Text)");
}
+ // #endregion: Compare sequences
+
+ // #region: Sequence emptiness
+
+ @Test
+ void testSequenceEmptiness_WithNonRepeatableField() {
+ // Field references always go through sequence emptiness, regardless of repeatability.
+ testExpressionTranslationWithContext("empty(PathNode/TextField/normalize-space(text()))",
+ "ND-Root", "BT-00-Text is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithStringSequence() {
+ testExpressionTranslationWithContext("empty(('a','b','c'))", "ND-Root",
+ "('a', 'b', 'c') is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithStringSequence_Negated() {
+ testExpressionTranslationWithContext("not(empty(('a','b','c')))", "ND-Root",
+ "('a', 'b', 'c') is not empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithNumericSequence() {
+ testExpressionTranslationWithContext("empty((1,2,3))", "ND-Root",
+ "(1, 2, 3) is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithBooleanSequence() {
+ testExpressionTranslationWithContext("empty((true(),false()))", "ND-Root",
+ "(TRUE, FALSE) is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithDateSequence() {
+ testExpressionTranslationWithContext(
+ "empty((xs:date('2024-01-01Z'),xs:date('2024-12-31Z')))", "ND-Root",
+ "(2024-01-01Z, 2024-12-31Z) is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithTimeSequence() {
+ testExpressionTranslationWithContext(
+ "empty((xs:time('12:00:00Z'),xs:time('13:00:00Z')))", "ND-Root",
+ "(12:00:00Z, 13:00:00Z) is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithDurationSequence() {
+ testExpressionTranslationWithContext(
+ "empty((xs:yearMonthDuration('P1Y'),xs:yearMonthDuration('P2Y')))", "ND-Root",
+ "(P1Y, P2Y) is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "empty(PathNode/RepeatableTextField/normalize-space(text()))", "ND-Root",
+ "BT-00-Repeatable-Text is empty");
+ }
+
+ @Test
+ void testSequenceEmptiness_WithRepeatableFieldReference_Negated() {
+ testExpressionTranslationWithContext(
+ "not(empty(PathNode/RepeatableTextField/normalize-space(text())))", "ND-Root",
+ "BT-00-Repeatable-Text is not empty");
+ }
+
+ // #endregion: Sequence emptiness
+
+ // #region: String empty function
+
+ @Test
+ void testStringEmptyFunction() {
+ testExpressionTranslationWithContext("'hello' = ''", "ND-Root", "empty('hello')");
+ }
+
+ @Test
+ void testStringEmptyFunction_WithFieldReference() {
+ testExpressionTranslationWithContext("PathNode/TextField/normalize-space(text()) = ''",
+ "ND-Root", "empty(BT-00-Text)");
+ }
+
+ // #endregion: String empty function
+
+ // #region: Sequence duplicates
+
+ @Test
+ void testSequenceDuplicates_WithNonRepeatableField() {
+ // Field references always go through sequence duplicates, regardless of repeatability.
+ testExpressionTranslationWithContext(
+ "not(count(PathNode/TextField/normalize-space(text())) = count(distinct-values(PathNode/TextField/normalize-space(text()))))",
+ "ND-Root", "BT-00-Text has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithStringSequence() {
+ testExpressionTranslationWithContext(
+ "not(count(('a','b','a')) = count(distinct-values(('a','b','a'))))", "ND-Root",
+ "('a', 'b', 'a') has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithStringSequence_Negated() {
+ testExpressionTranslationWithContext(
+ "count(('a','b','c')) = count(distinct-values(('a','b','c')))", "ND-Root",
+ "('a', 'b', 'c') has no duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithNumericSequence() {
+ testExpressionTranslationWithContext(
+ "not(count((1,2,3)) = count(distinct-values((1,2,3))))", "ND-Root",
+ "(1, 2, 3) has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithBooleanSequence() {
+ testExpressionTranslationWithContext(
+ "not(count((true(),false())) = count(distinct-values((true(),false()))))", "ND-Root",
+ "(TRUE, FALSE) has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithDateSequence() {
+ testExpressionTranslationWithContext(
+ "not(count((xs:date('2024-01-01Z'),xs:date('2024-12-31Z'))) = count(distinct-values((xs:date('2024-01-01Z'),xs:date('2024-12-31Z')))))",
+ "ND-Root",
+ "(2024-01-01Z, 2024-12-31Z) has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithTimeSequence() {
+ testExpressionTranslationWithContext(
+ "not(count((xs:time('12:00:00Z'),xs:time('13:00:00Z'))) = count(distinct-values((xs:time('12:00:00Z'),xs:time('13:00:00Z')))))",
+ "ND-Root",
+ "(12:00:00Z, 13:00:00Z) has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithDurationSequence() {
+ testExpressionTranslationWithContext(
+ "not(count((xs:yearMonthDuration('P1Y'),xs:yearMonthDuration('P2Y'))) = count(distinct-values((xs:yearMonthDuration('P1Y'),xs:yearMonthDuration('P2Y')))))",
+ "ND-Root",
+ "(P1Y, P2Y) has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithRepeatableFieldReference() {
+ testExpressionTranslationWithContext(
+ "not(count(PathNode/RepeatableTextField/normalize-space(text())) = count(distinct-values(PathNode/RepeatableTextField/normalize-space(text()))))",
+ "ND-Root",
+ "BT-00-Repeatable-Text has duplicates");
+ }
+
+ @Test
+ void testSequenceDuplicates_WithRepeatableFieldReference_Negated() {
+ testExpressionTranslationWithContext(
+ "count(PathNode/RepeatableTextField/normalize-space(text())) = count(distinct-values(PathNode/RepeatableTextField/normalize-space(text())))",
+ "ND-Root",
+ "BT-00-Repeatable-Text has no duplicates");
+ }
+
+ // #endregion: Sequence duplicates
+
@Test
void testParameterizedExpression_WithStringParameter() {
testExpressionTranslation("'hello' = 'world'", "{ND-Root, text:$p1, text:$p2} ${$p1 == $p2}",
diff --git a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
index fa775c03..346fd5fe 100644
--- a/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
+++ b/src/test/java/eu/europa/ted/efx/sdk2/EfxTemplateTranslatorV2Test.java
@@ -1428,6 +1428,122 @@ void testExpressionBlock_ShorthandFieldValueReferenceFromContextField_WithNodeCo
// #endregion Expression block -----------------------------------------------
+ // #region Formatting functions ------------------------------------------------
+
+ @Test
+ void testFormatShortDate() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-date(xs:date('2026-02-15'), '[D01]/[M01]/[Y0001]')) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-short(date('2026-02-15'))}"));
+ }
+
+ @Test
+ void testFormatShortDate_WithFieldReference() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-date(PathNode/StartDateField/xs:date(text()), '[D01]/[M01]/[Y0001]')) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-short(BT-00-StartDate)}"));
+ }
+
+ @Test
+ void testFormatMediumDate() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-date(xs:date('2026-02-15'), '[D01] [MNn,3-3] [Y0001]', 'en', (), ())) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-medium(date('2026-02-15'))}"));
+ }
+
+ @Test
+ void testFormatLongDate() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-date(xs:date('2026-02-15'), '[D01] [MNn] [Y0001]', 'en', (), ())) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-long(date('2026-02-15'))}"));
+ }
+
+ @Test
+ void testFormatShortTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-time(xs:time('14:30:00Z'), '[H01]:[m01] [Z]')) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-short(time('14:30:00Z'))}"));
+ }
+
+ @Test
+ void testFormatMediumTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-time(xs:time('14:30:00Z'), '[H01]:[m01]:[s01]')) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-medium(time('14:30:00Z'))}"));
+ }
+
+ @Test
+ void testFormatLongTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(format-time(xs:time('14:30:00Z'), '[H01]:[m01]:[s01] [Z]')) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-long(time('14:30:00Z'))}"));
+ }
+
+ @Test
+ void testFormatShortDateTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(concat(format-date(xs:date('2026-02-15'), '[D01]/[M01]/[Y0001]'), ' ', format-time(xs:time('14:30:00Z'), '[H01]:[m01] [Z]'))) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-short(date('2026-02-15'), time('14:30:00Z'))}"));
+ }
+
+ @Test
+ void testFormatMediumDateTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(concat(format-date(xs:date('2026-02-15'), '[D01] [MNn,3-3] [Y0001]', 'en', (), ()), ' ', format-time(xs:time('14:30:00Z'), '[H01]:[m01]:[s01]'))) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-medium(date('2026-02-15'), time('14:30:00Z'))}"));
+ }
+
+ @Test
+ void testFormatLongDateTime() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(concat(format-date(xs:date('2026-02-15'), '[D01] [MNn] [Y0001]', 'en', (), ()), ' ', format-time(xs:time('14:30:00Z'), '[H01]:[m01]:[s01] [Z]'))) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${format-long(date('2026-02-15'), time('14:30:00Z'))}"));
+ }
+
+ // #endregion Formatting functions ---------------------------------------------
+
+ // #region Preferred language functions ----------------------------------------
+
+ @Test
+ void testPreferredLanguageFunction() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(efx:preferred-language(PathNode/TextMultilingualField)) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${preferred-language(BT-00-Text-Multilingual)}"));
+ }
+
+ @Test
+ void testPreferredLanguageTextFunction() {
+ assertEquals(
+ lines("TEMPLATES:",
+ "let body01() -> { eval(efx:preferred-language-text(PathNode/TextMultilingualField)) }",
+ "MAIN:", "for-each(/*).call(body01())"),
+ translateTemplate("{/} ${preferred-language-text(BT-00-Text-Multilingual)}"));
+ }
+
+ // #endregion Preferred language functions -------------------------------------
+
// #region contextDeclarationBlock -------------------------------------------
@Test
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx
index a1090ec3..fe66b4ea 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testAssertAndReport_Simple/input.efx
@@ -8,6 +8,6 @@ WITH BT-00-Text
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1
- REPORT BT-00-Text is empty
+ REPORT empty(BT-00-Text)
AS WARNING R-X3F-N8W
FOR BT-00-Text IN 1
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch
index 8188422e..c1aed84a 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch
index a52ec7c3..15f14dec 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch
index 3d0fd20e..b98a033d 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-3.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch
index c00bb50b..c5e3e5f5 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-4.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch
index 8df534af..c23456f7 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-5.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch
index 66319591..aa6a5dec 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch
index fa15b55a..380cc4b3 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-E2.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch
index 6854b7c9..2b030c92 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/dynamic/validation-stage-1a-X01.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx
index 5dcbfbb5..eb251650 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/input.efx
@@ -8,6 +8,6 @@ WITH BT-00-Text
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN *
- ASSERT BT-00-Text is not empty
+ ASSERT not(empty(BT-00-Text))
AS WARNING R-X3F-N8W
FOR BT-00-Text IN ANY
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch
index 8188422e..c1aed84a 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch
index a52ec7c3..15f14dec 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch
index 3d0fd20e..b98a033d 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-3.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch
index c00bb50b..c5e3e5f5 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-4.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch
index 8df534af..c23456f7 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-5.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch
index 66319591..aa6a5dec 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch
index fa15b55a..380cc4b3 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-E2.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch
index 6854b7c9..2b030c92 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_AllNoticeTypes/static/validation-stage-1a-X01.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch
index b84fd2a4..8d12ef68 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch
index 9a303ee6..d6c2d6f5 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/dynamic/validation-stage-1-3.sch
@@ -1,6 +1,6 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx
index d8dc180e..6719b0bd 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/input.efx
@@ -24,7 +24,7 @@ WITH BT-00-Text
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1, 2
- ASSERT BT-00-Text is not empty
+ ASSERT not(empty(BT-00-Text))
AS WARNING R-X3F-N8W
FOR BT-00-Text IN 1, 3
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch
index b84fd2a4..8d12ef68 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-1.sch
@@ -2,6 +2,6 @@
rule|text|R-K7P-M2Q
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch
index 9a303ee6..d6c2d6f5 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testInClause_PhaseGeneration/static/validation-stage-1-3.sch
@@ -1,6 +1,6 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch
index a86e043e..bf9f8744 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-1.sch
@@ -12,7 +12,7 @@
rule|text|R-Z8H-A3X
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
rule|text|R-W1D-J2Y
rule|text|R-Q7G-E4Z
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch
index 0010b570..17875378 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-1a-2.sch
@@ -12,7 +12,7 @@
rule|text|R-Z8H-A3X
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
rule|text|R-W1D-J2Y
rule|text|R-Q7G-E4Z
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch
index 5eed45e1..4305445b 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-1.sch
@@ -2,7 +2,7 @@
rule|text|R-M3C-U8N
- rule|text|R-S9L-R5K
+ rule|text|R-S9L-R5K
rule|text|R-N6P-I2F
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch
index ca07e512..73e0aaa2 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/dynamic/validation-stage-2a-2.sch
@@ -2,7 +2,7 @@
rule|text|R-M3C-U8N
- rule|text|R-S9L-R5K
+ rule|text|R-S9L-R5K
rule|text|R-N6P-I2F
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx
index 93851c89..722ca645 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/input.efx
@@ -9,7 +9,7 @@ WITH BT-00-Text
ASSERT BT-00-Text is present
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1, 2
- REPORT BT-00-Text is empty
+ REPORT empty(BT-00-Text)
AS WARNING R-X3F-N8W
FOR BT-00-Text IN 1, 2
WHEN BT-00-Text == 'open'
@@ -36,7 +36,7 @@ WITH ND-SubNode
WITH BT-00-Number
WHEN BT-00-Number > 0
- REPORT BT-00-Text is not empty
+ REPORT not(empty(BT-00-Text))
AS INFO R-F5V-T6B
FOR BT-00-Text IN 1, 2
WHEN BT-00-Number < 100
@@ -53,7 +53,7 @@ WITH BT-00-Indicator
REPORT BT-00-Indicator is present
AS INFO R-M3C-U8N
FOR BT-00-Indicator IN 1, 2
- ASSERT BT-00-Text is not empty
+ ASSERT not(empty(BT-00-Text))
AS ERROR R-S9L-R5K
FOR BT-00-Text IN 1, 2
WHEN BT-00-Indicator == TRUE
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch
index a86e043e..bf9f8744 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-1.sch
@@ -12,7 +12,7 @@
rule|text|R-Z8H-A3X
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
rule|text|R-W1D-J2Y
rule|text|R-Q7G-E4Z
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch
index 0010b570..17875378 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-1a-2.sch
@@ -12,7 +12,7 @@
rule|text|R-Z8H-A3X
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
rule|text|R-W1D-J2Y
rule|text|R-Q7G-E4Z
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch
index 5eed45e1..4305445b 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-1.sch
@@ -2,7 +2,7 @@
rule|text|R-M3C-U8N
- rule|text|R-S9L-R5K
+ rule|text|R-S9L-R5K
rule|text|R-N6P-I2F
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch
index ca07e512..73e0aaa2 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_ComprehensiveMixedRules/static/validation-stage-2a-2.sch
@@ -2,7 +2,7 @@
rule|text|R-M3C-U8N
- rule|text|R-S9L-R5K
+ rule|text|R-S9L-R5K
rule|text|R-N6P-I2F
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx
index cd4e4844..498c79f0 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testOutput_FromSampleRulesFile/input.efx
@@ -54,7 +54,7 @@ LET text : $rootText = BT-00-Text;
// Complex WHEN condition with variable
WITH ND-SubNode
-WHEN $rootText is not empty
+WHEN not(empty($rootText))
ASSERT BT-00-Indicator is present
AS ERROR R-F5V-T6B
FOR BT-00-Indicator IN *;
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch
index e1878f3c..8855a6ed 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-1.sch
@@ -1,6 +1,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch
index 769a2f5b..985bcb13 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/dynamic/validation-stage-1a-2.sch
@@ -1,6 +1,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx
index 2f0d9ce1..1111ee6b 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/input.efx
@@ -8,6 +8,6 @@ LET text : $noticeType = "16";
---- STAGE 1a ----
WITH ND-Root
- ASSERT $sdkVersion is not empty
+ ASSERT not(empty($sdkVersion))
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1, 2
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch
index e1878f3c..8855a6ed 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-1.sch
@@ -1,6 +1,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch
index 769a2f5b..985bcb13 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_Global_AppearsBeforeIncludes/static/validation-stage-1a-2.sch
@@ -1,6 +1,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch
index d3235262..f8e573e9 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch
index 9c10e96d..798823e2 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/dynamic/validation-stage-1b-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx
index e39d8fb4..f5a52623 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/input.efx
@@ -1,11 +1,11 @@
---- STAGE 1a ----
WITH text : $stageVar = "first", BT-00-Text
-ASSERT $stageVar is not empty
+ASSERT not(empty($stageVar))
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1;
---- STAGE 1b ----
WITH text : $stageVar = "second", BT-00-Text
-ASSERT $stageVar is not empty
+ASSERT not(empty($stageVar))
AS ERROR R-X3F-N8W
FOR BT-00-Text IN 1;
\ No newline at end of file
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch
index d3235262..f8e573e9 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch
index 9c10e96d..798823e2 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testVariable_StageLevel/static/validation-stage-1b-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch
index e3457f4a..5b842b1a 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/dynamic/validation-stage-1a-2.sch
@@ -3,6 +3,6 @@
rule|text|R-X3F-N8W
rule|text|R-H9T-V5L
- rule|text|R-B6J-C4R
+ rule|text|R-B6J-C4R
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx
index e1a93042..d533058b 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/input.efx
@@ -21,6 +21,6 @@ WITH BT-00-Text
AS WARNING R-H9T-V5L
FOR BT-00-Text IN 2
- OTHERWISE REPORT BT-00-Text is not empty
+ OTHERWISE REPORT not(empty(BT-00-Text))
AS INFO R-B6J-C4R
FOR BT-00-Text IN 2
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch
index e3457f4a..5b842b1a 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWhen_WithOtherwise/static/validation-stage-1a-2.sch
@@ -3,6 +3,6 @@
rule|text|R-X3F-N8W
rule|text|R-H9T-V5L
- rule|text|R-B6J-C4R
+ rule|text|R-B6J-C4R
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch
index 135940d9..3bf3891f 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch
index e221483a..846841da 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/dynamic/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx
index edf7d629..50ed456f 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/input.efx
@@ -5,6 +5,6 @@
---- STAGE 1a ----
WITH context : $ctx = BT-00-Text
- ASSERT $ctx is not empty
+ ASSERT not(empty($ctx))
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1, 2
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch
index 135940d9..3bf3891f 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch
index e221483a..846841da 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariable/static/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-1.sch
index 945ab7d3..21875594 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-CTX-001
+ rule|text|R-CTX-001
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-2.sch
index 21908528..4c98abf7 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/dynamic/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
- rule|text|R-CTX-001
+ rule|text|R-CTX-001
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/input.efx
index 92f1ca00..87f30e2e 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/input.efx
@@ -5,6 +5,6 @@
---- STAGE 1a ----
WITH context : $ctx = ND-Root
- ASSERT $ctx::BT-00-Text is not empty
+ ASSERT not(empty($ctx::BT-00-Text))
AS ERROR R-CTX-001
FOR BT-00-Text IN 1, 2
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-1.sch
index 945ab7d3..21875594 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-CTX-001
+ rule|text|R-CTX-001
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-2.sch
index 21908528..4c98abf7 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_ContextVariableOverride/static/validation-stage-1a-2.sch
@@ -2,6 +2,6 @@
- rule|text|R-CTX-001
+ rule|text|R-CTX-001
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch
index c86e81e6..5c0079ce 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-1.sch
@@ -9,30 +9,30 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
- rule|text|R-Y2N-G7S
+ rule|text|R-Y2N-G7S
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
- rule|text|R-M3C-U8N
+ rule|text|R-M3C-U8N
- rule|text|R-V4T-O7J
+ rule|text|R-V4T-O7J
- rule|text|R-G2M-X6D
+ rule|text|R-G2M-X6D
- rule|text|R-C1V-Z4H
+ rule|text|R-C1V-Z4H
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch
index c2626ea8..219d94cb 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1a-2.sch
@@ -9,7 +9,7 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
rule|text|R-D4K-P9M
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch
index f5f07bc7..e8446a85 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/dynamic/validation-stage-1b-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-E3F-L2G
+ rule|text|R-E3F-L2G
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx
index 8ce5b661..2db111d8 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/input.efx
@@ -11,17 +11,17 @@ LET text : $stageVar2 = "S2";
// Case 1: No variables (baseline) - uses stage variable
WITH BT-00-Text
- ASSERT $stageVar1 is not empty
+ ASSERT not(empty($stageVar1))
AS ERROR R-K7P-M2Q
FOR BT-00-Text IN 1
- ASSERT BT-00-Text is not empty
+ ASSERT not(empty(BT-00-Text))
AS ERROR R-X3F-N8W
FOR BT-00-Text IN 2
// Case 2: Single variable before context (pattern-level)
WITH text : $before1 = "b1", BT-00-Number
- ASSERT $before1 is not empty
+ ASSERT not(empty($before1))
AS ERROR R-Y2N-G7S
FOR BT-00-Number IN 1
@@ -31,7 +31,7 @@ WITH text : $before1 = "b1", BT-00-Number
// Case 3: Multiple variables before context (pattern-level)
WITH text : $preA = "A", text : $preB = "B", BT-00-Indicator
- ASSERT $preA is not empty and $preB is not empty
+ ASSERT not(empty($preA)) and not(empty($preB))
AS ERROR R-F5V-T6B
FOR BT-00-Indicator IN 1
@@ -41,7 +41,7 @@ WITH text : $preA = "A", text : $preB = "B", BT-00-Indicator
// Case 4: Single variable after context (rule-level) - uses stage variable
WITH BT-00-Text, text : $after1 = "a1"
- ASSERT $stageVar2 is not empty and $after1 is not empty
+ ASSERT not(empty($stageVar2)) and not(empty($after1))
AS ERROR R-M3C-U8N
FOR BT-00-Text IN 1
@@ -51,7 +51,7 @@ WITH BT-00-Text, text : $after1 = "a1"
// Case 5: Multiple variables after context (rule-level)
WITH BT-00-Number, text : $postX = "X", text : $postY = "Y"
- ASSERT $postX is not empty and $postY is not empty
+ ASSERT not(empty($postX)) and not(empty($postY))
AS ERROR R-V4T-O7J
FOR BT-00-Number IN 1
@@ -61,7 +61,7 @@ WITH BT-00-Number, text : $postX = "X", text : $postY = "Y"
// Case 6: One before, one after (mixed pattern/rule level)
WITH text : $left = "L", BT-00-Indicator, text : $right = "R"
- ASSERT $left is not empty and $right is not empty
+ ASSERT not(empty($left)) and not(empty($right))
AS ERROR R-G2M-X6D
FOR BT-00-Indicator IN 1
@@ -71,7 +71,7 @@ WITH text : $left = "L", BT-00-Indicator, text : $right = "R"
// Case 7: Multiple before and after (full mixed) - uses both stage variables
WITH text : $p1 = "P1", text : $p2 = "P2", BT-00-Text, text : $r1 = "R1", text : $r2 = "R2"
- ASSERT $stageVar1 is not empty and $stageVar2 is not empty and $p1 is not empty
+ ASSERT not(empty($stageVar1)) and not(empty($stageVar2)) and not(empty($p1))
AS ERROR R-C1V-Z4H
FOR BT-00-Text IN 1
@@ -86,7 +86,7 @@ LET text : $stage1bVar = "1B";
// Simple rule using stage 1b variable
WITH BT-00-Text
- ASSERT $stage1bVar is not empty
+ ASSERT not(empty($stage1bVar))
AS ERROR R-E3F-L2G
FOR BT-00-Text IN 1
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch
index c86e81e6..5c0079ce 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-1.sch
@@ -9,30 +9,30 @@
- rule|text|R-K7P-M2Q
+ rule|text|R-K7P-M2Q
- rule|text|R-Y2N-G7S
+ rule|text|R-Y2N-G7S
- rule|text|R-F5V-T6B
+ rule|text|R-F5V-T6B
- rule|text|R-M3C-U8N
+ rule|text|R-M3C-U8N
- rule|text|R-V4T-O7J
+ rule|text|R-V4T-O7J
- rule|text|R-G2M-X6D
+ rule|text|R-G2M-X6D
- rule|text|R-C1V-Z4H
+ rule|text|R-C1V-Z4H
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch
index c2626ea8..219d94cb 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1a-2.sch
@@ -9,7 +9,7 @@
- rule|text|R-X3F-N8W
+ rule|text|R-X3F-N8W
rule|text|R-D4K-P9M
diff --git a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch
index f5f07bc7..e8446a85 100644
--- a/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch
+++ b/src/test/resources/eu/europa/ted/efx/sdk2/EfxRulesTranslatorV2Test/testWithClause_VariablePositioning/static/validation-stage-1b-1.sch
@@ -2,6 +2,6 @@
- rule|text|R-E3F-L2G
+ rule|text|R-E3F-L2G