intl/icu/source/i18n/msgfmt.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial