Bug Summary

File:root/firefox-clang/intl/icu/source/i18n/msgfmt.cpp
Warning:line 1405, column 13
Value stored to 'prevIndex' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name msgfmt.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D U_I18N_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -I /root/firefox-clang/intl/icu/source/common -I /root/firefox-clang/mfbt/double-conversion -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/intl/icu/source/i18n/msgfmt.cpp
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 1997-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************
8 *
9 * File MSGFMT.CPP
10 *
11 * Modification History:
12 *
13 * Date Name Description
14 * 02/19/97 aliu Converted from java.
15 * 03/20/97 helena Finished first cut of implementation.
16 * 04/10/97 aliu Made to work on AIX. Added stoi to replace wtoi.
17 * 06/11/97 helena Fixed addPattern to take the pattern correctly.
18 * 06/17/97 helena Fixed the getPattern to return the correct pattern.
19 * 07/09/97 helena Made ParsePosition into a class.
20 * 02/22/99 stephen Removed character literals for EBCDIC safety
21 * 11/01/09 kirtig Added SelectFormat
22 ********************************************************************/
23
24#include "unicode/utypes.h"
25
26#if !UCONFIG_NO_FORMATTING0
27
28#include "unicode/appendable.h"
29#include "unicode/choicfmt.h"
30#include "unicode/datefmt.h"
31#include "unicode/decimfmt.h"
32#include "unicode/localpointer.h"
33#include "unicode/msgfmt.h"
34#include "unicode/numberformatter.h"
35#include "unicode/plurfmt.h"
36#include "unicode/rbnf.h"
37#include "unicode/selfmt.h"
38#include "unicode/smpdtfmt.h"
39#include "unicode/umsg.h"
40#include "unicode/ustring.h"
41#include "cmemory.h"
42#include "patternprops.h"
43#include "messageimpl.h"
44#include "msgfmt_impl.h"
45#include "plurrule_impl.h"
46#include "uassert.h"
47#include "uelement.h"
48#include "uhash.h"
49#include "ustrfmt.h"
50#include "util.h"
51#include "uvector.h"
52#include "number_decimalquantity.h"
53
54// *****************************************************************************
55// class MessageFormat
56// *****************************************************************************
57
58#define SINGLE_QUOTE((char16_t)0x0027) ((char16_t)0x0027)
59#define COMMA((char16_t)0x002C) ((char16_t)0x002C)
60#define LEFT_CURLY_BRACE((char16_t)0x007B) ((char16_t)0x007B)
61#define RIGHT_CURLY_BRACE((char16_t)0x007D) ((char16_t)0x007D)
62
63//---------------------------------------
64// static data
65
66static const char16_t ID_NUMBER[] = {
67 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */
68};
69static const char16_t ID_DATE[] = {
70 0x64, 0x61, 0x74, 0x65, 0 /* "date" */
71};
72static const char16_t ID_TIME[] = {
73 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */
74};
75static const char16_t ID_SPELLOUT[] = {
76 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */
77};
78static const char16_t ID_ORDINAL[] = {
79 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */
80};
81static const char16_t ID_DURATION[] = {
82 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */
83};
84
85// MessageFormat Type List Number, Date, Time or Choice
86static const char16_t * const TYPE_IDS[] = {
87 ID_NUMBER,
88 ID_DATE,
89 ID_TIME,
90 ID_SPELLOUT,
91 ID_ORDINAL,
92 ID_DURATION,
93 nullptr,
94};
95
96static const char16_t ID_EMPTY[] = {
97 0 /* empty string, used for default so that null can mark end of list */
98};
99static const char16_t ID_CURRENCY[] = {
100 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */
101};
102static const char16_t ID_PERCENT[] = {
103 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */
104};
105static const char16_t ID_INTEGER[] = {
106 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */
107};
108
109// NumberFormat modifier list, default, currency, percent or integer
110static const char16_t * const NUMBER_STYLE_IDS[] = {
111 ID_EMPTY,
112 ID_CURRENCY,
113 ID_PERCENT,
114 ID_INTEGER,
115 nullptr,
116};
117
118static const char16_t ID_SHORT[] = {
119 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */
120};
121static const char16_t ID_MEDIUM[] = {
122 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */
123};
124static const char16_t ID_LONG[] = {
125 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */
126};
127static const char16_t ID_FULL[] = {
128 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */
129};
130
131// DateFormat modifier list, default, short, medium, long or full
132static const char16_t * const DATE_STYLE_IDS[] = {
133 ID_EMPTY,
134 ID_SHORT,
135 ID_MEDIUM,
136 ID_LONG,
137 ID_FULL,
138 nullptr,
139};
140
141static const icu::DateFormat::EStyle DATE_STYLES[] = {
142 icu::DateFormat::kDefault,
143 icu::DateFormat::kShort,
144 icu::DateFormat::kMedium,
145 icu::DateFormat::kLong,
146 icu::DateFormat::kFull,
147};
148
149static const int32_t DEFAULT_INITIAL_CAPACITY = 10;
150
151static const char16_t NULL_STRING[] = {
152 0x6E, 0x75, 0x6C, 0x6C, 0 // "null"
153};
154
155static const char16_t OTHER_STRING[] = {
156 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other"
157};
158
159U_CDECL_BEGINextern "C" {
160static UBool U_CALLCONV equalFormatsForHash(const UHashTok key1,
161 const UHashTok key2) {
162 return icu::MessageFormat::equalFormats(key1.pointer, key2.pointer);
163}
164
165U_CDECL_END}
166
167U_NAMESPACE_BEGINnamespace icu_77 {
168
169// -------------------------------------
170UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat)UClassID MessageFormat::getStaticClassID() { static char classID
= 0; return (UClassID)&classID; } UClassID MessageFormat
::getDynamicClassID() const { return MessageFormat::getStaticClassID
(); }
171UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration)UClassID FormatNameEnumeration::getStaticClassID() { static char
classID = 0; return (UClassID)&classID; } UClassID FormatNameEnumeration
::getDynamicClassID() const { return FormatNameEnumeration::getStaticClassID
(); }
172
173//--------------------------------------------------------------------
174
175/**
176 * Convert an integer value to a string and append the result to
177 * the given UnicodeString.
178 */
179static UnicodeString& itos(int32_t i, UnicodeString& appendTo) {
180 char16_t temp[16];
181 uprv_itouuprv_itou_77(temp,16,i,10,0); // 10 == radix
182 appendTo.append(temp, -1);
183 return appendTo;
184}
185
186
187// AppendableWrapper: encapsulates the result of formatting, keeping track
188// of the string and its length.
189class AppendableWrapper : public UMemory {
190public:
191 AppendableWrapper(Appendable& appendable) : app(appendable), len(0) {
192 }
193 void append(const UnicodeString& s) {
194 app.appendString(s.getBuffer(), s.length());
195 len += s.length();
196 }
197 void append(const char16_t* s, const int32_t sLength) {
198 app.appendString(s, sLength);
199 len += sLength;
200 }
201 void append(const UnicodeString& s, int32_t start, int32_t length) {
202 append(s.tempSubString(start, length));
203 }
204 void formatAndAppend(const Format* formatter, const Formattable& arg, UErrorCode& ec) {
205 UnicodeString s;
206 formatter->format(arg, s, ec);
207 if (U_SUCCESS(ec)) {
208 append(s);
209 }
210 }
211 void formatAndAppend(const Format* formatter, const Formattable& arg,
212 const UnicodeString &argString, UErrorCode& ec) {
213 if (!argString.isEmpty()) {
214 if (U_SUCCESS(ec)) {
215 append(argString);
216 }
217 } else {
218 formatAndAppend(formatter, arg, ec);
219 }
220 }
221 int32_t length() {
222 return len;
223 }
224private:
225 Appendable& app;
226 int32_t len;
227};
228
229
230// -------------------------------------
231// Creates a MessageFormat instance based on the pattern.
232
233MessageFormat::MessageFormat(const UnicodeString& pattern,
234 UErrorCode& success)
235: fLocale(Locale::getDefault()), // Uses the default locale
236 msgPattern(success),
237 formatAliases(nullptr),
238 formatAliasesCapacity(0),
239 argTypes(nullptr),
240 argTypeCount(0),
241 argTypeCapacity(0),
242 hasArgTypeConflicts(false),
243 defaultNumberFormat(nullptr),
244 defaultDateFormat(nullptr),
245 cachedFormatters(nullptr),
246 customFormatArgStarts(nullptr),
247 pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
248 ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
249{
250 setLocaleIDs(fLocale.getName(), fLocale.getName());
251 applyPattern(pattern, success);
252}
253
254MessageFormat::MessageFormat(const UnicodeString& pattern,
255 const Locale& newLocale,
256 UErrorCode& success)
257: fLocale(newLocale),
258 msgPattern(success),
259 formatAliases(nullptr),
260 formatAliasesCapacity(0),
261 argTypes(nullptr),
262 argTypeCount(0),
263 argTypeCapacity(0),
264 hasArgTypeConflicts(false),
265 defaultNumberFormat(nullptr),
266 defaultDateFormat(nullptr),
267 cachedFormatters(nullptr),
268 customFormatArgStarts(nullptr),
269 pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
270 ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
271{
272 setLocaleIDs(fLocale.getName(), fLocale.getName());
273 applyPattern(pattern, success);
274}
275
276MessageFormat::MessageFormat(const UnicodeString& pattern,
277 const Locale& newLocale,
278 UParseError& parseError,
279 UErrorCode& success)
280: fLocale(newLocale),
281 msgPattern(success),
282 formatAliases(nullptr),
283 formatAliasesCapacity(0),
284 argTypes(nullptr),
285 argTypeCount(0),
286 argTypeCapacity(0),
287 hasArgTypeConflicts(false),
288 defaultNumberFormat(nullptr),
289 defaultDateFormat(nullptr),
290 cachedFormatters(nullptr),
291 customFormatArgStarts(nullptr),
292 pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
293 ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
294{
295 setLocaleIDs(fLocale.getName(), fLocale.getName());
296 applyPattern(pattern, parseError, success);
297}
298
299MessageFormat::MessageFormat(const MessageFormat& that)
300:
301 Format(that),
302 fLocale(that.fLocale),
303 msgPattern(that.msgPattern),
304 formatAliases(nullptr),
305 formatAliasesCapacity(0),
306 argTypes(nullptr),
307 argTypeCount(0),
308 argTypeCapacity(0),
309 hasArgTypeConflicts(that.hasArgTypeConflicts),
310 defaultNumberFormat(nullptr),
311 defaultDateFormat(nullptr),
312 cachedFormatters(nullptr),
313 customFormatArgStarts(nullptr),
314 pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
315 ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
316{
317 // This will take care of creating the hash tables (since they are nullptr).
318 UErrorCode ec = U_ZERO_ERROR;
319 copyObjects(that, ec);
320 if (U_FAILURE(ec)) {
321 resetPattern();
322 }
323}
324
325MessageFormat::~MessageFormat()
326{
327 uhash_closeuhash_close_77(cachedFormatters);
328 uhash_closeuhash_close_77(customFormatArgStarts);
329
330 uprv_freeuprv_free_77(argTypes);
331 uprv_freeuprv_free_77(formatAliases);
332 delete defaultNumberFormat;
333 delete defaultDateFormat;
334}
335
336//--------------------------------------------------------------------
337// Variable-size array management
338
339/**
340 * Allocate argTypes[] to at least the given capacity and return
341 * true if successful. If not, leave argTypes[] unchanged.
342 *
343 * If argTypes is nullptr, allocate it. If it is not nullptr, enlarge it
344 * if necessary to be at least as large as specified.
345 */
346UBool MessageFormat::allocateArgTypes(int32_t capacity, UErrorCode& status) {
347 if (U_FAILURE(status)) {
348 return false;
349 }
350 if (argTypeCapacity >= capacity) {
351 return true;
352 }
353 if (capacity < DEFAULT_INITIAL_CAPACITY) {
354 capacity = DEFAULT_INITIAL_CAPACITY;
355 } else if (capacity < 2*argTypeCapacity) {
356 capacity = 2*argTypeCapacity;
357 }
358 Formattable::Type* a = static_cast<Formattable::Type*>(
359 uprv_reallocuprv_realloc_77(argTypes, sizeof(*argTypes) * capacity));
360 if (a == nullptr) {
361 status = U_MEMORY_ALLOCATION_ERROR;
362 return false;
363 }
364 argTypes = a;
365 argTypeCapacity = capacity;
366 return true;
367}
368
369// -------------------------------------
370// assignment operator
371
372const MessageFormat&
373MessageFormat::operator=(const MessageFormat& that)
374{
375 if (this != &that) {
376 // Calls the super class for assignment first.
377 Format::operator=(that);
378
379 setLocale(that.fLocale);
380 msgPattern = that.msgPattern;
381 hasArgTypeConflicts = that.hasArgTypeConflicts;
382
383 UErrorCode ec = U_ZERO_ERROR;
384 copyObjects(that, ec);
385 if (U_FAILURE(ec)) {
386 resetPattern();
387 }
388 }
389 return *this;
390}
391
392bool
393MessageFormat::operator==(const Format& rhs) const
394{
395 if (this == &rhs) return true;
396
397 // Check class ID before checking MessageFormat members
398 if (!Format::operator==(rhs)) return false;
399
400 const MessageFormat& that = static_cast<const MessageFormat&>(rhs);
401 if (msgPattern != that.msgPattern ||
402 fLocale != that.fLocale) {
403 return false;
404 }
405
406 // Compare hashtables.
407 if ((customFormatArgStarts == nullptr) != (that.customFormatArgStarts == nullptr)) {
408 return false;
409 }
410 if (customFormatArgStarts == nullptr) {
411 return true;
412 }
413
414 UErrorCode ec = U_ZERO_ERROR;
415 const int32_t count = uhash_countuhash_count_77(customFormatArgStarts);
416 const int32_t rhs_count = uhash_countuhash_count_77(that.customFormatArgStarts);
417 if (count != rhs_count) {
418 return false;
419 }
420 int32_t idx = 0, rhs_idx = 0, pos = UHASH_FIRST(-1), rhs_pos = UHASH_FIRST(-1);
421 for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) {
422 const UHashElement* cur = uhash_nextElementuhash_nextElement_77(customFormatArgStarts, &pos);
423 const UHashElement* rhs_cur = uhash_nextElementuhash_nextElement_77(that.customFormatArgStarts, &rhs_pos);
424 if (cur->key.integer != rhs_cur->key.integer) {
425 return false;
426 }
427 const Format* format = static_cast<const Format*>(uhash_igetuhash_iget_77(cachedFormatters, cur->key.integer));
428 const Format* rhs_format = static_cast<const Format*>(uhash_igetuhash_iget_77(that.cachedFormatters, rhs_cur->key.integer));
429 if (*format != *rhs_format) {
430 return false;
431 }
432 }
433 return true;
434}
435
436// -------------------------------------
437// Creates a copy of this MessageFormat, the caller owns the copy.
438
439MessageFormat*
440MessageFormat::clone() const
441{
442 return new MessageFormat(*this);
443}
444
445// -------------------------------------
446// Sets the locale of this MessageFormat object to theLocale.
447
448void
449MessageFormat::setLocale(const Locale& theLocale)
450{
451 if (fLocale != theLocale) {
452 delete defaultNumberFormat;
453 defaultNumberFormat = nullptr;
454 delete defaultDateFormat;
455 defaultDateFormat = nullptr;
456 fLocale = theLocale;
457 setLocaleIDs(fLocale.getName(), fLocale.getName());
458 pluralProvider.reset();
459 ordinalProvider.reset();
460 }
461}
462
463// -------------------------------------
464// Gets the locale of this MessageFormat object.
465
466const Locale&
467MessageFormat::getLocale() const
468{
469 return fLocale;
470}
471
472void
473MessageFormat::applyPattern(const UnicodeString& newPattern,
474 UErrorCode& status)
475{
476 UParseError parseError;
477 applyPattern(newPattern,parseError,status);
478}
479
480
481// -------------------------------------
482// Applies the new pattern and returns an error if the pattern
483// is not correct.
484void
485MessageFormat::applyPattern(const UnicodeString& pattern,
486 UParseError& parseError,
487 UErrorCode& ec)
488{
489 if(U_FAILURE(ec)) {
490 return;
491 }
492 msgPattern.parse(pattern, &parseError, ec);
493 cacheExplicitFormats(ec);
494
495 if (U_FAILURE(ec)) {
496 resetPattern();
497 }
498}
499
500void MessageFormat::resetPattern() {
501 msgPattern.clear();
502 uhash_closeuhash_close_77(cachedFormatters);
503 cachedFormatters = nullptr;
504 uhash_closeuhash_close_77(customFormatArgStarts);
505 customFormatArgStarts = nullptr;
506 argTypeCount = 0;
507 hasArgTypeConflicts = false;
508}
509
510void
511MessageFormat::applyPattern(const UnicodeString& pattern,
512 UMessagePatternApostropheMode aposMode,
513 UParseError* parseError,
514 UErrorCode& status) {
515 if (aposMode != msgPattern.getApostropheMode()) {
516 msgPattern.clearPatternAndSetApostropheMode(aposMode);
517 }
518 UParseError tempParseError;
519 applyPattern(pattern, (parseError == nullptr) ? tempParseError : *parseError, status);
520}
521
522// -------------------------------------
523// Converts this MessageFormat instance to a pattern.
524
525UnicodeString&
526MessageFormat::toPattern(UnicodeString& appendTo) const {
527 if ((customFormatArgStarts != nullptr && 0 != uhash_countuhash_count_77(customFormatArgStarts)) ||
528 0 == msgPattern.countParts()
529 ) {
530 appendTo.setToBogus();
531 return appendTo;
532 }
533 return appendTo.append(msgPattern.getPatternString());
534}
535
536int32_t MessageFormat::nextTopLevelArgStart(int32_t partIndex) const {
537 if (partIndex != 0) {
538 partIndex = msgPattern.getLimitPartIndex(partIndex);
539 }
540 for (;;) {
541 UMessagePatternPartType type = msgPattern.getPartType(++partIndex);
542 if (type == UMSGPAT_PART_TYPE_ARG_START) {
543 return partIndex;
544 }
545 if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) {
546 return -1;
547 }
548 }
549}
550
551void MessageFormat::setArgStartFormat(int32_t argStart,
552 Format* formatter,
553 UErrorCode& status) {
554 if (U_FAILURE(status)) {
555 delete formatter;
556 return;
557 }
558 if (cachedFormatters == nullptr) {
559 cachedFormatters=uhash_openuhash_open_77(uhash_hashLonguhash_hashLong_77, uhash_compareLonguhash_compareLong_77,
560 equalFormatsForHash, &status);
561 if (U_FAILURE(status)) {
562 delete formatter;
563 return;
564 }
565 uhash_setValueDeleteruhash_setValueDeleter_77(cachedFormatters, uprv_deleteUObjectuprv_deleteUObject_77);
566 }
567 if (formatter == nullptr) {
568 formatter = new DummyFormat();
569 }
570 uhash_iputuhash_iput_77(cachedFormatters, argStart, formatter, &status);
571}
572
573
574UBool MessageFormat::argNameMatches(int32_t partIndex, const UnicodeString& argName, int32_t argNumber) {
575 const MessagePattern::Part& part = msgPattern.getPart(partIndex);
576 return part.getType() == UMSGPAT_PART_TYPE_ARG_NAME ?
577 msgPattern.partSubstringMatches(part, argName) :
578 part.getValue() == argNumber; // ARG_NUMBER
579}
580
581// Sets a custom formatter for a MessagePattern ARG_START part index.
582// "Custom" formatters are provided by the user via setFormat() or similar APIs.
583void MessageFormat::setCustomArgStartFormat(int32_t argStart,
584 Format* formatter,
585 UErrorCode& status) {
586 setArgStartFormat(argStart, formatter, status);
587 if (customFormatArgStarts == nullptr) {
588 customFormatArgStarts=uhash_openuhash_open_77(uhash_hashLonguhash_hashLong_77, uhash_compareLonguhash_compareLong_77,
589 nullptr, &status);
590 }
591 uhash_iputiuhash_iputi_77(customFormatArgStarts, argStart, 1, &status);
592}
593
594Format* MessageFormat::getCachedFormatter(int32_t argumentNumber) const {
595 if (cachedFormatters == nullptr) {
596 return nullptr;
597 }
598 void* ptr = uhash_igetuhash_iget_77(cachedFormatters, argumentNumber);
599 if (ptr != nullptr && dynamic_cast<DummyFormat*>(static_cast<Format*>(ptr)) == nullptr) {
600 return static_cast<Format*>(ptr);
601 } else {
602 // Not cached, or a DummyFormat representing setFormat(nullptr).
603 return nullptr;
604 }
605}
606
607// -------------------------------------
608// Adopts the new formats array and updates the array count.
609// This MessageFormat instance owns the new formats.
610void
611MessageFormat::adoptFormats(Format** newFormats,
612 int32_t count) {
613 if (newFormats == nullptr || count < 0) {
614 return;
615 }
616 // Throw away any cached formatters.
617 if (cachedFormatters != nullptr) {
618 uhash_removeAlluhash_removeAll_77(cachedFormatters);
619 }
620 if (customFormatArgStarts != nullptr) {
621 uhash_removeAlluhash_removeAll_77(customFormatArgStarts);
622 }
623
624 int32_t formatNumber = 0;
625 UErrorCode status = U_ZERO_ERROR;
626 for (int32_t partIndex = 0;
627 formatNumber < count && U_SUCCESS(status) &&
628 (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
629 setCustomArgStartFormat(partIndex, newFormats[formatNumber], status);
630 ++formatNumber;
631 }
632 // Delete those that didn't get used (if any).
633 for (; formatNumber < count; ++formatNumber) {
634 delete newFormats[formatNumber];
635 }
636
637}
638
639// -------------------------------------
640// Sets the new formats array and updates the array count.
641// This MessageFormat instance makes a copy of the new formats.
642
643void
644MessageFormat::setFormats(const Format** newFormats,
645 int32_t count) {
646 if (newFormats == nullptr || count < 0) {
647 return;
648 }
649 // Throw away any cached formatters.
650 if (cachedFormatters != nullptr) {
651 uhash_removeAlluhash_removeAll_77(cachedFormatters);
652 }
653 if (customFormatArgStarts != nullptr) {
654 uhash_removeAlluhash_removeAll_77(customFormatArgStarts);
655 }
656
657 UErrorCode status = U_ZERO_ERROR;
658 int32_t formatNumber = 0;
659 for (int32_t partIndex = 0;
660 formatNumber < count && U_SUCCESS(status) && (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
661 Format* newFormat = nullptr;
662 if (newFormats[formatNumber] != nullptr) {
663 newFormat = newFormats[formatNumber]->clone();
664 if (newFormat == nullptr) {
665 status = U_MEMORY_ALLOCATION_ERROR;
666 }
667 }
668 setCustomArgStartFormat(partIndex, newFormat, status);
669 ++formatNumber;
670 }
671 if (U_FAILURE(status)) {
672 resetPattern();
673 }
674}
675
676// -------------------------------------
677// Adopt a single format by format number.
678// Do nothing if the format number is not less than the array count.
679
680void
681MessageFormat::adoptFormat(int32_t n, Format *newFormat) {
682 LocalPointer<Format> p(newFormat);
683 if (n >= 0) {
684 int32_t formatNumber = 0;
685 for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
686 if (n == formatNumber) {
687 UErrorCode status = U_ZERO_ERROR;
688 setCustomArgStartFormat(partIndex, p.orphan(), status);
689 return;
690 }
691 ++formatNumber;
692 }
693 }
694}
695
696// -------------------------------------
697// Adopt a single format by format name.
698// Do nothing if there is no match of formatName.
699void
700MessageFormat::adoptFormat(const UnicodeString& formatName,
701 Format* formatToAdopt,
702 UErrorCode& status) {
703 LocalPointer<Format> p(formatToAdopt);
704 if (U_FAILURE(status)) {
705 return;
706 }
707 int32_t argNumber = MessagePattern::validateArgumentName(formatName);
708 if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
709 status = U_ILLEGAL_ARGUMENT_ERROR;
710 return;
711 }
712 for (int32_t partIndex = 0;
713 (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
714 ) {
715 if (argNameMatches(partIndex + 1, formatName, argNumber)) {
716 Format* f;
717 if (p.isValid()) {
718 f = p.orphan();
719 } else if (formatToAdopt == nullptr) {
720 f = nullptr;
721 } else {
722 f = formatToAdopt->clone();
723 if (f == nullptr) {
724 status = U_MEMORY_ALLOCATION_ERROR;
725 return;
726 }
727 }
728 setCustomArgStartFormat(partIndex, f, status);
729 }
730 }
731}
732
733// -------------------------------------
734// Set a single format.
735// Do nothing if the variable is not less than the array count.
736void
737MessageFormat::setFormat(int32_t n, const Format& newFormat) {
738
739 if (n >= 0) {
740 int32_t formatNumber = 0;
741 for (int32_t partIndex = 0;
742 (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
743 if (n == formatNumber) {
744 Format* new_format = newFormat.clone();
745 if (new_format) {
746 UErrorCode status = U_ZERO_ERROR;
747 setCustomArgStartFormat(partIndex, new_format, status);
748 }
749 return;
750 }
751 ++formatNumber;
752 }
753 }
754}
755
756// -------------------------------------
757// Get a single format by format name.
758// Do nothing if the variable is not less than the array count.
759Format *
760MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) {
761 if (U_FAILURE(status) || cachedFormatters == nullptr) return nullptr;
762
763 int32_t argNumber = MessagePattern::validateArgumentName(formatName);
764 if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
765 status = U_ILLEGAL_ARGUMENT_ERROR;
766 return nullptr;
767 }
768 for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
769 if (argNameMatches(partIndex + 1, formatName, argNumber)) {
770 return getCachedFormatter(partIndex);
771 }
772 }
773 return nullptr;
774}
775
776// -------------------------------------
777// Set a single format by format name
778// Do nothing if the variable is not less than the array count.
779void
780MessageFormat::setFormat(const UnicodeString& formatName,
781 const Format& newFormat,
782 UErrorCode& status) {
783 if (U_FAILURE(status)) return;
784
785 int32_t argNumber = MessagePattern::validateArgumentName(formatName);
786 if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) {
787 status = U_ILLEGAL_ARGUMENT_ERROR;
788 return;
789 }
790 for (int32_t partIndex = 0;
791 (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
792 ) {
793 if (argNameMatches(partIndex + 1, formatName, argNumber)) {
794 Format* new_format = newFormat.clone();
795 if (new_format == nullptr) {
796 status = U_MEMORY_ALLOCATION_ERROR;
797 return;
798 }
799 setCustomArgStartFormat(partIndex, new_format, status);
800 }
801 }
802}
803
804// -------------------------------------
805// Gets the format array.
806const Format**
807MessageFormat::getFormats(int32_t& cnt) const
808{
809 // This old API returns an array (which we hold) of Format*
810 // pointers. The array is valid up to the next call to any
811 // method on this object. We construct and resize an array
812 // on demand that contains aliases to the subformats[i].format
813 // pointers.
814
815 // Get total required capacity first (it's refreshed on each call).
816 int32_t totalCapacity = 0;
817 for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {}
818
819 MessageFormat* t = const_cast<MessageFormat*> (this);
820 cnt = 0;
821 if (formatAliases == nullptr) {
822 t->formatAliasesCapacity = totalCapacity;
823 Format** a = static_cast<Format**>(
824 uprv_mallocuprv_malloc_77(sizeof(Format*) * formatAliasesCapacity));
825 if (a == nullptr) {
826 t->formatAliasesCapacity = 0;
827 return nullptr;
828 }
829 t->formatAliases = a;
830 } else if (totalCapacity > formatAliasesCapacity) {
831 Format** a = static_cast<Format**>(
832 uprv_reallocuprv_realloc_77(formatAliases, sizeof(Format*) * totalCapacity));
833 if (a == nullptr) {
834 t->formatAliasesCapacity = 0;
835 return nullptr;
836 }
837 t->formatAliases = a;
838 t->formatAliasesCapacity = totalCapacity;
839 }
840
841 for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
842 t->formatAliases[cnt++] = getCachedFormatter(partIndex);
843 }
844
845 return (const Format**)formatAliases;
846}
847
848
849UnicodeString MessageFormat::getArgName(int32_t partIndex) {
850 const MessagePattern::Part& part = msgPattern.getPart(partIndex);
851 return msgPattern.getSubstring(part);
852}
853
854StringEnumeration*
855MessageFormat::getFormatNames(UErrorCode& status) {
856 if (U_FAILURE(status)) return nullptr;
857
858 LocalPointer<UVector> formatNames(new UVector(status), status);
859 if (U_FAILURE(status)) {
860 return nullptr;
861 }
862 formatNames->setDeleter(uprv_deleteUObjectuprv_deleteUObject_77);
863
864 for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
865 LocalPointer<UnicodeString> name(getArgName(partIndex + 1).clone(), status);
866 formatNames->adoptElement(name.orphan(), status);
867 if (U_FAILURE(status)) return nullptr;
868 }
869
870 LocalPointer<StringEnumeration> nameEnumerator(
871 new FormatNameEnumeration(std::move(formatNames), status), status);
872 return U_SUCCESS(status) ? nameEnumerator.orphan() : nullptr;
873}
874
875// -------------------------------------
876// Formats the source Formattable array and copy into the result buffer.
877// Ignore the FieldPosition result for error checking.
878
879UnicodeString&
880MessageFormat::format(const Formattable* source,
881 int32_t cnt,
882 UnicodeString& appendTo,
883 FieldPosition& ignore,
884 UErrorCode& success) const
885{
886 return format(source, nullptr, cnt, appendTo, &ignore, success);
887}
888
889// -------------------------------------
890// Internally creates a MessageFormat instance based on the
891// pattern and formats the arguments Formattable array and
892// copy into the appendTo buffer.
893
894UnicodeString&
895MessageFormat::format( const UnicodeString& pattern,
896 const Formattable* arguments,
897 int32_t cnt,
898 UnicodeString& appendTo,
899 UErrorCode& success)
900{
901 MessageFormat temp(pattern, success);
902 return temp.format(arguments, nullptr, cnt, appendTo, nullptr, success);
903}
904
905// -------------------------------------
906// Formats the source Formattable object and copy into the
907// appendTo buffer. The Formattable object must be an array
908// of Formattable instances, returns error otherwise.
909
910UnicodeString&
911MessageFormat::format(const Formattable& source,
912 UnicodeString& appendTo,
913 FieldPosition& ignore,
914 UErrorCode& success) const
915{
916 if (U_FAILURE(success))
917 return appendTo;
918 if (source.getType() != Formattable::kArray) {
919 success = U_ILLEGAL_ARGUMENT_ERROR;
920 return appendTo;
921 }
922 int32_t cnt;
923 const Formattable* tmpPtr = source.getArray(cnt);
924 return format(tmpPtr, nullptr, cnt, appendTo, &ignore, success);
925}
926
927UnicodeString&
928MessageFormat::format(const UnicodeString* argumentNames,
929 const Formattable* arguments,
930 int32_t count,
931 UnicodeString& appendTo,
932 UErrorCode& success) const {
933 return format(arguments, argumentNames, count, appendTo, nullptr, success);
934}
935
936// Does linear search to find the match for an ArgName.
937const Formattable* MessageFormat::getArgFromListByName(const Formattable* arguments,
938 const UnicodeString *argumentNames,
939 int32_t cnt, UnicodeString& name) const {
940 for (int32_t i = 0; i < cnt; ++i) {
941 if (0 == argumentNames[i].compare(name)) {
942 return arguments + i;
943 }
944 }
945 return nullptr;
946}
947
948
949UnicodeString&
950MessageFormat::format(const Formattable* arguments,
951 const UnicodeString *argumentNames,
952 int32_t cnt,
953 UnicodeString& appendTo,
954 FieldPosition* pos,
955 UErrorCode& status) const {
956 if (U_FAILURE(status)) {
957 return appendTo;
958 }
959
960 UnicodeStringAppendable usapp(appendTo);
961 AppendableWrapper app(usapp);
962 format(0, nullptr, arguments, argumentNames, cnt, app, pos, status);
963 return appendTo;
964}
965
966namespace {
967
968/**
969 * Mutable input/output values for the PluralSelectorProvider.
970 * Separate so that it is possible to make MessageFormat Freezable.
971 */
972class PluralSelectorContext {
973public:
974 PluralSelectorContext(int32_t start, const UnicodeString &name,
975 const Formattable &num, double off, UErrorCode &errorCode)
976 : startIndex(start), argName(name), offset(off),
977 numberArgIndex(-1), formatter(nullptr), forReplaceNumber(false) {
978 // number needs to be set even when select() is not called.
979 // Keep it as a Number/Formattable:
980 // For format() methods, and to preserve information (e.g., BigDecimal).
981 if(off == 0) {
982 number = num;
983 } else {
984 number = num.getDouble(errorCode) - off;
985 }
986 }
987
988 // Input values for plural selection with decimals.
989 int32_t startIndex;
990 const UnicodeString &argName;
991 /** argument number - plural offset */
992 Formattable number;
993 double offset;
994 // Output values for plural selection with decimals.
995 /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */
996 int32_t numberArgIndex;
997 const Format *formatter;
998 /** formatted argument number - plural offset */
999 UnicodeString numberString;
1000 /** true if number-offset was formatted with the stock number formatter */
1001 UBool forReplaceNumber;
1002};
1003
1004} // namespace
1005
1006// if argumentNames is nullptr, this means arguments is a numeric array.
1007// arguments can not be nullptr.
1008// We use const void *plNumber rather than const PluralSelectorContext *pluralNumber
1009// so that we need not declare the PluralSelectorContext in the public header file.
1010void MessageFormat::format(int32_t msgStart, const void *plNumber,
1011 const Formattable* arguments,
1012 const UnicodeString *argumentNames,
1013 int32_t cnt,
1014 AppendableWrapper& appendTo,
1015 FieldPosition* ignore,
1016 UErrorCode& success) const {
1017 if (U_FAILURE(success)) {
1018 return;
1019 }
1020
1021 const UnicodeString& msgString = msgPattern.getPatternString();
1022 int32_t prevIndex = msgPattern.getPart(msgStart).getLimit();
1023 for (int32_t i = msgStart + 1; U_SUCCESS(success) ; ++i) {
1024 const MessagePattern::Part* part = &msgPattern.getPart(i);
1025 const UMessagePatternPartType type = part->getType();
1026 int32_t index = part->getIndex();
1027 appendTo.append(msgString, prevIndex, index - prevIndex);
1028 if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) {
1029 return;
1030 }
1031 prevIndex = part->getLimit();
1032 if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
1033 const PluralSelectorContext &pluralNumber =
1034 *static_cast<const PluralSelectorContext *>(plNumber);
1035 if(pluralNumber.forReplaceNumber) {
1036 // number-offset was already formatted.
1037 appendTo.formatAndAppend(pluralNumber.formatter,
1038 pluralNumber.number, pluralNumber.numberString, success);
1039 } else {
1040 const NumberFormat* nf = getDefaultNumberFormat(success);
1041 appendTo.formatAndAppend(nf, pluralNumber.number, success);
1042 }
1043 continue;
1044 }
1045 if (type != UMSGPAT_PART_TYPE_ARG_START) {
1046 continue;
1047 }
1048 int32_t argLimit = msgPattern.getLimitPartIndex(i);
1049 UMessagePatternArgType argType = part->getArgType();
1050 part = &msgPattern.getPart(++i);
1051 const Formattable* arg;
1052 UBool noArg = false;
1053 UnicodeString argName = msgPattern.getSubstring(*part);
1054 if (argumentNames == nullptr) {
1055 int32_t argNumber = part->getValue(); // ARG_NUMBER
1056 if (0 <= argNumber && argNumber < cnt) {
1057 arg = arguments + argNumber;
1058 } else {
1059 arg = nullptr;
1060 noArg = true;
1061 }
1062 } else {
1063 arg = getArgFromListByName(arguments, argumentNames, cnt, argName);
1064 if (arg == nullptr) {
1065 noArg = true;
1066 }
1067 }
1068 ++i;
1069 int32_t prevDestLength = appendTo.length();
1070 const Format* formatter = nullptr;
1071 if (noArg) {
1072 appendTo.append(
1073 UnicodeString(LEFT_CURLY_BRACE((char16_t)0x007B)).append(argName).append(RIGHT_CURLY_BRACE((char16_t)0x007D)));
1074 } else if (arg == nullptr) {
1075 appendTo.append(NULL_STRING, 4);
1076 } else if(plNumber!=nullptr &&
1077 static_cast<const PluralSelectorContext *>(plNumber)->numberArgIndex==(i-2)) {
1078 const PluralSelectorContext &pluralNumber =
1079 *static_cast<const PluralSelectorContext *>(plNumber);
1080 if(pluralNumber.offset == 0) {
1081 // The number was already formatted with this formatter.
1082 appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number,
1083 pluralNumber.numberString, success);
1084 } else {
1085 // Do not use the formatted (number-offset) string for a named argument
1086 // that formats the number without subtracting the offset.
1087 appendTo.formatAndAppend(pluralNumber.formatter, *arg, success);
1088 }
1089 } else if ((formatter = getCachedFormatter(i - 2)) != nullptr) {
1090 // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings.
1091 if (dynamic_cast<const ChoiceFormat*>(formatter) ||
1092 dynamic_cast<const PluralFormat*>(formatter) ||
1093 dynamic_cast<const SelectFormat*>(formatter)) {
1094 // We only handle nested formats here if they were provided via
1095 // setFormat() or its siblings. Otherwise they are not cached and instead
1096 // handled below according to argType.
1097 UnicodeString subMsgString;
1098 formatter->format(*arg, subMsgString, success);
1099 if (subMsgString.indexOf(LEFT_CURLY_BRACE((char16_t)0x007B)) >= 0 ||
1100 (subMsgString.indexOf(SINGLE_QUOTE((char16_t)0x0027)) >= 0 && !MessageImpl::jdkAposMode(msgPattern))
1101 ) {
1102 MessageFormat subMsgFormat(subMsgString, fLocale, success);
1103 subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, ignore, success);
1104 } else {
1105 appendTo.append(subMsgString);
1106 }
1107 } else {
1108 appendTo.formatAndAppend(formatter, *arg, success);
1109 }
1110 } else if (argType == UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_igetuhash_iget_77(cachedFormatters, i - 2))) {
1111 // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table.
1112 // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check
1113 // for the hash table containing DummyFormat.
1114 if (arg->isNumeric()) {
1115 const NumberFormat* nf = getDefaultNumberFormat(success);
1116 appendTo.formatAndAppend(nf, *arg, success);
1117 } else if (arg->getType() == Formattable::kDate) {
1118 const DateFormat* df = getDefaultDateFormat(success);
1119 appendTo.formatAndAppend(df, *arg, success);
1120 } else {
1121 appendTo.append(arg->getString(success));
1122 }
1123 } else if (argType == UMSGPAT_ARG_TYPE_CHOICE) {
1124 if (!arg->isNumeric()) {
1125 success = U_ILLEGAL_ARGUMENT_ERROR;
1126 return;
1127 }
1128 // We must use the Formattable::getDouble() variant with the UErrorCode parameter
1129 // because only this one converts non-double numeric types to double.
1130 const double number = arg->getDouble(success);
1131 int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number);
1132 formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames,
1133 cnt, appendTo, success);
1134 } else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)((argType)==UMSGPAT_ARG_TYPE_PLURAL || (argType)==UMSGPAT_ARG_TYPE_SELECTORDINAL
)
) {
1135 if (!arg->isNumeric()) {
1136 success = U_ILLEGAL_ARGUMENT_ERROR;
1137 return;
1138 }
1139 const PluralSelectorProvider &selector =
1140 argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider;
1141 // We must use the Formattable::getDouble() variant with the UErrorCode parameter
1142 // because only this one converts non-double numeric types to double.
1143 double offset = msgPattern.getPluralOffset(i);
1144 PluralSelectorContext context(i, argName, *arg, offset, success);
1145 int32_t subMsgStart = PluralFormat::findSubMessage(
1146 msgPattern, i, selector, &context, arg->getDouble(success), success);
1147 formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames,
1148 cnt, appendTo, success);
1149 } else if (argType == UMSGPAT_ARG_TYPE_SELECT) {
1150 int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success);
1151 formatComplexSubMessage(subMsgStart, nullptr, arguments, argumentNames,
1152 cnt, appendTo, success);
1153 } else {
1154 // This should never happen.
1155 success = U_INTERNAL_PROGRAM_ERROR;
1156 return;
1157 }
1158 ignore = updateMetaData(appendTo, prevDestLength, ignore, arg);
1159 prevIndex = msgPattern.getPart(argLimit).getLimit();
1160 i = argLimit;
1161 }
1162}
1163
1164
1165void MessageFormat::formatComplexSubMessage(int32_t msgStart,
1166 const void *plNumber,
1167 const Formattable* arguments,
1168 const UnicodeString *argumentNames,
1169 int32_t cnt,
1170 AppendableWrapper& appendTo,
1171 UErrorCode& success) const {
1172 if (U_FAILURE(success)) {
1173 return;
1174 }
1175
1176 if (!MessageImpl::jdkAposMode(msgPattern)) {
1177 format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, nullptr, success);
1178 return;
1179 }
1180
1181 // JDK compatibility mode: (see JDK MessageFormat.format() API docs)
1182 // - remove SKIP_SYNTAX; that is, remove half of the apostrophes
1183 // - if the result string contains an open curly brace '{' then
1184 // instantiate a temporary MessageFormat object and format again;
1185 // otherwise just append the result string
1186 const UnicodeString& msgString = msgPattern.getPatternString();
1187 UnicodeString sb;
1188 int32_t prevIndex = msgPattern.getPart(msgStart).getLimit();
1189 for (int32_t i = msgStart;;) {
1190 const MessagePattern::Part& part = msgPattern.getPart(++i);
1191 const UMessagePatternPartType type = part.getType();
1192 int32_t index = part.getIndex();
1193 if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) {
1194 sb.append(msgString, prevIndex, index - prevIndex);
1195 break;
1196 } else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
1197 sb.append(msgString, prevIndex, index - prevIndex);
1198 if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
1199 const PluralSelectorContext &pluralNumber =
1200 *static_cast<const PluralSelectorContext *>(plNumber);
1201 if(pluralNumber.forReplaceNumber) {
1202 // number-offset was already formatted.
1203 sb.append(pluralNumber.numberString);
1204 } else {
1205 const NumberFormat* nf = getDefaultNumberFormat(success);
1206 sb.append(nf->format(pluralNumber.number, sb, success));
1207 }
1208 }
1209 prevIndex = part.getLimit();
1210 } else if (type == UMSGPAT_PART_TYPE_ARG_START) {
1211 sb.append(msgString, prevIndex, index - prevIndex);
1212 prevIndex = index;
1213 i = msgPattern.getLimitPartIndex(i);
1214 index = msgPattern.getPart(i).getLimit();
1215 MessageImpl::appendReducedApostrophes(msgString, prevIndex, index, sb);
1216 prevIndex = index;
1217 }
1218 }
1219 if (sb.indexOf(LEFT_CURLY_BRACE((char16_t)0x007B)) >= 0) {
1220 UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter.
1221 MessageFormat subMsgFormat(emptyPattern, fLocale, success);
1222 subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, nullptr, success);
1223 subMsgFormat.format(0, nullptr, arguments, argumentNames, cnt, appendTo, nullptr, success);
1224 } else {
1225 appendTo.append(sb);
1226 }
1227}
1228
1229
1230UnicodeString MessageFormat::getLiteralStringUntilNextArgument(int32_t from) const {
1231 const UnicodeString& msgString=msgPattern.getPatternString();
1232 int32_t prevIndex=msgPattern.getPart(from).getLimit();
1233 UnicodeString b;
1234 for (int32_t i = from + 1; ; ++i) {
1235 const MessagePattern::Part& part = msgPattern.getPart(i);
1236 const UMessagePatternPartType type=part.getType();
1237 int32_t index=part.getIndex();
1238 b.append(msgString, prevIndex, index - prevIndex);
1239 if(type==UMSGPAT_PART_TYPE_ARG_START || type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
1240 return b;
1241 }
1242 // Unexpected Part "part" in parsed message.
1243 U_ASSERT(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR)(static_cast <bool> (type==UMSGPAT_PART_TYPE_SKIP_SYNTAX
|| type==UMSGPAT_PART_TYPE_INSERT_CHAR) ? void (0) : __assert_fail
("type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1244 prevIndex=part.getLimit();
1245 }
1246}
1247
1248
1249FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_t /*prevLength*/,
1250 FieldPosition* /*fp*/, const Formattable* /*argId*/) const {
1251 // Unlike in Java, there are no field attributes defined for MessageFormat. Do nothing.
1252 return nullptr;
1253 /*
1254 if (fp != nullptr && Field.ARGUMENT.equals(fp.getFieldAttribute())) {
1255 fp->setBeginIndex(prevLength);
1256 fp->setEndIndex(dest.get_length());
1257 return nullptr;
1258 }
1259 return fp;
1260 */
1261}
1262
1263int32_t
1264MessageFormat::findOtherSubMessage(int32_t partIndex) const {
1265 int32_t count=msgPattern.countParts();
1266 const MessagePattern::Part *part = &msgPattern.getPart(partIndex);
1267 if(MessagePattern::Part::hasNumericValue(part->getType())) {
1268 ++partIndex;
1269 }
1270 // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
1271 // until ARG_LIMIT or end of plural-only pattern.
1272 UnicodeString other(false, OTHER_STRING, 5);
1273 do {
1274 part=&msgPattern.getPart(partIndex++);
1275 UMessagePatternPartType type=part->getType();
1276 if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
1277 break;
1278 }
1279 U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR)(static_cast <bool> (type==UMSGPAT_PART_TYPE_ARG_SELECTOR
) ? void (0) : __assert_fail ("type==UMSGPAT_PART_TYPE_ARG_SELECTOR"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1280 // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
1281 if(msgPattern.partSubstringMatches(*part, other)) {
1282 return partIndex;
1283 }
1284 if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) {
1285 ++partIndex; // skip the numeric-value part of "=1" etc.
1286 }
1287 partIndex=msgPattern.getLimitPartIndex(partIndex);
1288 } while(++partIndex<count);
1289 return 0;
1290}
1291
1292int32_t
1293MessageFormat::findFirstPluralNumberArg(int32_t msgStart, const UnicodeString &argName) const {
1294 for(int32_t i=msgStart+1;; ++i) {
1295 const MessagePattern::Part &part=msgPattern.getPart(i);
1296 UMessagePatternPartType type=part.getType();
1297 if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
1298 return 0;
1299 }
1300 if(type==UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
1301 return -1;
1302 }
1303 if(type==UMSGPAT_PART_TYPE_ARG_START) {
1304 UMessagePatternArgType argType=part.getArgType();
1305 if(!argName.isEmpty() && (argType==UMSGPAT_ARG_TYPE_NONE || argType==UMSGPAT_ARG_TYPE_SIMPLE)) {
1306 // ARG_NUMBER or ARG_NAME
1307 if(msgPattern.partSubstringMatches(msgPattern.getPart(i+1), argName)) {
1308 return i;
1309 }
1310 }
1311 i=msgPattern.getLimitPartIndex(i);
1312 }
1313 }
1314}
1315
1316void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) {
1317 // Deep copy pointer fields.
1318 // We need not copy the formatAliases because they are re-filled
1319 // in each getFormats() call.
1320 // The defaultNumberFormat, defaultDateFormat and pluralProvider.rules
1321 // also get created on demand.
1322 argTypeCount = that.argTypeCount;
1323 if (argTypeCount > 0) {
1324 if (!allocateArgTypes(argTypeCount, ec)) {
1325 return;
1326 }
1327 uprv_memcpy(argTypes, that.argTypes, argTypeCount * sizeof(argTypes[0]))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (argTypes != __null) ? void (0) :
__assert_fail ("argTypes != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (that.argTypes != __null) ? void (0) : __assert_fail ("that.argTypes != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(argTypes, that.argTypes, argTypeCount
* sizeof(argTypes[0])); } while (false)
;
1328 }
1329 if (cachedFormatters != nullptr) {
1330 uhash_removeAlluhash_removeAll_77(cachedFormatters);
1331 }
1332 if (customFormatArgStarts != nullptr) {
1333 uhash_removeAlluhash_removeAll_77(customFormatArgStarts);
1334 }
1335 if (that.cachedFormatters) {
1336 if (cachedFormatters == nullptr) {
1337 cachedFormatters=uhash_openuhash_open_77(uhash_hashLonguhash_hashLong_77, uhash_compareLonguhash_compareLong_77,
1338 equalFormatsForHash, &ec);
1339 if (U_FAILURE(ec)) {
1340 return;
1341 }
1342 uhash_setValueDeleteruhash_setValueDeleter_77(cachedFormatters, uprv_deleteUObjectuprv_deleteUObject_77);
1343 }
1344
1345 const int32_t count = uhash_countuhash_count_77(that.cachedFormatters);
1346 int32_t pos, idx;
1347 for (idx = 0, pos = UHASH_FIRST(-1); idx < count && U_SUCCESS(ec); ++idx) {
1348 const UHashElement* cur = uhash_nextElementuhash_nextElement_77(that.cachedFormatters, &pos);
1349 Format* newFormat = static_cast<Format*>(cur->value.pointer)->clone();
1350 if (newFormat) {
1351 uhash_iputuhash_iput_77(cachedFormatters, cur->key.integer, newFormat, &ec);
1352 } else {
1353 ec = U_MEMORY_ALLOCATION_ERROR;
1354 return;
1355 }
1356 }
1357 }
1358 if (that.customFormatArgStarts) {
1359 if (customFormatArgStarts == nullptr) {
1360 customFormatArgStarts=uhash_openuhash_open_77(uhash_hashLonguhash_hashLong_77, uhash_compareLonguhash_compareLong_77,
1361 nullptr, &ec);
1362 }
1363 const int32_t count = uhash_countuhash_count_77(that.customFormatArgStarts);
1364 int32_t pos, idx;
1365 for (idx = 0, pos = UHASH_FIRST(-1); idx < count && U_SUCCESS(ec); ++idx) {
1366 const UHashElement* cur = uhash_nextElementuhash_nextElement_77(that.customFormatArgStarts, &pos);
1367 uhash_iputiuhash_iputi_77(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec);
1368 }
1369 }
1370}
1371
1372
1373Formattable*
1374MessageFormat::parse(int32_t msgStart,
1375 const UnicodeString& source,
1376 ParsePosition& pos,
1377 int32_t& count,
1378 UErrorCode& ec) const {
1379 count = 0;
1380 if (U_FAILURE(ec)) {
1381 pos.setErrorIndex(pos.getIndex());
1382 return nullptr;
1383 }
1384 // parse() does not work with named arguments.
1385 if (msgPattern.hasNamedArguments()) {
1386 ec = U_ARGUMENT_TYPE_MISMATCH;
1387 pos.setErrorIndex(pos.getIndex());
1388 return nullptr;
1389 }
1390 LocalArray<Formattable> resultArray(new Formattable[argTypeCount ? argTypeCount : 1]);
1391 const UnicodeString& msgString=msgPattern.getPatternString();
1392 int32_t prevIndex=msgPattern.getPart(msgStart).getLimit();
1393 int32_t sourceOffset = pos.getIndex();
1394 ParsePosition tempStatus(0);
1395
1396 for(int32_t i=msgStart+1; ; ++i) {
1397 UBool haveArgResult = false;
1398 const MessagePattern::Part* part=&msgPattern.getPart(i);
1399 const UMessagePatternPartType type=part->getType();
1400 int32_t index=part->getIndex();
1401 // Make sure the literal string matches.
1402 int32_t len = index - prevIndex;
1403 if (len == 0 || (0 == msgString.compare(prevIndex, len, source, sourceOffset, len))) {
1404 sourceOffset += len;
1405 prevIndex += len;
Value stored to 'prevIndex' is never read
1406 } else {
1407 pos.setErrorIndex(sourceOffset);
1408 return nullptr; // leave index as is to signal error
1409 }
1410 if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
1411 // Things went well! Done.
1412 pos.setIndex(sourceOffset);
1413 return resultArray.orphan();
1414 }
1415 if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR) {
1416 prevIndex=part->getLimit();
1417 continue;
1418 }
1419 // We do not support parsing Plural formats. (No REPLACE_NUMBER here.)
1420 // Unexpected Part "part" in parsed message.
1421 U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_START)(static_cast <bool> (type==UMSGPAT_PART_TYPE_ARG_START)
? void (0) : __assert_fail ("type==UMSGPAT_PART_TYPE_ARG_START"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1422 int32_t argLimit=msgPattern.getLimitPartIndex(i);
1423
1424 UMessagePatternArgType argType=part->getArgType();
1425 part=&msgPattern.getPart(++i);
1426 int32_t argNumber = part->getValue(); // ARG_NUMBER
1427 UnicodeString key;
1428 ++i;
1429 const Format* formatter = nullptr;
1430 Formattable& argResult = resultArray[argNumber];
1431
1432 if(cachedFormatters!=nullptr && (formatter = getCachedFormatter(i - 2))!=nullptr) {
1433 // Just parse using the formatter.
1434 tempStatus.setIndex(sourceOffset);
1435 formatter->parseObject(source, argResult, tempStatus);
1436 if (tempStatus.getIndex() == sourceOffset) {
1437 pos.setErrorIndex(sourceOffset);
1438 return nullptr; // leave index as is to signal error
1439 }
1440 sourceOffset = tempStatus.getIndex();
1441 haveArgResult = true;
1442 } else if(
1443 argType==UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_igetuhash_iget_77(cachedFormatters, i -2))) {
1444 // We arrive here if getCachedFormatter returned nullptr, but there was actually an element in the hash table.
1445 // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check
1446 // for the hash table containing DummyFormat.
1447
1448 // Match as a string.
1449 // if at end, use longest possible match
1450 // otherwise uses first match to intervening string
1451 // does NOT recursively try all possibilities
1452 UnicodeString stringAfterArgument = getLiteralStringUntilNextArgument(argLimit);
1453 int32_t next;
1454 if (!stringAfterArgument.isEmpty()) {
1455 next = source.indexOf(stringAfterArgument, sourceOffset);
1456 } else {
1457 next = source.length();
1458 }
1459 if (next < 0) {
1460 pos.setErrorIndex(sourceOffset);
1461 return nullptr; // leave index as is to signal error
1462 } else {
1463 UnicodeString strValue(source.tempSubString(sourceOffset, next - sourceOffset));
1464 UnicodeString compValue;
1465 compValue.append(LEFT_CURLY_BRACE((char16_t)0x007B));
1466 itos(argNumber, compValue);
1467 compValue.append(RIGHT_CURLY_BRACE((char16_t)0x007D));
1468 if (0 != strValue.compare(compValue)) {
1469 argResult.setString(strValue);
1470 haveArgResult = true;
1471 }
1472 sourceOffset = next;
1473 }
1474 } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) {
1475 tempStatus.setIndex(sourceOffset);
1476 double choiceResult = ChoiceFormat::parseArgument(msgPattern, i, source, tempStatus);
1477 if (tempStatus.getIndex() == sourceOffset) {
1478 pos.setErrorIndex(sourceOffset);
1479 return nullptr; // leave index as is to signal error
1480 }
1481 argResult.setDouble(choiceResult);
1482 haveArgResult = true;
1483 sourceOffset = tempStatus.getIndex();
1484 } else if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)((argType)==UMSGPAT_ARG_TYPE_PLURAL || (argType)==UMSGPAT_ARG_TYPE_SELECTORDINAL
)
|| argType==UMSGPAT_ARG_TYPE_SELECT) {
1485 // Parsing not supported.
1486 ec = U_UNSUPPORTED_ERROR;
1487 return nullptr;
1488 } else {
1489 // This should never happen.
1490 ec = U_INTERNAL_PROGRAM_ERROR;
1491 return nullptr;
1492 }
1493 if (haveArgResult && count <= argNumber) {
1494 count = argNumber + 1;
1495 }
1496 prevIndex=msgPattern.getPart(argLimit).getLimit();
1497 i=argLimit;
1498 }
1499}
1500// -------------------------------------
1501// Parses the source pattern and returns the Formattable objects array,
1502// the array count and the ending parse position. The caller of this method
1503// owns the array.
1504
1505Formattable*
1506MessageFormat::parse(const UnicodeString& source,
1507 ParsePosition& pos,
1508 int32_t& count) const {
1509 UErrorCode ec = U_ZERO_ERROR;
1510 return parse(0, source, pos, count, ec);
1511}
1512
1513// -------------------------------------
1514// Parses the source string and returns the array of
1515// Formattable objects and the array count. The caller
1516// owns the returned array.
1517
1518Formattable*
1519MessageFormat::parse(const UnicodeString& source,
1520 int32_t& cnt,
1521 UErrorCode& success) const
1522{
1523 if (msgPattern.hasNamedArguments()) {
1524 success = U_ARGUMENT_TYPE_MISMATCH;
1525 return nullptr;
1526 }
1527 ParsePosition status(0);
1528 // Calls the actual implementation method and starts
1529 // from zero offset of the source text.
1530 Formattable* result = parse(source, status, cnt);
1531 if (status.getIndex() == 0) {
1532 success = U_MESSAGE_PARSE_ERROR;
1533 delete[] result;
1534 return nullptr;
1535 }
1536 return result;
1537}
1538
1539// -------------------------------------
1540// Parses the source text and copy into the result buffer.
1541
1542void
1543MessageFormat::parseObject( const UnicodeString& source,
1544 Formattable& result,
1545 ParsePosition& status) const
1546{
1547 int32_t cnt = 0;
1548 Formattable* tmpResult = parse(source, status, cnt);
1549 if (tmpResult != nullptr)
1550 result.adoptArray(tmpResult, cnt);
1551}
1552
1553UnicodeString
1554MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) {
1555 UnicodeString result;
1556 if (U_SUCCESS(status)) {
1557 int32_t plen = pattern.length();
1558 const char16_t* pat = pattern.getBuffer();
1559 int32_t blen = plen * 2 + 1; // space for null termination, convenience
1560 char16_t* buf = result.getBuffer(blen);
1561 if (buf == nullptr) {
1562 status = U_MEMORY_ALLOCATION_ERROR;
1563 } else {
1564 int32_t len = umsg_autoQuoteApostropheumsg_autoQuoteApostrophe_77(pat, plen, buf, blen, &status);
1565 result.releaseBuffer(U_SUCCESS(status) ? len : 0);
1566 }
1567 }
1568 if (U_FAILURE(status)) {
1569 result.setToBogus();
1570 }
1571 return result;
1572}
1573
1574// -------------------------------------
1575
1576static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) {
1577 RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec);
1578 if (fmt == nullptr) {
1579 ec = U_MEMORY_ALLOCATION_ERROR;
1580 } else if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) {
1581 UErrorCode localStatus = U_ZERO_ERROR; // ignore unrecognized default rule set
1582 fmt->setDefaultRuleSet(defaultRuleSet, localStatus);
1583 }
1584 return fmt;
1585}
1586
1587void MessageFormat::cacheExplicitFormats(UErrorCode& status) {
1588 if (U_FAILURE(status)) {
1589 return;
1590 }
1591
1592 if (cachedFormatters != nullptr) {
1593 uhash_removeAlluhash_removeAll_77(cachedFormatters);
1594 }
1595 if (customFormatArgStarts != nullptr) {
1596 uhash_removeAlluhash_removeAll_77(customFormatArgStarts);
1597 }
1598
1599 // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT
1600 // which we need not examine.
1601 int32_t limit = msgPattern.countParts() - 2;
1602 argTypeCount = 0;
1603 // We also need not look at the first two "parts"
1604 // (at most MSG_START and ARG_START) in this loop.
1605 // We determine the argTypeCount first so that we can allocateArgTypes
1606 // so that the next loop can set argTypes[argNumber].
1607 // (This is for the C API which needs the argTypes to read its va_arg list.)
1608 for (int32_t i = 2; i < limit && U_SUCCESS(status); ++i) {
1609 const MessagePattern::Part& part = msgPattern.getPart(i);
1610 if (part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) {
1611 const int argNumber = part.getValue();
1612 if (argNumber >= argTypeCount) {
1613 argTypeCount = argNumber + 1;
1614 }
1615 }
1616 }
1617 if (!allocateArgTypes(argTypeCount, status)) {
1618 return;
1619 }
1620 // Set all argTypes to kObject, as a "none" value, for lack of any better value.
1621 // We never use kObject for real arguments.
1622 // We use it as "no argument yet" for the check for hasArgTypeConflicts.
1623 for (int32_t i = 0; i < argTypeCount; ++i) {
1624 argTypes[i] = Formattable::kObject;
1625 }
1626 hasArgTypeConflicts = false;
1627
1628 // This loop starts at part index 1 because we do need to examine
1629 // ARG_START parts. (But we can ignore the MSG_START.)
1630 for (int32_t i = 1; i < limit && U_SUCCESS(status); ++i) {
1631 const MessagePattern::Part* part = &msgPattern.getPart(i);
1632 if (part->getType() != UMSGPAT_PART_TYPE_ARG_START) {
1633 continue;
1634 }
1635 UMessagePatternArgType argType = part->getArgType();
1636
1637 int32_t argNumber = -1;
1638 part = &msgPattern.getPart(i + 1);
1639 if (part->getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) {
1640 argNumber = part->getValue();
1641 }
1642 Formattable::Type formattableType;
1643
1644 switch (argType) {
1645 case UMSGPAT_ARG_TYPE_NONE:
1646 formattableType = Formattable::kString;
1647 break;
1648 case UMSGPAT_ARG_TYPE_SIMPLE: {
1649 int32_t index = i;
1650 i += 2;
1651 UnicodeString explicitType = msgPattern.getSubstring(msgPattern.getPart(i++));
1652 UnicodeString style;
1653 if ((part = &msgPattern.getPart(i))->getType() == UMSGPAT_PART_TYPE_ARG_STYLE) {
1654 style = msgPattern.getSubstring(*part);
1655 ++i;
1656 }
1657 UParseError parseError;
1658 Format* formatter = createAppropriateFormat(explicitType, style, formattableType, parseError, status);
1659 setArgStartFormat(index, formatter, status);
1660 break;
1661 }
1662 case UMSGPAT_ARG_TYPE_CHOICE:
1663 case UMSGPAT_ARG_TYPE_PLURAL:
1664 case UMSGPAT_ARG_TYPE_SELECTORDINAL:
1665 formattableType = Formattable::kDouble;
1666 break;
1667 case UMSGPAT_ARG_TYPE_SELECT:
1668 formattableType = Formattable::kString;
1669 break;
1670 default:
1671 status = U_INTERNAL_PROGRAM_ERROR; // Should be unreachable.
1672 formattableType = Formattable::kString;
1673 break;
1674 }
1675 if (argNumber != -1) {
1676 if (argTypes[argNumber] != Formattable::kObject && argTypes[argNumber] != formattableType) {
1677 hasArgTypeConflicts = true;
1678 }
1679 argTypes[argNumber] = formattableType;
1680 }
1681 }
1682}
1683
1684Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style,
1685 Formattable::Type& formattableType, UParseError& parseError,
1686 UErrorCode& ec) {
1687 if (U_FAILURE(ec)) {
1688 return nullptr;
1689 }
1690 Format* fmt = nullptr;
1691 int32_t typeID, styleID;
1692 DateFormat::EStyle date_style;
1693 int32_t firstNonSpace;
1694
1695 switch (typeID = findKeyword(type, TYPE_IDS)) {
1696 case 0: // number
1697 formattableType = Formattable::kDouble;
1698 switch (findKeyword(style, NUMBER_STYLE_IDS)) {
1699 case 0: // default
1700 fmt = NumberFormat::createInstance(fLocale, ec);
1701 break;
1702 case 1: // currency
1703 fmt = NumberFormat::createCurrencyInstance(fLocale, ec);
1704 break;
1705 case 2: // percent
1706 fmt = NumberFormat::createPercentInstance(fLocale, ec);
1707 break;
1708 case 3: // integer
1709 formattableType = Formattable::kLong;
1710 fmt = createIntegerFormat(fLocale, ec);
1711 break;
1712 default: // pattern or skeleton
1713 firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
1714 if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
1715 // Skeleton
1716 UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
1717 fmt = number::NumberFormatter::forSkeleton(skeleton, ec).locale(fLocale).toFormat(ec);
1718 } else {
1719 // Pattern
1720 fmt = NumberFormat::createInstance(fLocale, ec);
1721 if (fmt) {
1722 auto* decfmt = dynamic_cast<DecimalFormat*>(fmt);
1723 if (decfmt != nullptr) {
1724 decfmt->applyPattern(style, parseError, ec);
1725 }
1726 }
1727 }
1728 break;
1729 }
1730 break;
1731
1732 case 1: // date
1733 case 2: // time
1734 formattableType = Formattable::kDate;
1735 firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
1736 if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
1737 // Skeleton
1738 UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
1739 fmt = DateFormat::createInstanceForSkeleton(skeleton, fLocale, ec);
1740 } else {
1741 // Pattern
1742 styleID = findKeyword(style, DATE_STYLE_IDS);
1743 date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault;
1744
1745 if (typeID == 1) {
1746 fmt = DateFormat::createDateInstance(date_style, fLocale);
1747 } else {
1748 fmt = DateFormat::createTimeInstance(date_style, fLocale);
1749 }
1750
1751 if (styleID < 0 && fmt != nullptr) {
1752 SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fmt);
1753 if (sdtfmt != nullptr) {
1754 sdtfmt->applyPattern(style);
1755 }
1756 }
1757 }
1758 break;
1759
1760 case 3: // spellout
1761 formattableType = Formattable::kDouble;
1762 fmt = makeRBNF(URBNF_SPELLOUT, fLocale, style, ec);
1763 break;
1764 case 4: // ordinal
1765 formattableType = Formattable::kDouble;
1766 fmt = makeRBNF(URBNF_ORDINAL, fLocale, style, ec);
1767 break;
1768 case 5: // duration
1769 formattableType = Formattable::kDouble;
1770 fmt = makeRBNF(URBNF_DURATION, fLocale, style, ec);
1771 break;
1772 default:
1773 formattableType = Formattable::kString;
1774 ec = U_ILLEGAL_ARGUMENT_ERROR;
1775 break;
1776 }
1777
1778 return fmt;
1779}
1780
1781
1782//-------------------------------------
1783// Finds the string, s, in the string array, list.
1784int32_t MessageFormat::findKeyword(const UnicodeString& s,
1785 const char16_t * const *list)
1786{
1787 if (s.isEmpty()) {
1788 return 0; // default
1789 }
1790
1791 int32_t length = s.length();
1792 const char16_t *ps = PatternProps::trimWhiteSpace(s.getBuffer(), length);
1793 UnicodeString buffer(false, ps, length);
1794 // Trims the space characters and turns all characters
1795 // in s to lower case.
1796 buffer.toLower("");
1797 for (int32_t i = 0; list[i]; ++i) {
1798 if (!buffer.compare(list[i], u_strlenu_strlen_77(list[i]))) {
1799 return i;
1800 }
1801 }
1802 return -1;
1803}
1804
1805/**
1806 * Convenience method that ought to be in NumberFormat
1807 */
1808NumberFormat*
1809MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const {
1810 NumberFormat *temp = NumberFormat::createInstance(locale, status);
1811 DecimalFormat *temp2;
1812 if (temp != nullptr && (temp2 = dynamic_cast<DecimalFormat*>(temp)) != nullptr) {
1813 temp2->setMaximumFractionDigits(0);
1814 temp2->setDecimalSeparatorAlwaysShown(false);
1815 temp2->setParseIntegerOnly(true);
1816 }
1817
1818 return temp;
1819}
1820
1821/**
1822 * Return the default number format. Used to format a numeric
1823 * argument when subformats[i].format is nullptr. Returns nullptr
1824 * on failure.
1825 *
1826 * Semantically const but may modify *this.
1827 */
1828const NumberFormat* MessageFormat::getDefaultNumberFormat(UErrorCode& ec) const {
1829 if (defaultNumberFormat == nullptr) {
1830 MessageFormat* t = const_cast<MessageFormat*>(this);
1831 t->defaultNumberFormat = NumberFormat::createInstance(fLocale, ec);
1832 if (U_FAILURE(ec)) {
1833 delete t->defaultNumberFormat;
1834 t->defaultNumberFormat = nullptr;
1835 } else if (t->defaultNumberFormat == nullptr) {
1836 ec = U_MEMORY_ALLOCATION_ERROR;
1837 }
1838 }
1839 return defaultNumberFormat;
1840}
1841
1842/**
1843 * Return the default date format. Used to format a date
1844 * argument when subformats[i].format is nullptr. Returns nullptr
1845 * on failure.
1846 *
1847 * Semantically const but may modify *this.
1848 */
1849const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const {
1850 if (defaultDateFormat == nullptr) {
1851 MessageFormat* t = const_cast<MessageFormat*>(this);
1852 t->defaultDateFormat = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale);
1853 if (t->defaultDateFormat == nullptr) {
1854 ec = U_MEMORY_ALLOCATION_ERROR;
1855 }
1856 }
1857 return defaultDateFormat;
1858}
1859
1860UBool
1861MessageFormat::usesNamedArguments() const {
1862 return msgPattern.hasNamedArguments();
1863}
1864
1865int32_t
1866MessageFormat::getArgTypeCount() const {
1867 return argTypeCount;
1868}
1869
1870UBool MessageFormat::equalFormats(const void* left, const void* right) {
1871 return *static_cast<const Format*>(left) == *static_cast<const Format*>(right);
1872}
1873
1874
1875bool MessageFormat::DummyFormat::operator==(const Format&) const {
1876 return true;
1877}
1878
1879MessageFormat::DummyFormat* MessageFormat::DummyFormat::clone() const {
1880 return new DummyFormat();
1881}
1882
1883UnicodeString& MessageFormat::DummyFormat::format(const Formattable&,
1884 UnicodeString& appendTo,
1885 UErrorCode& status) const {
1886 if (U_SUCCESS(status)) {
1887 status = U_UNSUPPORTED_ERROR;
1888 }
1889 return appendTo;
1890}
1891
1892UnicodeString& MessageFormat::DummyFormat::format(const Formattable&,
1893 UnicodeString& appendTo,
1894 FieldPosition&,
1895 UErrorCode& status) const {
1896 if (U_SUCCESS(status)) {
1897 status = U_UNSUPPORTED_ERROR;
1898 }
1899 return appendTo;
1900}
1901
1902UnicodeString& MessageFormat::DummyFormat::format(const Formattable&,
1903 UnicodeString& appendTo,
1904 FieldPositionIterator*,
1905 UErrorCode& status) const {
1906 if (U_SUCCESS(status)) {
1907 status = U_UNSUPPORTED_ERROR;
1908 }
1909 return appendTo;
1910}
1911
1912void MessageFormat::DummyFormat::parseObject(const UnicodeString&,
1913 Formattable&,
1914 ParsePosition& ) const {
1915}
1916
1917
1918FormatNameEnumeration::FormatNameEnumeration(LocalPointer<UVector> nameList, UErrorCode& /*status*/) {
1919 pos=0;
1920 fFormatNames = std::move(nameList);
1921}
1922
1923const UnicodeString*
1924FormatNameEnumeration::snext(UErrorCode& status) {
1925 if (U_SUCCESS(status) && pos < fFormatNames->size()) {
1926 return static_cast<const UnicodeString*>(fFormatNames->elementAt(pos++));
1927 }
1928 return nullptr;
1929}
1930
1931void
1932FormatNameEnumeration::reset(UErrorCode& /*status*/) {
1933 pos=0;
1934}
1935
1936int32_t
1937FormatNameEnumeration::count(UErrorCode& /*status*/) const {
1938 return (fFormatNames==nullptr) ? 0 : fFormatNames->size();
1939}
1940
1941FormatNameEnumeration::~FormatNameEnumeration() {
1942}
1943
1944MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t)
1945 : msgFormat(mf), rules(nullptr), type(t) {
1946}
1947
1948MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() {
1949 delete rules;
1950}
1951
1952UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number,
1953 UErrorCode& ec) const {
1954 if (U_FAILURE(ec)) {
1955 return UnicodeString(false, OTHER_STRING, 5);
1956 }
1957 MessageFormat::PluralSelectorProvider* t = const_cast<MessageFormat::PluralSelectorProvider*>(this);
1958 if(rules == nullptr) {
1959 t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec);
1960 if (U_FAILURE(ec)) {
1961 return UnicodeString(false, OTHER_STRING, 5);
1962 }
1963 }
1964 // Select a sub-message according to how the number is formatted,
1965 // which is specified in the selected sub-message.
1966 // We avoid this circle by looking at how
1967 // the number is formatted in the "other" sub-message
1968 // which must always be present and usually contains the number.
1969 // Message authors should be consistent across sub-messages.
1970 PluralSelectorContext &context = *static_cast<PluralSelectorContext *>(ctx);
1971 int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex);
1972 context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName);
1973 if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != nullptr) {
1974 context.formatter =
1975 static_cast<const Format*>(uhash_igetuhash_iget_77(msgFormat.cachedFormatters, context.numberArgIndex));
1976 }
1977 if(context.formatter == nullptr) {
1978 context.formatter = msgFormat.getDefaultNumberFormat(ec);
1979 context.forReplaceNumber = true;
1980 }
1981 if (context.number.getDouble(ec) != number) {
1982 ec = U_INTERNAL_PROGRAM_ERROR;
1983 return UnicodeString(false, OTHER_STRING, 5);
1984 }
1985 context.formatter->format(context.number, context.numberString, ec);
1986 const auto* decFmt = dynamic_cast<const DecimalFormat*>(context.formatter);
1987 if(decFmt != nullptr) {
1988 number::impl::DecimalQuantity dq;
1989 decFmt->formatToDecimalQuantity(context.number, dq, ec);
1990 if (U_FAILURE(ec)) {
1991 return UnicodeString(false, OTHER_STRING, 5);
1992 }
1993 return rules->select(dq);
1994 } else {
1995 return rules->select(number);
1996 }
1997}
1998
1999void MessageFormat::PluralSelectorProvider::reset() {
2000 delete rules;
2001 rules = nullptr;
2002}
2003
2004
2005U_NAMESPACE_END}
2006
2007#endif /* #if !UCONFIG_NO_FORMATTING */
2008
2009//eof