1 // © 2024 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #if !UCONFIG_NO_MF2
9
10 #include <math.h>
11
12 #include "unicode/dtptngen.h"
13 #include "unicode/messageformat2_data_model_names.h"
14 #include "unicode/messageformat2_function_registry.h"
15 #include "unicode/smpdtfmt.h"
16 #include "charstr.h"
17 #include "double-conversion.h"
18 #include "messageformat2_allocation.h"
19 #include "messageformat2_function_registry_internal.h"
20 #include "messageformat2_macros.h"
21 #include "hash.h"
22 #include "number_types.h"
23 #include "uvector.h" // U_ASSERT
24
25 // The C99 standard suggested that C++ implementations not define PRId64 etc. constants
26 // unless this macro is defined.
27 // See the Notes at https://en.cppreference.com/w/cpp/types/integer .
28 // Similar to defining __STDC_LIMIT_MACROS in unicode/ptypes.h .
29 #ifndef __STDC_FORMAT_MACROS
30 # define __STDC_FORMAT_MACROS
31 #endif
32 #include <inttypes.h>
33 #include <math.h>
34
35 U_NAMESPACE_BEGIN
36
37 namespace message2 {
38
39 // Function registry implementation
40
~Formatter()41 Formatter::~Formatter() {}
~Selector()42 Selector::~Selector() {}
~FormatterFactory()43 FormatterFactory::~FormatterFactory() {}
~SelectorFactory()44 SelectorFactory::~SelectorFactory() {}
45
build()46 MFFunctionRegistry MFFunctionRegistry::Builder::build() {
47 U_ASSERT(formatters != nullptr && selectors != nullptr && formattersByType != nullptr);
48 MFFunctionRegistry result = MFFunctionRegistry(formatters, selectors, formattersByType);
49 formatters = nullptr;
50 selectors = nullptr;
51 formattersByType = nullptr;
52 return result;
53 }
54
adoptSelector(const FunctionName & selectorName,SelectorFactory * selectorFactory,UErrorCode & errorCode)55 MFFunctionRegistry::Builder& MFFunctionRegistry::Builder::adoptSelector(const FunctionName& selectorName, SelectorFactory* selectorFactory, UErrorCode& errorCode) {
56 if (U_SUCCESS(errorCode)) {
57 U_ASSERT(selectors != nullptr);
58 selectors->put(selectorName, selectorFactory, errorCode);
59 }
60 return *this;
61 }
62
adoptFormatter(const FunctionName & formatterName,FormatterFactory * formatterFactory,UErrorCode & errorCode)63 MFFunctionRegistry::Builder& MFFunctionRegistry::Builder::adoptFormatter(const FunctionName& formatterName, FormatterFactory* formatterFactory, UErrorCode& errorCode) {
64 if (U_SUCCESS(errorCode)) {
65 U_ASSERT(formatters != nullptr);
66 formatters->put(formatterName, formatterFactory, errorCode);
67 }
68 return *this;
69 }
70
setDefaultFormatterNameByType(const UnicodeString & type,const FunctionName & functionName,UErrorCode & errorCode)71 MFFunctionRegistry::Builder& MFFunctionRegistry::Builder::setDefaultFormatterNameByType(const UnicodeString& type, const FunctionName& functionName, UErrorCode& errorCode) {
72 if (U_SUCCESS(errorCode)) {
73 U_ASSERT(formattersByType != nullptr);
74 FunctionName* f = create<FunctionName>(FunctionName(functionName), errorCode);
75 formattersByType->put(type, f, errorCode);
76 }
77 return *this;
78 }
79
Builder(UErrorCode & errorCode)80 MFFunctionRegistry::Builder::Builder(UErrorCode& errorCode) {
81 CHECK_ERROR(errorCode);
82
83 formatters = new Hashtable();
84 selectors = new Hashtable();
85 formattersByType = new Hashtable();
86 if (!(formatters != nullptr && selectors != nullptr && formattersByType != nullptr)) {
87 errorCode = U_MEMORY_ALLOCATION_ERROR;
88 }
89 formatters->setValueDeleter(uprv_deleteUObject);
90 selectors->setValueDeleter(uprv_deleteUObject);
91 formattersByType->setValueDeleter(uprv_deleteUObject);
92 }
93
~Builder()94 MFFunctionRegistry::Builder::~Builder() {
95 if (formatters != nullptr) {
96 delete formatters;
97 }
98 if (selectors != nullptr) {
99 delete selectors;
100 }
101 if (formattersByType != nullptr) {
102 delete formattersByType;
103 }
104 }
105
106 // Returns non-owned pointer. Returns pointer rather than reference because it can fail.
107 // Returns non-const because FormatterFactory is mutable.
108 // TODO: This is unsafe because of the cached-formatters map
109 // (the caller could delete the resulting pointer)
getFormatter(const FunctionName & formatterName) const110 FormatterFactory* MFFunctionRegistry::getFormatter(const FunctionName& formatterName) const {
111 U_ASSERT(formatters != nullptr);
112 return static_cast<FormatterFactory*>(formatters->get(formatterName));
113 }
114
getDefaultFormatterNameByType(const UnicodeString & type,FunctionName & name) const115 UBool MFFunctionRegistry::getDefaultFormatterNameByType(const UnicodeString& type, FunctionName& name) const {
116 U_ASSERT(formatters != nullptr);
117 const FunctionName* f = static_cast<FunctionName*>(formattersByType->get(type));
118 if (f != nullptr) {
119 name = *f;
120 return true;
121 }
122 return false;
123 }
124
getSelector(const FunctionName & selectorName) const125 const SelectorFactory* MFFunctionRegistry::getSelector(const FunctionName& selectorName) const {
126 U_ASSERT(selectors != nullptr);
127 return static_cast<const SelectorFactory*>(selectors->get(selectorName));
128 }
129
hasFormatter(const FunctionName & f) const130 bool MFFunctionRegistry::hasFormatter(const FunctionName& f) const {
131 return getFormatter(f) != nullptr;
132 }
133
hasSelector(const FunctionName & s) const134 bool MFFunctionRegistry::hasSelector(const FunctionName& s) const {
135 return getSelector(s) != nullptr;
136 }
137
checkFormatter(const char * s) const138 void MFFunctionRegistry::checkFormatter(const char* s) const {
139 #if U_DEBUG
140 U_ASSERT(hasFormatter(FunctionName(UnicodeString(s))));
141 #else
142 (void) s;
143 #endif
144 }
145
checkSelector(const char * s) const146 void MFFunctionRegistry::checkSelector(const char* s) const {
147 #if U_DEBUG
148 U_ASSERT(hasSelector(FunctionName(UnicodeString(s))));
149 #else
150 (void) s;
151 #endif
152 }
153
154 // Debugging
checkStandard() const155 void MFFunctionRegistry::checkStandard() const {
156 checkFormatter("datetime");
157 checkFormatter("date");
158 checkFormatter("time");
159 checkFormatter("number");
160 checkFormatter("integer");
161 checkSelector("number");
162 checkSelector("integer");
163 checkSelector("string");
164 }
165
166 // Formatter/selector helpers
167
168 // Converts `s` to a double, indicating failure via `errorCode`
strToDouble(const UnicodeString & s,double & result,UErrorCode & errorCode)169 static void strToDouble(const UnicodeString& s, double& result, UErrorCode& errorCode) {
170 CHECK_ERROR(errorCode);
171
172 // Using en-US locale because it happens to correspond to the spec:
173 // https://github.com/unicode-org/message-format-wg/blob/main/spec/registry.md#number-operands
174 // Ideally, this should re-use the code for parsing number literals (Parser::parseUnquotedLiteral())
175 // It's hard to reuse the same code because of how parse errors work.
176 // TODO: Refactor
177 LocalPointer<NumberFormat> numberFormat(NumberFormat::createInstance(Locale("en-US"), errorCode));
178 CHECK_ERROR(errorCode);
179 icu::Formattable asNumber;
180 numberFormat->parse(s, asNumber, errorCode);
181 CHECK_ERROR(errorCode);
182 result = asNumber.getDouble(errorCode);
183 }
184
tryStringAsNumber(const Locale & locale,const Formattable & val,UErrorCode & errorCode)185 static double tryStringAsNumber(const Locale& locale, const Formattable& val, UErrorCode& errorCode) {
186 // Check for a string option, try to parse it as a number if present
187 UnicodeString tempString = val.getString(errorCode);
188 LocalPointer<NumberFormat> numberFormat(NumberFormat::createInstance(locale, errorCode));
189 if (U_SUCCESS(errorCode)) {
190 icu::Formattable asNumber;
191 numberFormat->parse(tempString, asNumber, errorCode);
192 if (U_SUCCESS(errorCode)) {
193 return asNumber.getDouble(errorCode);
194 }
195 }
196 return 0;
197 }
198
getInt64Value(const Locale & locale,const Formattable & value,UErrorCode & errorCode)199 static int64_t getInt64Value(const Locale& locale, const Formattable& value, UErrorCode& errorCode) {
200 if (U_SUCCESS(errorCode)) {
201 if (!value.isNumeric()) {
202 double doubleResult = tryStringAsNumber(locale, value, errorCode);
203 if (U_SUCCESS(errorCode)) {
204 return static_cast<int64_t>(doubleResult);
205 }
206 }
207 else {
208 int64_t result = value.getInt64(errorCode);
209 if (U_SUCCESS(errorCode)) {
210 return result;
211 }
212 }
213 }
214 // Option was numeric but couldn't be converted to int64_t -- could be overflow
215 return 0;
216 }
217
218 // Adopts its arguments
MFFunctionRegistry(FormatterMap * f,SelectorMap * s,Hashtable * byType)219 MFFunctionRegistry::MFFunctionRegistry(FormatterMap* f, SelectorMap* s, Hashtable* byType) : formatters(f), selectors(s), formattersByType(byType) {
220 U_ASSERT(f != nullptr && s != nullptr && byType != nullptr);
221 }
222
operator =(MFFunctionRegistry && other)223 MFFunctionRegistry& MFFunctionRegistry::operator=(MFFunctionRegistry&& other) noexcept {
224 cleanup();
225
226 formatters = other.formatters;
227 selectors = other.selectors;
228 formattersByType = other.formattersByType;
229 other.formatters = nullptr;
230 other.selectors = nullptr;
231 other.formattersByType = nullptr;
232
233 return *this;
234 }
235
cleanup()236 void MFFunctionRegistry::cleanup() noexcept {
237 if (formatters != nullptr) {
238 delete formatters;
239 }
240 if (selectors != nullptr) {
241 delete selectors;
242 }
243 if (formattersByType != nullptr) {
244 delete formattersByType;
245 }
246 }
247
248
~MFFunctionRegistry()249 MFFunctionRegistry::~MFFunctionRegistry() {
250 cleanup();
251 }
252
253 // Specific formatter implementations
254
255 // --------- Number
256
formatterForOptions(const Number & number,const FunctionOptions & opts,UErrorCode & status)257 /* static */ number::LocalizedNumberFormatter StandardFunctions::formatterForOptions(const Number& number,
258 const FunctionOptions& opts,
259 UErrorCode& status) {
260 number::UnlocalizedNumberFormatter nf;
261
262 using namespace number;
263
264 if (U_SUCCESS(status)) {
265 Formattable opt;
266 nf = NumberFormatter::with();
267 bool isInteger = number.isInteger;
268
269 if (isInteger) {
270 nf = nf.precision(Precision::integer());
271 }
272
273 // Notation options
274 if (!isInteger) {
275 // These options only apply to `:number`
276
277 // Default notation is simple
278 Notation notation = Notation::simple();
279 UnicodeString notationOpt = opts.getStringFunctionOption(UnicodeString("notation"));
280 if (notationOpt == UnicodeString("scientific")) {
281 notation = Notation::scientific();
282 } else if (notationOpt == UnicodeString("engineering")) {
283 notation = Notation::engineering();
284 } else if (notationOpt == UnicodeString("compact")) {
285 UnicodeString displayOpt = opts.getStringFunctionOption(UnicodeString("compactDisplay"));
286 if (displayOpt == UnicodeString("long")) {
287 notation = Notation::compactLong();
288 } else {
289 // Default is short
290 notation = Notation::compactShort();
291 }
292 } else {
293 // Already set to default
294 }
295 nf = nf.notation(notation);
296 }
297
298 // Style options -- specific to `:number`
299 if (!isInteger) {
300 if (number.usePercent(opts)) {
301 nf = nf.unit(NoUnit::percent()).scale(Scale::powerOfTen(2));
302 }
303 }
304
305 int32_t maxSignificantDigits = number.maximumSignificantDigits(opts);
306 if (!isInteger) {
307 int32_t minFractionDigits = number.minimumFractionDigits(opts);
308 int32_t maxFractionDigits = number.maximumFractionDigits(opts);
309 int32_t minSignificantDigits = number.minimumSignificantDigits(opts);
310 Precision p = Precision::unlimited();
311 bool precisionOptions = false;
312
313 // Returning -1 means the option wasn't provided
314 if (maxFractionDigits != -1 && minFractionDigits != -1) {
315 precisionOptions = true;
316 p = Precision::minMaxFraction(minFractionDigits, maxFractionDigits);
317 } else if (minFractionDigits != -1) {
318 precisionOptions = true;
319 p = Precision::minFraction(minFractionDigits);
320 } else if (maxFractionDigits != -1) {
321 precisionOptions = true;
322 p = Precision::maxFraction(maxFractionDigits);
323 }
324
325 if (minSignificantDigits != -1) {
326 precisionOptions = true;
327 p = p.minSignificantDigits(minSignificantDigits);
328 }
329 if (maxSignificantDigits != -1) {
330 precisionOptions = true;
331 p = p.maxSignificantDigits(maxSignificantDigits);
332 }
333 if (precisionOptions) {
334 nf = nf.precision(p);
335 }
336 } else {
337 // maxSignificantDigits applies to `:integer`, but the other precision options don't
338 Precision p = Precision::integer();
339 if (maxSignificantDigits != -1) {
340 p = p.maxSignificantDigits(maxSignificantDigits);
341 }
342 nf = nf.precision(p);
343 }
344
345 // All other options apply to both `:number` and `:integer`
346 int32_t minIntegerDigits = number.minimumIntegerDigits(opts);
347 nf = nf.integerWidth(IntegerWidth::zeroFillTo(minIntegerDigits));
348
349 // signDisplay
350 UnicodeString sd = opts.getStringFunctionOption(UnicodeString("signDisplay"));
351 UNumberSignDisplay signDisplay;
352 if (sd == UnicodeString("always")) {
353 signDisplay = UNumberSignDisplay::UNUM_SIGN_ALWAYS;
354 } else if (sd == UnicodeString("exceptZero")) {
355 signDisplay = UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO;
356 } else if (sd == UnicodeString("negative")) {
357 signDisplay = UNumberSignDisplay::UNUM_SIGN_NEGATIVE;
358 } else if (sd == UnicodeString("never")) {
359 signDisplay = UNumberSignDisplay::UNUM_SIGN_NEVER;
360 } else {
361 signDisplay = UNumberSignDisplay::UNUM_SIGN_AUTO;
362 }
363 nf = nf.sign(signDisplay);
364
365 // useGrouping
366 UnicodeString ug = opts.getStringFunctionOption(UnicodeString("useGrouping"));
367 UNumberGroupingStrategy grp;
368 if (ug == UnicodeString("always")) {
369 grp = UNumberGroupingStrategy::UNUM_GROUPING_ON_ALIGNED;
370 } else if (ug == UnicodeString("never")) {
371 grp = UNumberGroupingStrategy::UNUM_GROUPING_OFF;
372 } else if (ug == UnicodeString("min2")) {
373 grp = UNumberGroupingStrategy::UNUM_GROUPING_MIN2;
374 } else {
375 // Default is "auto"
376 grp = UNumberGroupingStrategy::UNUM_GROUPING_AUTO;
377 }
378 nf = nf.grouping(grp);
379
380 // numberingSystem
381 UnicodeString ns = opts.getStringFunctionOption(UnicodeString("numberingSystem"));
382 if (ns.length() > 0) {
383 ns = ns.toLower(Locale("en-US"));
384 CharString buffer;
385 // Ignore bad option values, so use a local status
386 UErrorCode localStatus = U_ZERO_ERROR;
387 // Copied from number_skeletons.cpp (helpers::parseNumberingSystemOption)
388 buffer.appendInvariantChars({false, ns.getBuffer(), ns.length()}, localStatus);
389 if (U_SUCCESS(localStatus)) {
390 LocalPointer<NumberingSystem> symbols
391 (NumberingSystem::createInstanceByName(buffer.data(), localStatus));
392 if (U_SUCCESS(localStatus)) {
393 nf = nf.adoptSymbols(symbols.orphan());
394 }
395 }
396 }
397 }
398 return nf.locale(number.locale);
399 }
400
createFormatter(const Locale & locale,UErrorCode & errorCode)401 Formatter* StandardFunctions::NumberFactory::createFormatter(const Locale& locale, UErrorCode& errorCode) {
402 NULL_ON_ERROR(errorCode);
403
404 Formatter* result = new Number(locale);
405 if (result == nullptr) {
406 errorCode = U_MEMORY_ALLOCATION_ERROR;
407 }
408 return result;
409 }
410
createFormatter(const Locale & locale,UErrorCode & errorCode)411 Formatter* StandardFunctions::IntegerFactory::createFormatter(const Locale& locale, UErrorCode& errorCode) {
412 NULL_ON_ERROR(errorCode);
413
414 Formatter* result = new Number(Number::integer(locale));
415 if (result == nullptr) {
416 errorCode = U_MEMORY_ALLOCATION_ERROR;
417 }
418 return result;
419 }
420
~IntegerFactory()421 StandardFunctions::IntegerFactory::~IntegerFactory() {}
422
notANumber(const FormattedPlaceholder & input)423 static FormattedPlaceholder notANumber(const FormattedPlaceholder& input) {
424 return FormattedPlaceholder(input, FormattedValue(UnicodeString("NaN")));
425 }
426
parseNumberLiteral(const FormattedPlaceholder & input,UErrorCode & errorCode)427 static double parseNumberLiteral(const FormattedPlaceholder& input, UErrorCode& errorCode) {
428 if (U_FAILURE(errorCode)) {
429 return {};
430 }
431
432 // Copying string to avoid GCC dangling-reference warning
433 // (although the reference is safe)
434 UnicodeString inputStr = input.asFormattable().getString(errorCode);
435 // Precondition: `input`'s source Formattable has type string
436 if (U_FAILURE(errorCode)) {
437 return {};
438 }
439
440 // Hack: Check for cases that are forbidden by the MF2 grammar
441 // but allowed by StringToDouble
442 int32_t len = inputStr.length();
443
444 if (len > 0 && ((inputStr[0] == '+')
445 || (inputStr[0] == '0' && len > 1 && inputStr[1] != '.')
446 || (inputStr[len - 1] == '.')
447 || (inputStr[0] == '.'))) {
448 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
449 return 0;
450 }
451
452 // Otherwise, convert to double using double_conversion::StringToDoubleConverter
453 using namespace double_conversion;
454 int processedCharactersCount = 0;
455 StringToDoubleConverter converter(0, 0, 0, "", "");
456 double result =
457 converter.StringToDouble(reinterpret_cast<const uint16_t*>(inputStr.getBuffer()),
458 len,
459 &processedCharactersCount);
460 if (processedCharactersCount != len) {
461 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
462 }
463 return result;
464 }
465
tryParsingNumberLiteral(const number::LocalizedNumberFormatter & nf,const FormattedPlaceholder & input,UErrorCode & errorCode)466 static FormattedPlaceholder tryParsingNumberLiteral(const number::LocalizedNumberFormatter& nf, const FormattedPlaceholder& input, UErrorCode& errorCode) {
467 double numberValue = parseNumberLiteral(input, errorCode);
468 if (U_FAILURE(errorCode)) {
469 return notANumber(input);
470 }
471
472 UErrorCode savedStatus = errorCode;
473 number::FormattedNumber result = nf.formatDouble(numberValue, errorCode);
474 // Ignore U_USING_DEFAULT_WARNING
475 if (errorCode == U_USING_DEFAULT_WARNING) {
476 errorCode = savedStatus;
477 }
478 return FormattedPlaceholder(input, FormattedValue(std::move(result)));
479 }
480
maximumFractionDigits(const FunctionOptions & opts) const481 int32_t StandardFunctions::Number::maximumFractionDigits(const FunctionOptions& opts) const {
482 Formattable opt;
483
484 if (isInteger) {
485 return 0;
486 }
487
488 if (opts.getFunctionOption(UnicodeString("maximumFractionDigits"), opt)) {
489 UErrorCode localErrorCode = U_ZERO_ERROR;
490 int64_t val = getInt64Value(locale, opt, localErrorCode);
491 if (U_SUCCESS(localErrorCode)) {
492 return static_cast<int32_t>(val);
493 }
494 }
495 // Returning -1 indicates that the option wasn't provided or was a non-integer.
496 // The caller needs to check for that case, since passing -1 to Precision::maxFraction()
497 // is an error.
498 return -1;
499 }
500
minimumFractionDigits(const FunctionOptions & opts) const501 int32_t StandardFunctions::Number::minimumFractionDigits(const FunctionOptions& opts) const {
502 Formattable opt;
503
504 if (!isInteger) {
505 if (opts.getFunctionOption(UnicodeString("minimumFractionDigits"), opt)) {
506 UErrorCode localErrorCode = U_ZERO_ERROR;
507 int64_t val = getInt64Value(locale, opt, localErrorCode);
508 if (U_SUCCESS(localErrorCode)) {
509 return static_cast<int32_t>(val);
510 }
511 }
512 }
513 // Returning -1 indicates that the option wasn't provided or was a non-integer.
514 // The caller needs to check for that case, since passing -1 to Precision::minFraction()
515 // is an error.
516 return -1;
517 }
518
minimumIntegerDigits(const FunctionOptions & opts) const519 int32_t StandardFunctions::Number::minimumIntegerDigits(const FunctionOptions& opts) const {
520 Formattable opt;
521
522 if (opts.getFunctionOption(UnicodeString("minimumIntegerDigits"), opt)) {
523 UErrorCode localErrorCode = U_ZERO_ERROR;
524 int64_t val = getInt64Value(locale, opt, localErrorCode);
525 if (U_SUCCESS(localErrorCode)) {
526 return static_cast<int32_t>(val);
527 }
528 }
529 return 0;
530 }
531
minimumSignificantDigits(const FunctionOptions & opts) const532 int32_t StandardFunctions::Number::minimumSignificantDigits(const FunctionOptions& opts) const {
533 Formattable opt;
534
535 if (!isInteger) {
536 if (opts.getFunctionOption(UnicodeString("minimumSignificantDigits"), opt)) {
537 UErrorCode localErrorCode = U_ZERO_ERROR;
538 int64_t val = getInt64Value(locale, opt, localErrorCode);
539 if (U_SUCCESS(localErrorCode)) {
540 return static_cast<int32_t>(val);
541 }
542 }
543 }
544 // Returning -1 indicates that the option wasn't provided or was a non-integer.
545 // The caller needs to check for that case, since passing -1 to Precision::minSignificantDigits()
546 // is an error.
547 return -1;
548 }
549
maximumSignificantDigits(const FunctionOptions & opts) const550 int32_t StandardFunctions::Number::maximumSignificantDigits(const FunctionOptions& opts) const {
551 Formattable opt;
552
553 if (opts.getFunctionOption(UnicodeString("maximumSignificantDigits"), opt)) {
554 UErrorCode localErrorCode = U_ZERO_ERROR;
555 int64_t val = getInt64Value(locale, opt, localErrorCode);
556 if (U_SUCCESS(localErrorCode)) {
557 return static_cast<int32_t>(val);
558 }
559 }
560 // Returning -1 indicates that the option wasn't provided or was a non-integer.
561 // The caller needs to check for that case, since passing -1 to Precision::maxSignificantDigits()
562 // is an error.
563 return -1; // Not a valid value for Precision; has to be checked
564 }
565
usePercent(const FunctionOptions & opts) const566 bool StandardFunctions::Number::usePercent(const FunctionOptions& opts) const {
567 Formattable opt;
568 if (isInteger
569 || !opts.getFunctionOption(UnicodeString("style"), opt)
570 || opt.getType() != UFMT_STRING) {
571 return false;
572 }
573 UErrorCode localErrorCode = U_ZERO_ERROR;
574 const UnicodeString& style = opt.getString(localErrorCode);
575 U_ASSERT(U_SUCCESS(localErrorCode));
576 return (style == UnicodeString("percent"));
577 }
578
integer(const Locale & loc)579 /* static */ StandardFunctions::Number StandardFunctions::Number::integer(const Locale& loc) {
580 return StandardFunctions::Number(loc, true);
581 }
582
format(FormattedPlaceholder && arg,FunctionOptions && opts,UErrorCode & errorCode) const583 FormattedPlaceholder StandardFunctions::Number::format(FormattedPlaceholder&& arg, FunctionOptions&& opts, UErrorCode& errorCode) const {
584 if (U_FAILURE(errorCode)) {
585 return {};
586 }
587
588 // No argument => return "NaN"
589 if (!arg.canFormat()) {
590 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
591 return notANumber(arg);
592 }
593
594 number::LocalizedNumberFormatter realFormatter;
595 realFormatter = formatterForOptions(*this, opts, errorCode);
596
597 number::FormattedNumber numberResult;
598 if (U_SUCCESS(errorCode)) {
599 // Already checked that contents can be formatted
600 const Formattable& toFormat = arg.asFormattable();
601 switch (toFormat.getType()) {
602 case UFMT_DOUBLE: {
603 double d = toFormat.getDouble(errorCode);
604 U_ASSERT(U_SUCCESS(errorCode));
605 numberResult = realFormatter.formatDouble(d, errorCode);
606 break;
607 }
608 case UFMT_LONG: {
609 int32_t l = toFormat.getLong(errorCode);
610 U_ASSERT(U_SUCCESS(errorCode));
611 numberResult = realFormatter.formatInt(l, errorCode);
612 break;
613 }
614 case UFMT_INT64: {
615 int64_t i = toFormat.getInt64(errorCode);
616 U_ASSERT(U_SUCCESS(errorCode));
617 numberResult = realFormatter.formatInt(i, errorCode);
618 break;
619 }
620 case UFMT_STRING: {
621 // Try to parse the string as a number
622 return tryParsingNumberLiteral(realFormatter, arg, errorCode);
623 }
624 default: {
625 // Other types can't be parsed as a number
626 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
627 return notANumber(arg);
628 }
629 }
630 }
631
632 return FormattedPlaceholder(arg, FormattedValue(std::move(numberResult)));
633 }
634
~Number()635 StandardFunctions::Number::~Number() {}
~NumberFactory()636 StandardFunctions::NumberFactory::~NumberFactory() {}
637
638 // --------- PluralFactory
639
640
pluralType(const FunctionOptions & opts) const641 StandardFunctions::Plural::PluralType StandardFunctions::Plural::pluralType(const FunctionOptions& opts) const {
642 Formattable opt;
643
644 if (opts.getFunctionOption(UnicodeString("select"), opt)) {
645 UErrorCode localErrorCode = U_ZERO_ERROR;
646 UnicodeString val = opt.getString(localErrorCode);
647 if (U_SUCCESS(localErrorCode)) {
648 if (val == UnicodeString("ordinal")) {
649 return PluralType::PLURAL_ORDINAL;
650 }
651 if (val == UnicodeString("exact")) {
652 return PluralType::PLURAL_EXACT;
653 }
654 }
655 }
656 return PluralType::PLURAL_CARDINAL;
657 }
658
createSelector(const Locale & locale,UErrorCode & errorCode) const659 Selector* StandardFunctions::PluralFactory::createSelector(const Locale& locale, UErrorCode& errorCode) const {
660 NULL_ON_ERROR(errorCode);
661
662 Selector* result;
663 if (isInteger) {
664 result = new Plural(Plural::integer(locale, errorCode));
665 } else {
666 result = new Plural(locale, errorCode);
667 }
668 NULL_ON_ERROR(errorCode);
669 if (result == nullptr) {
670 errorCode = U_MEMORY_ALLOCATION_ERROR;
671 }
672 return result;
673 }
674
selectKey(FormattedPlaceholder && toFormat,FunctionOptions && opts,const UnicodeString * keys,int32_t keysLen,UnicodeString * prefs,int32_t & prefsLen,UErrorCode & errorCode) const675 void StandardFunctions::Plural::selectKey(FormattedPlaceholder&& toFormat,
676 FunctionOptions&& opts,
677 const UnicodeString* keys,
678 int32_t keysLen,
679 UnicodeString* prefs,
680 int32_t& prefsLen,
681 UErrorCode& errorCode) const {
682 CHECK_ERROR(errorCode);
683
684 // No argument => return "NaN"
685 if (!toFormat.canFormat()) {
686 errorCode = U_MF_SELECTOR_ERROR;
687 return;
688 }
689
690 // Handle any formatting options
691 PluralType type = pluralType(opts);
692 FormattedPlaceholder resolvedSelector = numberFormatter->format(std::move(toFormat),
693 std::move(opts),
694 errorCode);
695 CHECK_ERROR(errorCode);
696
697 U_ASSERT(resolvedSelector.isEvaluated() && resolvedSelector.output().isNumber());
698
699 // See https://github.com/unicode-org/message-format-wg/blob/main/spec/registry.md#number-selection
700 // 1. Let exact be the JSON string representation of the numeric value of resolvedSelector
701 const number::FormattedNumber& formattedNumber = resolvedSelector.output().getNumber();
702 UnicodeString exact = formattedNumber.toString(errorCode);
703
704 if (U_FAILURE(errorCode)) {
705 // Non-number => selector error
706 errorCode = U_MF_SELECTOR_ERROR;
707 return;
708 }
709
710 // Step 2. Let keyword be a string which is the result of rule selection on resolvedSelector.
711 // If the option select is set to exact, rule-based selection is not used. Return the empty string.
712 UnicodeString keyword;
713 if (type != PluralType::PLURAL_EXACT) {
714 UPluralType t = type == PluralType::PLURAL_ORDINAL ? UPLURAL_TYPE_ORDINAL : UPLURAL_TYPE_CARDINAL;
715 // Look up plural rules by locale and type
716 LocalPointer<PluralRules> rules(PluralRules::forLocale(locale, t, errorCode));
717 CHECK_ERROR(errorCode);
718
719 keyword = rules->select(formattedNumber, errorCode);
720 }
721
722 // Steps 3-4 elided:
723 // 3. Let resultExact be a new empty list of strings.
724 // 4. Let resultKeyword be a new empty list of strings.
725 // Instead, we use `prefs` the concatenation of `resultExact`
726 // and `resultKeyword`.
727
728 prefsLen = 0;
729
730 // 5. For each string key in keys:
731 double keyAsDouble = 0;
732 for (int32_t i = 0; i < keysLen; i++) {
733 // Try parsing the key as a double
734 UErrorCode localErrorCode = U_ZERO_ERROR;
735 strToDouble(keys[i], keyAsDouble, localErrorCode);
736 // 5i. If the value of key matches the production number-literal, then
737 if (U_SUCCESS(localErrorCode)) {
738 // 5i(a). If key and exact consist of the same sequence of Unicode code points, then
739 if (exact == keys[i]) {
740 // 5i(a)(a) Append key as the last element of the list resultExact.
741 prefs[prefsLen] = keys[i];
742 prefsLen++;
743 break;
744 }
745 }
746 }
747
748 // Return immediately if exact matching was requested
749 if (prefsLen == keysLen || type == PluralType::PLURAL_EXACT) {
750 return;
751 }
752
753
754 for (int32_t i = 0; i < keysLen; i ++) {
755 if (prefsLen >= keysLen) {
756 break;
757 }
758 // 5ii. Else if key is one of the keywords zero, one, two, few, many, or other, then
759 // 5ii(a). If key and keyword consist of the same sequence of Unicode code points, then
760 if (keyword == keys[i]) {
761 // 5ii(a)(a) Append key as the last element of the list resultKeyword.
762 prefs[prefsLen] = keys[i];
763 prefsLen++;
764 }
765 }
766
767 // Note: Step 5(iii) "Else, emit a Selection Error" is omitted in both loops
768
769 // 6. Return a new list whose elements are the concatenation of the elements
770 // (in order) of resultExact followed by the elements (in order) of resultKeyword.
771 // (Implicit, since `prefs` is an out-parameter)
772 }
773
Plural(const Locale & loc,UErrorCode & status)774 StandardFunctions::Plural::Plural(const Locale& loc, UErrorCode& status) : locale(loc) {
775 CHECK_ERROR(status);
776
777 numberFormatter.adoptInstead(new StandardFunctions::Number(loc));
778 if (!numberFormatter.isValid()) {
779 status = U_MEMORY_ALLOCATION_ERROR;
780 }
781 }
782
Plural(const Locale & loc,bool isInt,UErrorCode & status)783 StandardFunctions::Plural::Plural(const Locale& loc, bool isInt, UErrorCode& status) : locale(loc), isInteger(isInt) {
784 CHECK_ERROR(status);
785
786 if (isInteger) {
787 numberFormatter.adoptInstead(new StandardFunctions::Number(loc, true));
788 } else {
789 numberFormatter.adoptInstead(new StandardFunctions::Number(loc));
790 }
791
792 if (!numberFormatter.isValid()) {
793 status = U_MEMORY_ALLOCATION_ERROR;
794 }
795 }
796
~Plural()797 StandardFunctions::Plural::~Plural() {}
798
~PluralFactory()799 StandardFunctions::PluralFactory::~PluralFactory() {}
800
801 // --------- DateTimeFactory
802
getStringOption(const FunctionOptions & opts,const UnicodeString & optionName,UErrorCode & errorCode)803 /* static */ UnicodeString StandardFunctions::getStringOption(const FunctionOptions& opts,
804 const UnicodeString& optionName,
805 UErrorCode& errorCode) {
806 if (U_SUCCESS(errorCode)) {
807 Formattable opt;
808 if (opts.getFunctionOption(optionName, opt)) {
809 return opt.getString(errorCode); // In case it's not a string, error code will be set
810 } else {
811 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
812 }
813 }
814 // Default is empty string
815 return {};
816 }
817
818 // Date/time options only
defaultForOption(const UnicodeString & optionName)819 static UnicodeString defaultForOption(const UnicodeString& optionName) {
820 if (optionName == UnicodeString("dateStyle")
821 || optionName == UnicodeString("timeStyle")
822 || optionName == UnicodeString("style")) {
823 return UnicodeString("short");
824 }
825 return {}; // Empty string is default
826 }
827
828 // TODO
829 // Only DateTime currently uses the function options stored in the placeholder.
830 // It also doesn't use them very consistently (it looks at the previous set of options,
831 // and others aren't preserved). This needs to be generalized,
832 // but that depends on https://github.com/unicode-org/message-format-wg/issues/515
833 // Finally, the option value is assumed to be a string,
834 // which works for datetime options but not necessarily in general.
getFunctionOption(const FormattedPlaceholder & toFormat,const FunctionOptions & opts,const UnicodeString & optionName) const835 UnicodeString StandardFunctions::DateTime::getFunctionOption(const FormattedPlaceholder& toFormat,
836 const FunctionOptions& opts,
837 const UnicodeString& optionName) const {
838 // Options passed to the current function invocation take priority
839 Formattable opt;
840 UnicodeString s;
841 UErrorCode localErrorCode = U_ZERO_ERROR;
842 s = getStringOption(opts, optionName, localErrorCode);
843 if (U_SUCCESS(localErrorCode)) {
844 return s;
845 }
846 // Next try the set of options used to construct `toFormat`
847 localErrorCode = U_ZERO_ERROR;
848 s = getStringOption(toFormat.options(), optionName, localErrorCode);
849 if (U_SUCCESS(localErrorCode)) {
850 return s;
851 }
852 // Finally, use default
853 return defaultForOption(optionName);
854 }
855
856 // Used for options that don't have defaults
getFunctionOption(const FormattedPlaceholder & toFormat,const FunctionOptions & opts,const UnicodeString & optionName,UErrorCode & errorCode) const857 UnicodeString StandardFunctions::DateTime::getFunctionOption(const FormattedPlaceholder& toFormat,
858 const FunctionOptions& opts,
859 const UnicodeString& optionName,
860 UErrorCode& errorCode) const {
861 if (U_SUCCESS(errorCode)) {
862 // Options passed to the current function invocation take priority
863 Formattable opt;
864 UnicodeString s;
865 UErrorCode localErrorCode = U_ZERO_ERROR;
866 s = getStringOption(opts, optionName, localErrorCode);
867 if (U_SUCCESS(localErrorCode)) {
868 return s;
869 }
870 // Next try the set of options used to construct `toFormat`
871 localErrorCode = U_ZERO_ERROR;
872 s = getStringOption(toFormat.options(), optionName, localErrorCode);
873 if (U_SUCCESS(localErrorCode)) {
874 return s;
875 }
876 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
877 }
878 return {};
879 }
880
stringToStyle(UnicodeString option,UErrorCode & errorCode)881 static DateFormat::EStyle stringToStyle(UnicodeString option, UErrorCode& errorCode) {
882 if (U_SUCCESS(errorCode)) {
883 UnicodeString upper = option.toUpper();
884 if (upper == UnicodeString("FULL")) {
885 return DateFormat::EStyle::kFull;
886 }
887 if (upper == UnicodeString("LONG")) {
888 return DateFormat::EStyle::kLong;
889 }
890 if (upper == UnicodeString("MEDIUM")) {
891 return DateFormat::EStyle::kMedium;
892 }
893 if (upper == UnicodeString("SHORT")) {
894 return DateFormat::EStyle::kShort;
895 }
896 if (upper.isEmpty() || upper == UnicodeString("DEFAULT")) {
897 return DateFormat::EStyle::kDefault;
898 }
899 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
900 }
901 return DateFormat::EStyle::kNone;
902 }
903
dateTime(UErrorCode & errorCode)904 /* static */ StandardFunctions::DateTimeFactory* StandardFunctions::DateTimeFactory::dateTime(UErrorCode& errorCode) {
905 NULL_ON_ERROR(errorCode);
906
907 DateTimeFactory* result = new StandardFunctions::DateTimeFactory(DateTimeType::DateTime);
908 if (result == nullptr) {
909 errorCode = U_MEMORY_ALLOCATION_ERROR;
910 }
911 return result;
912 }
913
date(UErrorCode & errorCode)914 /* static */ StandardFunctions::DateTimeFactory* StandardFunctions::DateTimeFactory::date(UErrorCode& errorCode) {
915 NULL_ON_ERROR(errorCode);
916
917 DateTimeFactory* result = new DateTimeFactory(DateTimeType::Date);
918 if (result == nullptr) {
919 errorCode = U_MEMORY_ALLOCATION_ERROR;
920 }
921 return result;
922 }
923
time(UErrorCode & errorCode)924 /* static */ StandardFunctions::DateTimeFactory* StandardFunctions::DateTimeFactory::time(UErrorCode& errorCode) {
925 NULL_ON_ERROR(errorCode);
926
927 DateTimeFactory* result = new DateTimeFactory(DateTimeType::Time);
928 if (result == nullptr) {
929 errorCode = U_MEMORY_ALLOCATION_ERROR;
930 }
931 return result;
932 }
933
createFormatter(const Locale & locale,UErrorCode & errorCode)934 Formatter* StandardFunctions::DateTimeFactory::createFormatter(const Locale& locale, UErrorCode& errorCode) {
935 NULL_ON_ERROR(errorCode);
936
937 Formatter* result = new StandardFunctions::DateTime(locale, type);
938 if (result == nullptr) {
939 errorCode = U_MEMORY_ALLOCATION_ERROR;
940 }
941 return result;
942 }
943
format(FormattedPlaceholder && toFormat,FunctionOptions && opts,UErrorCode & errorCode) const944 FormattedPlaceholder StandardFunctions::DateTime::format(FormattedPlaceholder&& toFormat,
945 FunctionOptions&& opts,
946 UErrorCode& errorCode) const {
947 if (U_FAILURE(errorCode)) {
948 return {};
949 }
950
951 // Argument must be present
952 if (!toFormat.canFormat()) {
953 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
954 return std::move(toFormat);
955 }
956
957 LocalPointer<DateFormat> df;
958 Formattable opt;
959
960 DateFormat::EStyle dateStyle = DateFormat::kShort;
961 DateFormat::EStyle timeStyle = DateFormat::kShort;
962
963 UnicodeString dateStyleName("dateStyle");
964 UnicodeString timeStyleName("timeStyle");
965 UnicodeString styleName("style");
966
967 bool hasDateStyleOption = opts.getFunctionOption(dateStyleName, opt);
968 bool hasTimeStyleOption = opts.getFunctionOption(timeStyleName, opt);
969 bool noOptions = opts.optionsCount() == 0;
970
971 bool useStyle = (type == DateTimeFactory::DateTimeType::DateTime
972 && (hasDateStyleOption || hasTimeStyleOption
973 || noOptions))
974 || (type != DateTimeFactory::DateTimeType::DateTime);
975
976 bool useDate = type == DateTimeFactory::DateTimeType::Date
977 || (type == DateTimeFactory::DateTimeType::DateTime
978 && hasDateStyleOption);
979 bool useTime = type == DateTimeFactory::DateTimeType::Time
980 || (type == DateTimeFactory::DateTimeType::DateTime
981 && hasTimeStyleOption);
982
983 if (useStyle) {
984 // Extract style options
985 if (type == DateTimeFactory::DateTimeType::DateTime) {
986 // Note that the options-getting has to be repeated across the three cases,
987 // since `:datetime` uses "dateStyle"/"timeStyle" and `:date` and `:time`
988 // use "style"
989 dateStyle = stringToStyle(getFunctionOption(toFormat, opts, dateStyleName), errorCode);
990 timeStyle = stringToStyle(getFunctionOption(toFormat, opts, timeStyleName), errorCode);
991
992 if (useDate && !useTime) {
993 df.adoptInstead(DateFormat::createDateInstance(dateStyle, locale));
994 } else if (useTime && !useDate) {
995 df.adoptInstead(DateFormat::createTimeInstance(timeStyle, locale));
996 } else {
997 df.adoptInstead(DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale));
998 }
999 } else if (type == DateTimeFactory::DateTimeType::Date) {
1000 dateStyle = stringToStyle(getFunctionOption(toFormat, opts, styleName), errorCode);
1001 df.adoptInstead(DateFormat::createDateInstance(dateStyle, locale));
1002 } else {
1003 // :time
1004 timeStyle = stringToStyle(getFunctionOption(toFormat, opts, styleName), errorCode);
1005 df.adoptInstead(DateFormat::createTimeInstance(timeStyle, locale));
1006 }
1007 } else {
1008 // Build up a skeleton based on the field options, then use that to
1009 // create the date formatter
1010
1011 UnicodeString skeleton;
1012 #define ADD_PATTERN(s) skeleton += UnicodeString(s)
1013 if (U_SUCCESS(errorCode)) {
1014 // Year
1015 UnicodeString year = getFunctionOption(toFormat, opts, UnicodeString("year"), errorCode);
1016 if (U_FAILURE(errorCode)) {
1017 errorCode = U_ZERO_ERROR;
1018 } else {
1019 useDate = true;
1020 if (year == UnicodeString("2-digit")) {
1021 ADD_PATTERN("YY");
1022 } else if (year == UnicodeString("numeric")) {
1023 ADD_PATTERN("YYYY");
1024 }
1025 }
1026 // Month
1027 UnicodeString month = getFunctionOption(toFormat, opts, UnicodeString("month"), errorCode);
1028 if (U_FAILURE(errorCode)) {
1029 errorCode = U_ZERO_ERROR;
1030 } else {
1031 useDate = true;
1032 /* numeric, 2-digit, long, short, narrow */
1033 if (month == UnicodeString("long")) {
1034 ADD_PATTERN("MMMM");
1035 } else if (month == UnicodeString("short")) {
1036 ADD_PATTERN("MMM");
1037 } else if (month == UnicodeString("narrow")) {
1038 ADD_PATTERN("MMMMM");
1039 } else if (month == UnicodeString("numeric")) {
1040 ADD_PATTERN("M");
1041 } else if (month == UnicodeString("2-digit")) {
1042 ADD_PATTERN("MM");
1043 }
1044 }
1045 // Weekday
1046 UnicodeString weekday = getFunctionOption(toFormat, opts, UnicodeString("weekday"), errorCode);
1047 if (U_FAILURE(errorCode)) {
1048 errorCode = U_ZERO_ERROR;
1049 } else {
1050 useDate = true;
1051 if (weekday == UnicodeString("long")) {
1052 ADD_PATTERN("EEEE");
1053 } else if (weekday == UnicodeString("short")) {
1054 ADD_PATTERN("EEEEE");
1055 } else if (weekday == UnicodeString("narrow")) {
1056 ADD_PATTERN("EEEEE");
1057 }
1058 }
1059 // Day
1060 UnicodeString day = getFunctionOption(toFormat, opts, UnicodeString("day"), errorCode);
1061 if (U_FAILURE(errorCode)) {
1062 errorCode = U_ZERO_ERROR;
1063 } else {
1064 useDate = true;
1065 if (day == UnicodeString("numeric")) {
1066 ADD_PATTERN("d");
1067 } else if (day == UnicodeString("2-digit")) {
1068 ADD_PATTERN("dd");
1069 }
1070 }
1071 // Hour
1072 UnicodeString hour = getFunctionOption(toFormat, opts, UnicodeString("hour"), errorCode);
1073 if (U_FAILURE(errorCode)) {
1074 errorCode = U_ZERO_ERROR;
1075 } else {
1076 useTime = true;
1077 if (hour == UnicodeString("numeric")) {
1078 ADD_PATTERN("h");
1079 } else if (hour == UnicodeString("2-digit")) {
1080 ADD_PATTERN("hh");
1081 }
1082 }
1083 // Minute
1084 UnicodeString minute = getFunctionOption(toFormat, opts, UnicodeString("minute"), errorCode);
1085 if (U_FAILURE(errorCode)) {
1086 errorCode = U_ZERO_ERROR;
1087 } else {
1088 useTime = true;
1089 if (minute == UnicodeString("numeric")) {
1090 ADD_PATTERN("m");
1091 } else if (minute == UnicodeString("2-digit")) {
1092 ADD_PATTERN("mm");
1093 }
1094 }
1095 // Second
1096 UnicodeString second = getFunctionOption(toFormat, opts, UnicodeString("second"), errorCode);
1097 if (U_FAILURE(errorCode)) {
1098 errorCode = U_ZERO_ERROR;
1099 } else {
1100 useTime = true;
1101 if (second == UnicodeString("numeric")) {
1102 ADD_PATTERN("s");
1103 } else if (second == UnicodeString("2-digit")) {
1104 ADD_PATTERN("ss");
1105 }
1106 }
1107 }
1108 /*
1109 TODO
1110 fractionalSecondDigits
1111 hourCycle
1112 timeZoneName
1113 era
1114 */
1115 df.adoptInstead(DateFormat::createInstanceForSkeleton(skeleton, errorCode));
1116 }
1117
1118 if (U_FAILURE(errorCode)) {
1119 return {};
1120 }
1121 if (!df.isValid()) {
1122 errorCode = U_MEMORY_ALLOCATION_ERROR;
1123 return {};
1124 }
1125
1126 UnicodeString result;
1127 const Formattable& source = toFormat.asFormattable();
1128 switch (source.getType()) {
1129 case UFMT_STRING: {
1130 const UnicodeString& sourceStr = source.getString(errorCode);
1131 U_ASSERT(U_SUCCESS(errorCode));
1132 // Pattern for ISO 8601 format - datetime
1133 UnicodeString pattern("YYYY-MM-dd'T'HH:mm:ss");
1134 LocalPointer<DateFormat> dateParser(new SimpleDateFormat(pattern, errorCode));
1135 if (U_FAILURE(errorCode)) {
1136 errorCode = U_MF_FORMATTING_ERROR;
1137 } else {
1138 // Parse the date
1139 UDate d = dateParser->parse(sourceStr, errorCode);
1140 if (U_FAILURE(errorCode)) {
1141 // Pattern for ISO 8601 format - date
1142 UnicodeString pattern("YYYY-MM-dd");
1143 errorCode = U_ZERO_ERROR;
1144 dateParser.adoptInstead(new SimpleDateFormat(pattern, errorCode));
1145 if (U_FAILURE(errorCode)) {
1146 errorCode = U_MF_FORMATTING_ERROR;
1147 } else {
1148 d = dateParser->parse(sourceStr, errorCode);
1149 if (U_FAILURE(errorCode)) {
1150 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
1151 }
1152 }
1153 }
1154 // Use the parsed date as the source value
1155 // in the returned FormattedPlaceholder; this is necessary
1156 // so the date can be re-formatted
1157 toFormat = FormattedPlaceholder(message2::Formattable::forDate(d),
1158 toFormat.getFallback());
1159 df->format(d, result, 0, errorCode);
1160 }
1161 break;
1162 }
1163 case UFMT_DATE: {
1164 df->format(source.asICUFormattable(errorCode), result, 0, errorCode);
1165 if (U_FAILURE(errorCode)) {
1166 if (errorCode == U_ILLEGAL_ARGUMENT_ERROR) {
1167 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
1168 }
1169 }
1170 break;
1171 }
1172 // Any other cases are an error
1173 default: {
1174 errorCode = U_MF_OPERAND_MISMATCH_ERROR;
1175 break;
1176 }
1177 }
1178 if (U_FAILURE(errorCode)) {
1179 return {};
1180 }
1181 return FormattedPlaceholder(toFormat, std::move(opts), FormattedValue(std::move(result)));
1182 }
1183
~DateTimeFactory()1184 StandardFunctions::DateTimeFactory::~DateTimeFactory() {}
~DateTime()1185 StandardFunctions::DateTime::~DateTime() {}
1186
1187 // --------- TextFactory
1188
createSelector(const Locale & locale,UErrorCode & errorCode) const1189 Selector* StandardFunctions::TextFactory::createSelector(const Locale& locale, UErrorCode& errorCode) const {
1190 Selector* result = new TextSelector(locale);
1191 if (result == nullptr) {
1192 errorCode = U_MEMORY_ALLOCATION_ERROR;
1193 return nullptr;
1194 }
1195 return result;
1196 }
1197
selectKey(FormattedPlaceholder && toFormat,FunctionOptions && opts,const UnicodeString * keys,int32_t keysLen,UnicodeString * prefs,int32_t & prefsLen,UErrorCode & errorCode) const1198 void StandardFunctions::TextSelector::selectKey(FormattedPlaceholder&& toFormat,
1199 FunctionOptions&& opts,
1200 const UnicodeString* keys,
1201 int32_t keysLen,
1202 UnicodeString* prefs,
1203 int32_t& prefsLen,
1204 UErrorCode& errorCode) const {
1205 // No options
1206 (void) opts;
1207
1208 CHECK_ERROR(errorCode);
1209
1210 // Just compares the key and value as strings
1211
1212 // Argument must be present
1213 if (!toFormat.canFormat()) {
1214 errorCode = U_MF_SELECTOR_ERROR;
1215 return;
1216 }
1217
1218 prefsLen = 0;
1219
1220 // Convert to string
1221 const UnicodeString& formattedValue = toFormat.formatToString(locale, errorCode);
1222 if (U_FAILURE(errorCode)) {
1223 return;
1224 }
1225
1226 for (int32_t i = 0; i < keysLen; i++) {
1227 if (keys[i] == formattedValue) {
1228 prefs[0] = keys[i];
1229 prefsLen = 1;
1230 break;
1231 }
1232 }
1233 }
1234
~TextFactory()1235 StandardFunctions::TextFactory::~TextFactory() {}
~TextSelector()1236 StandardFunctions::TextSelector::~TextSelector() {}
1237
1238 } // namespace message2
1239 U_NAMESPACE_END
1240
1241 #endif /* #if !UCONFIG_NO_MF2 */
1242
1243 #endif /* #if !UCONFIG_NO_FORMATTING */
1244
1245