Skip to content

Commit

Permalink
Add single method to control error handling behavior
Browse files Browse the repository at this point in the history
Add MessageFormatter::Builder::setErrorHandlingBehavior() method
and a new enum type MessageFormatter::Builder::UMFErrorHandlingBehavior
to denote strict or best-effort behavior.

The reason for adding a single method that takes an enum is to allow
for the possibility of more error handling modes in the future.
  • Loading branch information
catamorphism committed Aug 9, 2024
1 parent db75954 commit 1657aff
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 34 deletions.
16 changes: 16 additions & 0 deletions icu4c/source/i18n/messageformat2_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ namespace message2 {
return *this;
}

MessageFormatter::Builder&
MessageFormatter::Builder::setErrorHandlingBehavior(
MessageFormatter::Builder::UMFErrorHandlingBehavior type) {
switch (type) {
case U_MF_STRICT: {
signalErrors = true;
break;
}
case U_MF_BEST_EFFORT: {
signalErrors = false;
break;
}
}
return *this;
}

/*
This build() method is non-destructive, which entails the risk that
its borrowed MFFunctionRegistry and (if the setDataModel() method was called)
Expand Down
64 changes: 40 additions & 24 deletions icu4c/source/i18n/unicode/messageformat2.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,29 @@ namespace message2 {

void clearState();
public:
/*
* Used in conjunction with the `setErrorHandlingBehavior()` method.
*
* @internal ICU 76 technology preview
* @deprecated This API is for technology preview only.
*/
typedef enum UMFErrorHandlingBehavior {
/*
* Suppress errors and return best-effort output.
*
* @internal ICU 76 technology preview
* @deprecated This API is for technology preview only.
*/
U_MF_BEST_EFFORT = 0,
/*
* Signal all MessageFormat errors using the UErrorCode
* argument.
*
* @internal ICU 76 technology preview
* @deprecated This API is for technology preview only.
*/
U_MF_STRICT
} UMFErrorHandlingBehavior;
/**
* Sets the locale to use for formatting.
*
Expand Down Expand Up @@ -222,42 +245,35 @@ namespace message2 {
*/
Builder& setDataModel(MFDataModel&& dataModel);
/**
* Set this formatter to handle errors "strictly",
* meaning that formatting methods will set their
* UErrorCode arguments to signal MessageFormat data model,
* resolution, and runtime errors. Syntax errors are
* always signaled.
* This doesn't re-trigger parsing of an existing pattern,
* so it has to be called before parsing the pattern
* whose error behavior should be affected.
* Set the error handling behavior for this formatter.
*
* The default is to suppress all MessageFormat errors
* and return best-effort output.
*
* @return A reference to the builder.
* "Strict" error behavior means that that formatting methods
* will set their UErrorCode arguments to signal MessageFormat
* data model, resolution, and runtime errors. Syntax errors are
* always signaled.
*
* @internal ICU 75 technology preview
* @deprecated This API is for technology preview only.
*/
Builder& setStrictErrors() { signalErrors = true; return *this; }
/**
* Set this formatter to suppress errors
* meaning that formatting methods will _not_ set their
* "Best effort" error behavior means that MessageFormat errors are
* suppressed: formatting methods will _not_ set their
* UErrorCode arguments to signal MessageFormat data model,
* resolution, or runtime errors. Best-effort output
* will be returned. Syntax errors are always signaled.
* This is the default behavior.
*
* This doesn't re-trigger parsing of an existing pattern,
* so it has to be called before parsing the pattern
* whose error behavior should be affected.
* @param type An enum with type `UMFErrorHandlingBehavior`;
* if type == `U_MF_STRICT`, then
* errors are handled strictly.
* If type == `U_MF_BEST_EFFORT`, then
* best-effort output is returned.
*
* The default is to suppress all MessageFormat errors
* and return best-effort output.
*
* @return A reference to the builder.
*
* @internal ICU 75 technology preview
* @internal ICU 76 technology preview
* @deprecated This API is for technology preview only.
*/
Builder& setSuppressErrors() { signalErrors = false; return *this; }
Builder& setErrorHandlingBehavior(UMFErrorHandlingBehavior type); // { signalErrors = true; return *this; }
/**
* Constructs a new immutable MessageFormatter using the pattern or data model
* that was previously set, and the locale (if it was previously set)
Expand Down
16 changes: 8 additions & 8 deletions icu4c/source/test/intltest/messageformat2test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,21 @@ void TestMessageFormat2::testFormatterAPI() {
// if there's a syntax error
UnicodeString pattern = "{{}";
MessageFormatter::Builder mfBuilder(errorCode);
mfBuilder.setSuppressErrors(); // This shouldn't matter, since there's a syntax error
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_BEST_EFFORT); // This shouldn't matter, since there's a syntax error
mfBuilder.setPattern(pattern, parseError, errorCode);
MessageFormatter mf = mfBuilder.build(errorCode);
U_ASSERT(errorCode == U_MF_SYNTAX_ERROR);

/*
Parsing is done when setPattern() is called,
so setStrictErrors() or setSuppressErrors must be called
so setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT) or setSuppressErrors must be called
_before_ setPattern() to get the right behavior,
and if either method is called after setting a pattern,
setPattern() has to be called again.
*/
// Should get the same behavior with strict errors
errorCode.reset();
mfBuilder.setStrictErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT);
// Force re-parsing, as above comment
mfBuilder.setPattern(pattern, parseError, errorCode);
mf = mfBuilder.build(errorCode);
Expand All @@ -99,7 +99,7 @@ void TestMessageFormat2::testFormatterAPI() {
pattern = "{{{$x}}}";
errorCode.reset();
// Check that a pattern with a resolution error gives fallback output
mfBuilder.setSuppressErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_BEST_EFFORT);
mfBuilder.setPattern(pattern, parseError, errorCode);
mf = mfBuilder.build(errorCode);
U_ASSERT(U_SUCCESS(errorCode));
Expand All @@ -108,7 +108,7 @@ void TestMessageFormat2::testFormatterAPI() {
U_ASSERT(result == "{$x}");

// Check that we do get an error with strict errors
mfBuilder.setStrictErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT);
mf = mfBuilder.build(errorCode);
U_ASSERT(U_SUCCESS(errorCode));
result = mf.formatToString(MessageArguments(), errorCode);
Expand All @@ -118,15 +118,15 @@ void TestMessageFormat2::testFormatterAPI() {
errorCode.reset();
pattern = "hello";
mfBuilder.setPattern(pattern, parseError, errorCode);
mfBuilder.setSuppressErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_BEST_EFFORT);
mf = mfBuilder.build(errorCode);
U_ASSERT(U_SUCCESS(errorCode));
result = mf.formatToString(MessageArguments(), errorCode);
U_ASSERT(U_SUCCESS(errorCode));
U_ASSERT(result == "hello");

