• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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