Skip to content

Commit 23edf9c

Browse files
ICU-22762 MF2: Add builder method to control error handling behavior
Add MessageFormatter::Builder::setErrorHandlingBehavior() method and a new enum type MessageFormatter::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. Co-authored-by: Markus Scherer <[email protected]>
1 parent ac737da commit 23edf9c

10 files changed

+290
-73
lines changed

icu4c/source/i18n/messageformat2.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,7 +814,12 @@ UnicodeString MessageFormatter::formatToString(const MessageArguments& arguments
814814
}
815815
}
816816
// Update status according to all errors seen while formatting
817-
context.checkErrors(status);
817+
if (signalErrors) {
818+
context.checkErrors(status);
819+
}
820+
if (U_FAILURE(status)) {
821+
result.remove();
822+
}
818823
return result;
819824
}
820825

@@ -869,7 +874,7 @@ void MessageFormatter::checkDeclarations(MessageContext& context, Environment*&
869874
CHECK_ERROR(status);
870875

871876
const Binding* decls = getDataModel().getLocalVariablesInternal();
872-
U_ASSERT(env != nullptr && decls != nullptr);
877+
U_ASSERT(env != nullptr && (decls != nullptr || getDataModel().bindingsLen == 0));
873878

874879
for (int32_t i = 0; i < getDataModel().bindingsLen; i++) {
875880
const Binding& decl = decls[i];

icu4c/source/i18n/messageformat2_data_model.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -930,9 +930,10 @@ const Pattern& MFDataModel::getPattern() const {
930930
return *(std::get_if<Pattern>(&body));
931931
}
932932

933+
// Returns nullptr if no bindings
933934
const Binding* MFDataModel::getLocalVariablesInternal() const {
934935
U_ASSERT(!bogus);
935-
U_ASSERT(bindings.isValid());
936+
U_ASSERT(bindingsLen == 0 || bindings.isValid());
936937
return bindings.getAlias();
937938
}
938939

@@ -948,9 +949,10 @@ const Variant* MFDataModel::getVariantsInternal() const {
948949
return std::get_if<Matcher>(&body)->variants.getAlias();
949950
}
950951

952+
// Returns nullptr if no unsupported statements
951953
const UnsupportedStatement* MFDataModel::getUnsupportedStatementsInternal() const {
952954
U_ASSERT(!bogus);
953-
U_ASSERT(unsupportedStatements.isValid());
955+
U_ASSERT(unsupportedStatementsLen == 0 || unsupportedStatements != nullptr);
954956
return unsupportedStatements.getAlias();
955957
}
956958

@@ -1056,7 +1058,6 @@ MFDataModel::MFDataModel(const MFDataModel& other) : body(Pattern()) {
10561058
UErrorCode localErrorCode = U_ZERO_ERROR;
10571059

10581060
if (other.hasPattern()) {
1059-
// body.emplace<Pattern>(Pattern(*std::get_if<Pattern>(&other.body)));
10601061
body = *std::get_if<Pattern>(&other.body);
10611062
} else {
10621063
const Expression* otherSelectors = other.getSelectorsInternal();
@@ -1069,17 +1070,17 @@ MFDataModel::MFDataModel(const MFDataModel& other) : body(Pattern()) {
10691070
bogus = true;
10701071
return;
10711072
}
1072-
// body.emplace<Matcher>(Matcher(copiedSelectors, numSelectors, copiedVariants, numVariants));
10731073
body = Matcher(copiedSelectors, numSelectors, copiedVariants, numVariants);
10741074
}
10751075

10761076
bindingsLen = other.bindingsLen;
1077-
bindings.adoptInstead(copyArray(other.bindings.getAlias(), bindingsLen, localErrorCode));
1078-
if (U_FAILURE(localErrorCode)) {
1079-
bogus = true;
1077+
if (bindingsLen > 0) {
1078+
bindings.adoptInstead(copyArray(other.bindings.getAlias(), bindingsLen, localErrorCode));
10801079
}
10811080
unsupportedStatementsLen = other.unsupportedStatementsLen;
1082-
unsupportedStatements.adoptInstead(copyArray(other.unsupportedStatements.getAlias(), unsupportedStatementsLen, localErrorCode));
1081+
if (unsupportedStatementsLen > 0) {
1082+
unsupportedStatements.adoptInstead(copyArray(other.unsupportedStatements.getAlias(), unsupportedStatementsLen, localErrorCode));
1083+
}
10831084
if (U_FAILURE(localErrorCode)) {
10841085
bogus = true;
10851086
}
@@ -1106,9 +1107,14 @@ MFDataModel::MFDataModel(const MFDataModel::Builder& builder, UErrorCode& errorC
11061107

11071108
U_ASSERT(builder.bindings != nullptr);
11081109
bindingsLen = builder.bindings->size();
1109-
bindings.adoptInstead(copyVectorToArray<Binding>(*builder.bindings, errorCode));
1110+
if (bindingsLen > 0) {
1111+
bindings.adoptInstead(copyVectorToArray<Binding>(*builder.bindings, errorCode));
1112+
}
11101113
unsupportedStatementsLen = builder.unsupportedStatements->size();
1111-
unsupportedStatements.adoptInstead(copyVectorToArray<UnsupportedStatement>(*builder.unsupportedStatements, errorCode));
1114+
if (unsupportedStatementsLen > 0) {
1115+
unsupportedStatements.adoptInstead(copyVectorToArray<UnsupportedStatement>(*builder.unsupportedStatements,
1116+
errorCode));
1117+
}
11121118
if (U_FAILURE(errorCode)) {
11131119
bogus = true;
11141120
}

icu4c/source/i18n/messageformat2_errors.cpp

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -121,41 +121,10 @@ namespace message2 {
121121
if (count() == 0) {
122122
return;
123123
}
124-
if (staticErrors.syntaxAndDataModelErrors->size() > 0) {
125-
switch (staticErrors.first().type) {
126-
case StaticErrorType::DuplicateDeclarationError: {
127-
status = U_MF_DUPLICATE_DECLARATION_ERROR;
128-
break;
129-
}
130-
case StaticErrorType::DuplicateOptionName: {
131-
status = U_MF_DUPLICATE_OPTION_NAME_ERROR;
132-
break;
133-
}
134-
case StaticErrorType::VariantKeyMismatchError: {
135-
status = U_MF_VARIANT_KEY_MISMATCH_ERROR;
136-
break;
137-
}
138-
case StaticErrorType::DuplicateVariant: {
139-
status = U_MF_DUPLICATE_VARIANT_ERROR;
140-
break;
141-
}
142-
case StaticErrorType::NonexhaustivePattern: {
143-
status = U_MF_NONEXHAUSTIVE_PATTERN_ERROR;
144-
break;
145-
}
146-
case StaticErrorType::MissingSelectorAnnotation: {
147-
status = U_MF_MISSING_SELECTOR_ANNOTATION_ERROR;
148-
break;
149-
}
150-
case StaticErrorType::SyntaxError: {
151-
status = U_MF_SYNTAX_ERROR;
152-
break;
153-
}
154-
case StaticErrorType::UnsupportedStatementError: {
155-
status = U_MF_UNSUPPORTED_STATEMENT_ERROR;
156-
}
157-
}
158-
} else {
124+
staticErrors.checkErrors(status);
125+
if (U_FAILURE(status)) {
126+
return;
127+
}
159128
U_ASSERT(resolutionAndFormattingErrors->size() > 0);
160129
switch (first().type) {
161130
case DynamicErrorType::UnknownFunction: {
@@ -183,7 +152,6 @@ namespace message2 {
183152
break;
184153
}
185154
}
186-
}
187155
}
188156

189157
void StaticErrors::addSyntaxError(UErrorCode& status) {
@@ -277,6 +245,47 @@ namespace message2 {
277245
}
278246
}
279247

248+
void StaticErrors::checkErrors(UErrorCode& status) const {
249+
if (U_FAILURE(status)) {
250+
return;
251+
}
252+
if (syntaxAndDataModelErrors->size() > 0) {
253+
switch (first().type) {
254+
case StaticErrorType::DuplicateDeclarationError: {
255+
status = U_MF_DUPLICATE_DECLARATION_ERROR;
256+
break;
257+
}
258+
case StaticErrorType::DuplicateOptionName: {
259+
status = U_MF_DUPLICATE_OPTION_NAME_ERROR;
260+
break;
261+
}
262+
case StaticErrorType::VariantKeyMismatchError: {
263+
status = U_MF_VARIANT_KEY_MISMATCH_ERROR;
264+
break;
265+
}
266+
case StaticErrorType::DuplicateVariant: {
267+
status = U_MF_DUPLICATE_VARIANT_ERROR;
268+
break;
269+
}
270+
case StaticErrorType::NonexhaustivePattern: {
271+
status = U_MF_NONEXHAUSTIVE_PATTERN_ERROR;
272+
break;
273+
}
274+
case StaticErrorType::MissingSelectorAnnotation: {
275+
status = U_MF_MISSING_SELECTOR_ANNOTATION_ERROR;
276+
break;
277+
}
278+
case StaticErrorType::SyntaxError: {
279+
status = U_MF_SYNTAX_ERROR;
280+
break;
281+
}
282+
case StaticErrorType::UnsupportedStatementError: {
283+
status = U_MF_UNSUPPORTED_STATEMENT_ERROR;
284+
}
285+
}
286+
}
287+
}
288+
280289
const StaticError& StaticErrors::first() const {
281290
U_ASSERT(syntaxAndDataModelErrors.isValid() && syntaxAndDataModelErrors->size() > 0);
282291
return *static_cast<StaticError*>(syntaxAndDataModelErrors->elementAt(0));

icu4c/source/i18n/messageformat2_errors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ namespace message2 {
100100
bool hasSyntaxError() const { return syntaxError; }
101101
bool hasMissingSelectorAnnotationError() const { return missingSelectorAnnotationError; }
102102
void addError(StaticError&&, UErrorCode&);
103-
void checkErrors(UErrorCode&);
103+
void checkErrors(UErrorCode&) const;
104104

105+
void clear();
105106
const StaticError& first() const;
106107
StaticErrors(const StaticErrors&, UErrorCode&);
107108
StaticErrors(StaticErrors&&) noexcept;

icu4c/source/i18n/messageformat2_formatter.cpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,31 @@ namespace message2 {
2727
// -------------------------------------
2828
// Creates a MessageFormat instance based on the pattern.
2929

30-
MessageFormatter::Builder& MessageFormatter::Builder::setPattern(const UnicodeString& pat, UParseError& parseError, UErrorCode& errorCode) {
30+
void MessageFormatter::Builder::clearState() {
3131
normalizedInput.remove();
32+
delete errors;
33+
errors = nullptr;
34+
}
35+
36+
MessageFormatter::Builder& MessageFormatter::Builder::setPattern(const UnicodeString& pat,
37+
UParseError& parseError,
38+
UErrorCode& errorCode) {
39+
clearState();
40+
// Create errors
41+
errors = create<StaticErrors>(StaticErrors(errorCode), errorCode);
42+
THIS_ON_ERROR(errorCode);
43+
3244
// Parse the pattern
3345
MFDataModel::Builder tree(errorCode);
3446
Parser(pat, tree, *errors, normalizedInput).parse(parseError, errorCode);
3547

48+
// Fail on syntax errors
49+
if (errors->hasSyntaxError()) {
50+
errors->checkErrors(errorCode);
51+
// Check that the checkErrors() method set the error code
52+
U_ASSERT(U_FAILURE(errorCode));
53+
}
54+
3655
// Build the data model based on what was parsed
3756
dataModel = tree.build(errorCode);
3857
hasDataModel = true;
@@ -55,16 +74,21 @@ namespace message2 {
5574
}
5675

5776
MessageFormatter::Builder& MessageFormatter::Builder::setDataModel(MFDataModel&& newDataModel) {
58-
normalizedInput.remove();
59-
delete errors;
60-
errors = nullptr;
77+
clearState();
6178
hasPattern = false;
6279
hasDataModel = true;
6380
dataModel = std::move(newDataModel);
6481

6582
return *this;
6683
}
6784

85+
MessageFormatter::Builder&
86+
MessageFormatter::Builder::setErrorHandlingBehavior(
87+
MessageFormatter::UMFErrorHandlingBehavior type) {
88+
signalErrors = type == U_MF_STRICT;
89+
return *this;
90+
}
91+
6892
/*
6993
This build() method is non-destructive, which entails the risk that
7094
its borrowed MFFunctionRegistry and (if the setDataModel() method was called)
@@ -86,6 +110,7 @@ namespace message2 {
86110
MessageFormatter::Builder::~Builder() {
87111
if (errors != nullptr) {
88112
delete errors;
113+
errors = nullptr;
89114
}
90115
}
91116

@@ -116,6 +141,7 @@ namespace message2 {
116141
standardMFFunctionRegistry.checkStandard();
117142

118143
normalizedInput = builder.normalizedInput;
144+
signalErrors = builder.signalErrors;
119145

120146
// Build data model
121147
// First, check that there is a data model
@@ -162,6 +188,7 @@ namespace message2 {
162188
customMFFunctionRegistry = other.customMFFunctionRegistry;
163189
dataModel = std::move(other.dataModel);
164190
normalizedInput = std::move(other.normalizedInput);
191+
signalErrors = other.signalErrors;
165192
errors = other.errors;
166193
other.errors = nullptr;
167194
return *this;

icu4c/source/i18n/messageformat2_serializer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ void Serializer::emit(const Pattern& pat) {
248248

249249
void Serializer::serializeDeclarations() {
250250
const Binding* bindings = dataModel.getLocalVariablesInternal();
251-
U_ASSERT(bindings != nullptr);
251+
U_ASSERT(dataModel.bindingsLen == 0 || bindings != nullptr);
252252

253253
for (int32_t i = 0; i < dataModel.bindingsLen; i++) {
254254
const Binding& b = bindings[i];
@@ -272,7 +272,7 @@ void Serializer::serializeDeclarations() {
272272

273273
void Serializer::serializeUnsupported() {
274274
const UnsupportedStatement* statements = dataModel.getUnsupportedStatementsInternal();
275-
U_ASSERT(statements != nullptr);
275+
U_ASSERT(dataModel.unsupportedStatementsLen == 0 || statements != nullptr);
276276

277277
for (int32_t i = 0; i < dataModel.unsupportedStatementsLen; i++) {
278278
const UnsupportedStatement& s = statements[i];

0 commit comments

Comments
 (0)