From 1657aff03bdb0b6756e02271d0e3c9110577487d Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Fri, 9 Aug 2024 15:24:00 -0700 Subject: [PATCH] Add single method to control error handling behavior 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. --- .../source/i18n/messageformat2_formatter.cpp | 16 +++++ icu4c/source/i18n/unicode/messageformat2.h | 64 ++++++++++++------- .../test/intltest/messageformat2test.cpp | 16 ++--- .../test/intltest/messageformat2test_utils.h | 4 +- 4 files changed, 66 insertions(+), 34 deletions(-) diff --git a/icu4c/source/i18n/messageformat2_formatter.cpp b/icu4c/source/i18n/messageformat2_formatter.cpp index 4a9bc683d193..2d93d1368700 100644 --- a/icu4c/source/i18n/messageformat2_formatter.cpp +++ b/icu4c/source/i18n/messageformat2_formatter.cpp @@ -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) diff --git a/icu4c/source/i18n/unicode/messageformat2.h b/icu4c/source/i18n/unicode/messageformat2.h index 17e19713b8ca..6bfb93f2ca9e 100644 --- a/icu4c/source/i18n/unicode/messageformat2.h +++ b/icu4c/source/i18n/unicode/messageformat2.h @@ -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. * @@ -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) diff --git a/icu4c/source/test/intltest/messageformat2test.cpp b/icu4c/source/test/intltest/messageformat2test.cpp index 5b1b65a4904c..06666ac7905e 100644 --- a/icu4c/source/test/intltest/messageformat2test.cpp +++ b/icu4c/source/test/intltest/messageformat2test.cpp @@ -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); @@ -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)); @@ -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); @@ -118,7 +118,7 @@ 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); @@ -126,7 +126,7 @@ void TestMessageFormat2::testFormatterAPI() { 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); @@ -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) diff --git a/icu4c/source/test/intltest/messageformat2test_utils.h b/icu4c/source/test/intltest/messageformat2test_utils.h index 60160aafcb5e..8ced63372090 100644 --- a/icu4c/source/test/intltest/messageformat2test_utils.h +++ b/icu4c/source/test/intltest/messageformat2test_utils.h @@ -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; @@ -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);