// Check that behavior is the same with strict errors
mfBuilder.setStrictErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT);
mf = mfBuilder.build(errorCode);
U_ASSERT(U_SUCCESS(errorCode));
result = mf.formatToString(MessageArguments(), errorCode);
Expand Down Expand Up @@ -286,7 +286,7 @@ void TestMessageFormat2::testAPICustomFunctions() {
MessageFormatter::Builder mfBuilder(errorCode);
UnicodeString result;
// This fails, because we did not provide a function registry:
MessageFormatter mf = mfBuilder.setStrictErrors()
MessageFormatter mf = mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT)
.setPattern("Hello {$name :person formality=informal}",
parseError, errorCode)
.setLocale(locale)
Expand Down
4 changes: 2 additions & 2 deletions icu4c/source/test/intltest/messageformat2test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class TestUtils {
}
// Initially, set error behavior to strict.
// We'll re-run to check for errors.
mfBuilder.setStrictErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_STRICT);
MessageFormatter mf = mfBuilder.build(errorCode);
UnicodeString result;

Expand Down Expand Up @@ -279,7 +279,7 @@ class TestUtils {
// Re-run the formatter if there was an error,
// in order to get best-effort output
errorCode.reset();
mfBuilder.setSuppressErrors();
mfBuilder.setErrorHandlingBehavior(MessageFormatter::Builder::U_MF_BEST_EFFORT);
mf = mfBuilder.build(errorCode);
if (U_SUCCESS(errorCode)) {
result = mf.formatToString(MessageArguments(testCase.getArguments(), errorCode), errorCode);
Expand Down

0 comments on commit 1657aff

Please sign in to comment.