michael@0: /******************************************************************** michael@0: * COPYRIGHT: michael@0: * Copyright (c) 1997-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************** michael@0: * michael@0: * File MSGFMT.CPP michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 02/19/97 aliu Converted from java. michael@0: * 03/20/97 helena Finished first cut of implementation. michael@0: * 04/10/97 aliu Made to work on AIX. Added stoi to replace wtoi. michael@0: * 06/11/97 helena Fixed addPattern to take the pattern correctly. michael@0: * 06/17/97 helena Fixed the getPattern to return the correct pattern. michael@0: * 07/09/97 helena Made ParsePosition into a class. michael@0: * 02/22/99 stephen Removed character literals for EBCDIC safety michael@0: * 11/01/09 kirtig Added SelectFormat michael@0: ********************************************************************/ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/appendable.h" michael@0: #include "unicode/choicfmt.h" michael@0: #include "unicode/datefmt.h" michael@0: #include "unicode/decimfmt.h" michael@0: #include "unicode/localpointer.h" michael@0: #include "unicode/msgfmt.h" michael@0: #include "unicode/plurfmt.h" michael@0: #include "unicode/rbnf.h" michael@0: #include "unicode/selfmt.h" michael@0: #include "unicode/smpdtfmt.h" michael@0: #include "unicode/umsg.h" michael@0: #include "unicode/ustring.h" michael@0: #include "cmemory.h" michael@0: #include "patternprops.h" michael@0: #include "messageimpl.h" michael@0: #include "msgfmt_impl.h" michael@0: #include "plurrule_impl.h" michael@0: #include "uassert.h" michael@0: #include "uelement.h" michael@0: #include "uhash.h" michael@0: #include "ustrfmt.h" michael@0: #include "util.h" michael@0: #include "uvector.h" michael@0: michael@0: // ***************************************************************************** michael@0: // class MessageFormat michael@0: // ***************************************************************************** michael@0: michael@0: #define SINGLE_QUOTE ((UChar)0x0027) michael@0: #define COMMA ((UChar)0x002C) michael@0: #define LEFT_CURLY_BRACE ((UChar)0x007B) michael@0: #define RIGHT_CURLY_BRACE ((UChar)0x007D) michael@0: michael@0: //--------------------------------------- michael@0: // static data michael@0: michael@0: static const UChar ID_NUMBER[] = { michael@0: 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */ michael@0: }; michael@0: static const UChar ID_DATE[] = { michael@0: 0x64, 0x61, 0x74, 0x65, 0 /* "date" */ michael@0: }; michael@0: static const UChar ID_TIME[] = { michael@0: 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */ michael@0: }; michael@0: static const UChar ID_SPELLOUT[] = { michael@0: 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */ michael@0: }; michael@0: static const UChar ID_ORDINAL[] = { michael@0: 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */ michael@0: }; michael@0: static const UChar ID_DURATION[] = { michael@0: 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */ michael@0: }; michael@0: michael@0: // MessageFormat Type List Number, Date, Time or Choice michael@0: static const UChar * const TYPE_IDS[] = { michael@0: ID_NUMBER, michael@0: ID_DATE, michael@0: ID_TIME, michael@0: ID_SPELLOUT, michael@0: ID_ORDINAL, michael@0: ID_DURATION, michael@0: NULL, michael@0: }; michael@0: michael@0: static const UChar ID_EMPTY[] = { michael@0: 0 /* empty string, used for default so that null can mark end of list */ michael@0: }; michael@0: static const UChar ID_CURRENCY[] = { michael@0: 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */ michael@0: }; michael@0: static const UChar ID_PERCENT[] = { michael@0: 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */ michael@0: }; michael@0: static const UChar ID_INTEGER[] = { michael@0: 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */ michael@0: }; michael@0: michael@0: // NumberFormat modifier list, default, currency, percent or integer michael@0: static const UChar * const NUMBER_STYLE_IDS[] = { michael@0: ID_EMPTY, michael@0: ID_CURRENCY, michael@0: ID_PERCENT, michael@0: ID_INTEGER, michael@0: NULL, michael@0: }; michael@0: michael@0: static const UChar ID_SHORT[] = { michael@0: 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */ michael@0: }; michael@0: static const UChar ID_MEDIUM[] = { michael@0: 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */ michael@0: }; michael@0: static const UChar ID_LONG[] = { michael@0: 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */ michael@0: }; michael@0: static const UChar ID_FULL[] = { michael@0: 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */ michael@0: }; michael@0: michael@0: // DateFormat modifier list, default, short, medium, long or full michael@0: static const UChar * const DATE_STYLE_IDS[] = { michael@0: ID_EMPTY, michael@0: ID_SHORT, michael@0: ID_MEDIUM, michael@0: ID_LONG, michael@0: ID_FULL, michael@0: NULL, michael@0: }; michael@0: michael@0: static const icu::DateFormat::EStyle DATE_STYLES[] = { michael@0: icu::DateFormat::kDefault, michael@0: icu::DateFormat::kShort, michael@0: icu::DateFormat::kMedium, michael@0: icu::DateFormat::kLong, michael@0: icu::DateFormat::kFull, michael@0: }; michael@0: michael@0: static const int32_t DEFAULT_INITIAL_CAPACITY = 10; michael@0: michael@0: static const UChar NULL_STRING[] = { michael@0: 0x6E, 0x75, 0x6C, 0x6C, 0 // "null" michael@0: }; michael@0: michael@0: static const UChar OTHER_STRING[] = { michael@0: 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" michael@0: }; michael@0: michael@0: U_CDECL_BEGIN michael@0: static UBool U_CALLCONV equalFormatsForHash(const UHashTok key1, michael@0: const UHashTok key2) { michael@0: return icu::MessageFormat::equalFormats(key1.pointer, key2.pointer); michael@0: } michael@0: michael@0: U_CDECL_END michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: // ------------------------------------- michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat) michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration) michael@0: michael@0: //-------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Convert an integer value to a string and append the result to michael@0: * the given UnicodeString. michael@0: */ michael@0: static UnicodeString& itos(int32_t i, UnicodeString& appendTo) { michael@0: UChar temp[16]; michael@0: uprv_itou(temp,16,i,10,0); // 10 == radix michael@0: appendTo.append(temp, -1); michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: // AppendableWrapper: encapsulates the result of formatting, keeping track michael@0: // of the string and its length. michael@0: class AppendableWrapper : public UMemory { michael@0: public: michael@0: AppendableWrapper(Appendable& appendable) : app(appendable), len(0) { michael@0: } michael@0: void append(const UnicodeString& s) { michael@0: app.appendString(s.getBuffer(), s.length()); michael@0: len += s.length(); michael@0: } michael@0: void append(const UChar* s, const int32_t sLength) { michael@0: app.appendString(s, sLength); michael@0: len += sLength; michael@0: } michael@0: void append(const UnicodeString& s, int32_t start, int32_t length) { michael@0: append(s.tempSubString(start, length)); michael@0: } michael@0: void formatAndAppend(const Format* formatter, const Formattable& arg, UErrorCode& ec) { michael@0: UnicodeString s; michael@0: formatter->format(arg, s, ec); michael@0: if (U_SUCCESS(ec)) { michael@0: append(s); michael@0: } michael@0: } michael@0: void formatAndAppend(const Format* formatter, const Formattable& arg, michael@0: const UnicodeString &argString, UErrorCode& ec) { michael@0: if (!argString.isEmpty()) { michael@0: if (U_SUCCESS(ec)) { michael@0: append(argString); michael@0: } michael@0: } else { michael@0: formatAndAppend(formatter, arg, ec); michael@0: } michael@0: } michael@0: int32_t length() { michael@0: return len; michael@0: } michael@0: private: michael@0: Appendable& app; michael@0: int32_t len; michael@0: }; michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // Creates a MessageFormat instance based on the pattern. michael@0: michael@0: MessageFormat::MessageFormat(const UnicodeString& pattern, michael@0: UErrorCode& success) michael@0: : fLocale(Locale::getDefault()), // Uses the default locale michael@0: msgPattern(success), michael@0: formatAliases(NULL), michael@0: formatAliasesCapacity(0), michael@0: argTypes(NULL), michael@0: argTypeCount(0), michael@0: argTypeCapacity(0), michael@0: hasArgTypeConflicts(FALSE), michael@0: defaultNumberFormat(NULL), michael@0: defaultDateFormat(NULL), michael@0: cachedFormatters(NULL), michael@0: customFormatArgStarts(NULL), michael@0: pluralProvider(*this, UPLURAL_TYPE_CARDINAL), michael@0: ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) michael@0: { michael@0: setLocaleIDs(fLocale.getName(), fLocale.getName()); michael@0: applyPattern(pattern, success); michael@0: } michael@0: michael@0: MessageFormat::MessageFormat(const UnicodeString& pattern, michael@0: const Locale& newLocale, michael@0: UErrorCode& success) michael@0: : fLocale(newLocale), michael@0: msgPattern(success), michael@0: formatAliases(NULL), michael@0: formatAliasesCapacity(0), michael@0: argTypes(NULL), michael@0: argTypeCount(0), michael@0: argTypeCapacity(0), michael@0: hasArgTypeConflicts(FALSE), michael@0: defaultNumberFormat(NULL), michael@0: defaultDateFormat(NULL), michael@0: cachedFormatters(NULL), michael@0: customFormatArgStarts(NULL), michael@0: pluralProvider(*this, UPLURAL_TYPE_CARDINAL), michael@0: ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) michael@0: { michael@0: setLocaleIDs(fLocale.getName(), fLocale.getName()); michael@0: applyPattern(pattern, success); michael@0: } michael@0: michael@0: MessageFormat::MessageFormat(const UnicodeString& pattern, michael@0: const Locale& newLocale, michael@0: UParseError& parseError, michael@0: UErrorCode& success) michael@0: : fLocale(newLocale), michael@0: msgPattern(success), michael@0: formatAliases(NULL), michael@0: formatAliasesCapacity(0), michael@0: argTypes(NULL), michael@0: argTypeCount(0), michael@0: argTypeCapacity(0), michael@0: hasArgTypeConflicts(FALSE), michael@0: defaultNumberFormat(NULL), michael@0: defaultDateFormat(NULL), michael@0: cachedFormatters(NULL), michael@0: customFormatArgStarts(NULL), michael@0: pluralProvider(*this, UPLURAL_TYPE_CARDINAL), michael@0: ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) michael@0: { michael@0: setLocaleIDs(fLocale.getName(), fLocale.getName()); michael@0: applyPattern(pattern, parseError, success); michael@0: } michael@0: michael@0: MessageFormat::MessageFormat(const MessageFormat& that) michael@0: : michael@0: Format(that), michael@0: fLocale(that.fLocale), michael@0: msgPattern(that.msgPattern), michael@0: formatAliases(NULL), michael@0: formatAliasesCapacity(0), michael@0: argTypes(NULL), michael@0: argTypeCount(0), michael@0: argTypeCapacity(0), michael@0: hasArgTypeConflicts(that.hasArgTypeConflicts), michael@0: defaultNumberFormat(NULL), michael@0: defaultDateFormat(NULL), michael@0: cachedFormatters(NULL), michael@0: customFormatArgStarts(NULL), michael@0: pluralProvider(*this, UPLURAL_TYPE_CARDINAL), michael@0: ordinalProvider(*this, UPLURAL_TYPE_ORDINAL) michael@0: { michael@0: // This will take care of creating the hash tables (since they are NULL). michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: copyObjects(that, ec); michael@0: if (U_FAILURE(ec)) { michael@0: resetPattern(); michael@0: } michael@0: } michael@0: michael@0: MessageFormat::~MessageFormat() michael@0: { michael@0: uhash_close(cachedFormatters); michael@0: uhash_close(customFormatArgStarts); michael@0: michael@0: uprv_free(argTypes); michael@0: uprv_free(formatAliases); michael@0: delete defaultNumberFormat; michael@0: delete defaultDateFormat; michael@0: } michael@0: michael@0: //-------------------------------------------------------------------- michael@0: // Variable-size array management michael@0: michael@0: /** michael@0: * Allocate argTypes[] to at least the given capacity and return michael@0: * TRUE if successful. If not, leave argTypes[] unchanged. michael@0: * michael@0: * If argTypes is NULL, allocate it. If it is not NULL, enlarge it michael@0: * if necessary to be at least as large as specified. michael@0: */ michael@0: UBool MessageFormat::allocateArgTypes(int32_t capacity, UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return FALSE; michael@0: } michael@0: if (argTypeCapacity >= capacity) { michael@0: return TRUE; michael@0: } michael@0: if (capacity < DEFAULT_INITIAL_CAPACITY) { michael@0: capacity = DEFAULT_INITIAL_CAPACITY; michael@0: } else if (capacity < 2*argTypeCapacity) { michael@0: capacity = 2*argTypeCapacity; michael@0: } michael@0: Formattable::Type* a = (Formattable::Type*) michael@0: uprv_realloc(argTypes, sizeof(*argTypes) * capacity); michael@0: if (a == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return FALSE; michael@0: } michael@0: argTypes = a; michael@0: argTypeCapacity = capacity; michael@0: return TRUE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // assignment operator michael@0: michael@0: const MessageFormat& michael@0: MessageFormat::operator=(const MessageFormat& that) michael@0: { michael@0: if (this != &that) { michael@0: // Calls the super class for assignment first. michael@0: Format::operator=(that); michael@0: michael@0: setLocale(that.fLocale); michael@0: msgPattern = that.msgPattern; michael@0: hasArgTypeConflicts = that.hasArgTypeConflicts; michael@0: michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: copyObjects(that, ec); michael@0: if (U_FAILURE(ec)) { michael@0: resetPattern(); michael@0: } michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: UBool michael@0: MessageFormat::operator==(const Format& rhs) const michael@0: { michael@0: if (this == &rhs) return TRUE; michael@0: michael@0: MessageFormat& that = (MessageFormat&)rhs; michael@0: michael@0: // Check class ID before checking MessageFormat members michael@0: if (!Format::operator==(rhs) || michael@0: msgPattern != that.msgPattern || michael@0: fLocale != that.fLocale) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // Compare hashtables. michael@0: if ((customFormatArgStarts == NULL) != (that.customFormatArgStarts == NULL)) { michael@0: return FALSE; michael@0: } michael@0: if (customFormatArgStarts == NULL) { michael@0: return TRUE; michael@0: } michael@0: michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: const int32_t count = uhash_count(customFormatArgStarts); michael@0: const int32_t rhs_count = uhash_count(that.customFormatArgStarts); michael@0: if (count != rhs_count) { michael@0: return FALSE; michael@0: } michael@0: int32_t idx = 0, rhs_idx = 0, pos = -1, rhs_pos = -1; michael@0: for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) { michael@0: const UHashElement* cur = uhash_nextElement(customFormatArgStarts, &pos); michael@0: const UHashElement* rhs_cur = uhash_nextElement(that.customFormatArgStarts, &rhs_pos); michael@0: if (cur->key.integer != rhs_cur->key.integer) { michael@0: return FALSE; michael@0: } michael@0: const Format* format = (const Format*)uhash_iget(cachedFormatters, cur->key.integer); michael@0: const Format* rhs_format = (const Format*)uhash_iget(that.cachedFormatters, rhs_cur->key.integer); michael@0: if (*format != *rhs_format) { michael@0: return FALSE; michael@0: } michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Creates a copy of this MessageFormat, the caller owns the copy. michael@0: michael@0: Format* michael@0: MessageFormat::clone() const michael@0: { michael@0: return new MessageFormat(*this); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the locale of this MessageFormat object to theLocale. michael@0: michael@0: void michael@0: MessageFormat::setLocale(const Locale& theLocale) michael@0: { michael@0: if (fLocale != theLocale) { michael@0: delete defaultNumberFormat; michael@0: defaultNumberFormat = NULL; michael@0: delete defaultDateFormat; michael@0: defaultDateFormat = NULL; michael@0: fLocale = theLocale; michael@0: setLocaleIDs(fLocale.getName(), fLocale.getName()); michael@0: pluralProvider.reset(); michael@0: ordinalProvider.reset(); michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the locale of this MessageFormat object. michael@0: michael@0: const Locale& michael@0: MessageFormat::getLocale() const michael@0: { michael@0: return fLocale; michael@0: } michael@0: michael@0: void michael@0: MessageFormat::applyPattern(const UnicodeString& newPattern, michael@0: UErrorCode& status) michael@0: { michael@0: UParseError parseError; michael@0: applyPattern(newPattern,parseError,status); michael@0: } michael@0: michael@0: michael@0: // ------------------------------------- michael@0: // Applies the new pattern and returns an error if the pattern michael@0: // is not correct. michael@0: void michael@0: MessageFormat::applyPattern(const UnicodeString& pattern, michael@0: UParseError& parseError, michael@0: UErrorCode& ec) michael@0: { michael@0: if(U_FAILURE(ec)) { michael@0: return; michael@0: } michael@0: msgPattern.parse(pattern, &parseError, ec); michael@0: cacheExplicitFormats(ec); michael@0: michael@0: if (U_FAILURE(ec)) { michael@0: resetPattern(); michael@0: } michael@0: } michael@0: michael@0: void MessageFormat::resetPattern() { michael@0: msgPattern.clear(); michael@0: uhash_close(cachedFormatters); michael@0: cachedFormatters = NULL; michael@0: uhash_close(customFormatArgStarts); michael@0: customFormatArgStarts = NULL; michael@0: argTypeCount = 0; michael@0: hasArgTypeConflicts = FALSE; michael@0: } michael@0: michael@0: void michael@0: MessageFormat::applyPattern(const UnicodeString& pattern, michael@0: UMessagePatternApostropheMode aposMode, michael@0: UParseError* parseError, michael@0: UErrorCode& status) { michael@0: if (aposMode != msgPattern.getApostropheMode()) { michael@0: msgPattern.clearPatternAndSetApostropheMode(aposMode); michael@0: } michael@0: applyPattern(pattern, *parseError, status); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Converts this MessageFormat instance to a pattern. michael@0: michael@0: UnicodeString& michael@0: MessageFormat::toPattern(UnicodeString& appendTo) const { michael@0: if ((customFormatArgStarts != NULL && 0 != uhash_count(customFormatArgStarts)) || michael@0: 0 == msgPattern.countParts() michael@0: ) { michael@0: appendTo.setToBogus(); michael@0: return appendTo; michael@0: } michael@0: return appendTo.append(msgPattern.getPatternString()); michael@0: } michael@0: michael@0: int32_t MessageFormat::nextTopLevelArgStart(int32_t partIndex) const { michael@0: if (partIndex != 0) { michael@0: partIndex = msgPattern.getLimitPartIndex(partIndex); michael@0: } michael@0: for (;;) { michael@0: UMessagePatternPartType type = msgPattern.getPartType(++partIndex); michael@0: if (type == UMSGPAT_PART_TYPE_ARG_START) { michael@0: return partIndex; michael@0: } michael@0: if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { michael@0: return -1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void MessageFormat::setArgStartFormat(int32_t argStart, michael@0: Format* formatter, michael@0: UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: delete formatter; michael@0: return; michael@0: } michael@0: if (cachedFormatters == NULL) { michael@0: cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, michael@0: equalFormatsForHash, &status); michael@0: if (U_FAILURE(status)) { michael@0: delete formatter; michael@0: return; michael@0: } michael@0: uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); michael@0: } michael@0: if (formatter == NULL) { michael@0: formatter = new DummyFormat(); michael@0: } michael@0: uhash_iput(cachedFormatters, argStart, formatter, &status); michael@0: } michael@0: michael@0: michael@0: UBool MessageFormat::argNameMatches(int32_t partIndex, const UnicodeString& argName, int32_t argNumber) { michael@0: const MessagePattern::Part& part = msgPattern.getPart(partIndex); michael@0: return part.getType() == UMSGPAT_PART_TYPE_ARG_NAME ? michael@0: msgPattern.partSubstringMatches(part, argName) : michael@0: part.getValue() == argNumber; // ARG_NUMBER michael@0: } michael@0: michael@0: // Sets a custom formatter for a MessagePattern ARG_START part index. michael@0: // "Custom" formatters are provided by the user via setFormat() or similar APIs. michael@0: void MessageFormat::setCustomArgStartFormat(int32_t argStart, michael@0: Format* formatter, michael@0: UErrorCode& status) { michael@0: setArgStartFormat(argStart, formatter, status); michael@0: if (customFormatArgStarts == NULL) { michael@0: customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, michael@0: NULL, &status); michael@0: } michael@0: uhash_iputi(customFormatArgStarts, argStart, 1, &status); michael@0: } michael@0: michael@0: Format* MessageFormat::getCachedFormatter(int32_t argumentNumber) const { michael@0: if (cachedFormatters == NULL) { michael@0: return NULL; michael@0: } michael@0: void* ptr = uhash_iget(cachedFormatters, argumentNumber); michael@0: if (ptr != NULL && dynamic_cast((Format*)ptr) == NULL) { michael@0: return (Format*) ptr; michael@0: } else { michael@0: // Not cached, or a DummyFormat representing setFormat(NULL). michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Adopts the new formats array and updates the array count. michael@0: // This MessageFormat instance owns the new formats. michael@0: void michael@0: MessageFormat::adoptFormats(Format** newFormats, michael@0: int32_t count) { michael@0: if (newFormats == NULL || count < 0) { michael@0: return; michael@0: } michael@0: // Throw away any cached formatters. michael@0: if (cachedFormatters != NULL) { michael@0: uhash_removeAll(cachedFormatters); michael@0: } michael@0: if (customFormatArgStarts != NULL) { michael@0: uhash_removeAll(customFormatArgStarts); michael@0: } michael@0: michael@0: int32_t formatNumber = 0; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: for (int32_t partIndex = 0; michael@0: formatNumber < count && U_SUCCESS(status) && michael@0: (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: setCustomArgStartFormat(partIndex, newFormats[formatNumber], status); michael@0: ++formatNumber; michael@0: } michael@0: // Delete those that didn't get used (if any). michael@0: for (; formatNumber < count; ++formatNumber) { michael@0: delete newFormats[formatNumber]; michael@0: } michael@0: michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Sets the new formats array and updates the array count. michael@0: // This MessageFormat instance maks a copy of the new formats. michael@0: michael@0: void michael@0: MessageFormat::setFormats(const Format** newFormats, michael@0: int32_t count) { michael@0: if (newFormats == NULL || count < 0) { michael@0: return; michael@0: } michael@0: // Throw away any cached formatters. michael@0: if (cachedFormatters != NULL) { michael@0: uhash_removeAll(cachedFormatters); michael@0: } michael@0: if (customFormatArgStarts != NULL) { michael@0: uhash_removeAll(customFormatArgStarts); michael@0: } michael@0: michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t formatNumber = 0; michael@0: for (int32_t partIndex = 0; michael@0: formatNumber < count && U_SUCCESS(status) && (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: Format* newFormat = NULL; michael@0: if (newFormats[formatNumber] != NULL) { michael@0: newFormat = newFormats[formatNumber]->clone(); michael@0: if (newFormat == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: setCustomArgStartFormat(partIndex, newFormat, status); michael@0: ++formatNumber; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: resetPattern(); michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Adopt a single format by format number. michael@0: // Do nothing if the format number is not less than the array count. michael@0: michael@0: void michael@0: MessageFormat::adoptFormat(int32_t n, Format *newFormat) { michael@0: LocalPointer p(newFormat); michael@0: if (n >= 0) { michael@0: int32_t formatNumber = 0; michael@0: for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: if (n == formatNumber) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: setCustomArgStartFormat(partIndex, p.orphan(), status); michael@0: return; michael@0: } michael@0: ++formatNumber; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Adopt a single format by format name. michael@0: // Do nothing if there is no match of formatName. michael@0: void michael@0: MessageFormat::adoptFormat(const UnicodeString& formatName, michael@0: Format* formatToAdopt, michael@0: UErrorCode& status) { michael@0: LocalPointer p(formatToAdopt); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: int32_t argNumber = MessagePattern::validateArgumentName(formatName); michael@0: if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: for (int32_t partIndex = 0; michael@0: (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); michael@0: ) { michael@0: if (argNameMatches(partIndex + 1, formatName, argNumber)) { michael@0: Format* f; michael@0: if (p.isValid()) { michael@0: f = p.orphan(); michael@0: } else if (formatToAdopt == NULL) { michael@0: f = NULL; michael@0: } else { michael@0: f = formatToAdopt->clone(); michael@0: if (f == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: setCustomArgStartFormat(partIndex, f, status); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Set a single format. michael@0: // Do nothing if the variable is not less than the array count. michael@0: void michael@0: MessageFormat::setFormat(int32_t n, const Format& newFormat) { michael@0: michael@0: if (n >= 0) { michael@0: int32_t formatNumber = 0; michael@0: for (int32_t partIndex = 0; michael@0: (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: if (n == formatNumber) { michael@0: Format* new_format = newFormat.clone(); michael@0: if (new_format) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: setCustomArgStartFormat(partIndex, new_format, status); michael@0: } michael@0: return; michael@0: } michael@0: ++formatNumber; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Get a single format by format name. michael@0: // Do nothing if the variable is not less than the array count. michael@0: Format * michael@0: MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) { michael@0: if (U_FAILURE(status) || cachedFormatters == NULL) return NULL; michael@0: michael@0: int32_t argNumber = MessagePattern::validateArgumentName(formatName); michael@0: if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return NULL; michael@0: } michael@0: for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: if (argNameMatches(partIndex + 1, formatName, argNumber)) { michael@0: return getCachedFormatter(partIndex); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Set a single format by format name michael@0: // Do nothing if the variable is not less than the array count. michael@0: void michael@0: MessageFormat::setFormat(const UnicodeString& formatName, michael@0: const Format& newFormat, michael@0: UErrorCode& status) { michael@0: if (U_FAILURE(status)) return; michael@0: michael@0: int32_t argNumber = MessagePattern::validateArgumentName(formatName); michael@0: if (argNumber < UMSGPAT_ARG_NAME_NOT_NUMBER) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: for (int32_t partIndex = 0; michael@0: (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status); michael@0: ) { michael@0: if (argNameMatches(partIndex + 1, formatName, argNumber)) { michael@0: if (&newFormat == NULL) { michael@0: setCustomArgStartFormat(partIndex, NULL, status); michael@0: } else { michael@0: Format* new_format = newFormat.clone(); michael@0: if (new_format == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: setCustomArgStartFormat(partIndex, new_format, status); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Gets the format array. michael@0: const Format** michael@0: MessageFormat::getFormats(int32_t& cnt) const michael@0: { michael@0: // This old API returns an array (which we hold) of Format* michael@0: // pointers. The array is valid up to the next call to any michael@0: // method on this object. We construct and resize an array michael@0: // on demand that contains aliases to the subformats[i].format michael@0: // pointers. michael@0: MessageFormat* t = const_cast (this); michael@0: cnt = 0; michael@0: if (formatAliases == NULL) { michael@0: t->formatAliasesCapacity = (argTypeCount<10) ? 10 : argTypeCount; michael@0: Format** a = (Format**) michael@0: uprv_malloc(sizeof(Format*) * formatAliasesCapacity); michael@0: if (a == NULL) { michael@0: t->formatAliasesCapacity = 0; michael@0: return NULL; michael@0: } michael@0: t->formatAliases = a; michael@0: } else if (argTypeCount > formatAliasesCapacity) { michael@0: Format** a = (Format**) michael@0: uprv_realloc(formatAliases, sizeof(Format*) * argTypeCount); michael@0: if (a == NULL) { michael@0: t->formatAliasesCapacity = 0; michael@0: return NULL; michael@0: } michael@0: t->formatAliases = a; michael@0: t->formatAliasesCapacity = argTypeCount; michael@0: } michael@0: michael@0: for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: t->formatAliases[cnt++] = getCachedFormatter(partIndex); michael@0: } michael@0: michael@0: return (const Format**)formatAliases; michael@0: } michael@0: michael@0: michael@0: UnicodeString MessageFormat::getArgName(int32_t partIndex) { michael@0: const MessagePattern::Part& part = msgPattern.getPart(partIndex); michael@0: return msgPattern.getSubstring(part); michael@0: } michael@0: michael@0: StringEnumeration* michael@0: MessageFormat::getFormatNames(UErrorCode& status) { michael@0: if (U_FAILURE(status)) return NULL; michael@0: michael@0: UVector *fFormatNames = new UVector(status); michael@0: if (U_FAILURE(status)) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: fFormatNames->setDeleter(uprv_deleteUObject); michael@0: michael@0: for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) { michael@0: fFormatNames->addElement(new UnicodeString(getArgName(partIndex + 1)), status); michael@0: } michael@0: michael@0: StringEnumeration* nameEnumerator = new FormatNameEnumeration(fFormatNames, status); michael@0: return nameEnumerator; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Formats the source Formattable array and copy into the result buffer. michael@0: // Ignore the FieldPosition result for error checking. michael@0: michael@0: UnicodeString& michael@0: MessageFormat::format(const Formattable* source, michael@0: int32_t cnt, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& ignore, michael@0: UErrorCode& success) const michael@0: { michael@0: return format(source, NULL, cnt, appendTo, &ignore, success); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Internally creates a MessageFormat instance based on the michael@0: // pattern and formats the arguments Formattable array and michael@0: // copy into the appendTo buffer. michael@0: michael@0: UnicodeString& michael@0: MessageFormat::format( const UnicodeString& pattern, michael@0: const Formattable* arguments, michael@0: int32_t cnt, michael@0: UnicodeString& appendTo, michael@0: UErrorCode& success) michael@0: { michael@0: MessageFormat temp(pattern, success); michael@0: return temp.format(arguments, NULL, cnt, appendTo, NULL, success); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Formats the source Formattable object and copy into the michael@0: // appendTo buffer. The Formattable object must be an array michael@0: // of Formattable instances, returns error otherwise. michael@0: michael@0: UnicodeString& michael@0: MessageFormat::format(const Formattable& source, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& ignore, michael@0: UErrorCode& success) const michael@0: { michael@0: if (U_FAILURE(success)) michael@0: return appendTo; michael@0: if (source.getType() != Formattable::kArray) { michael@0: success = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return appendTo; michael@0: } michael@0: int32_t cnt; michael@0: const Formattable* tmpPtr = source.getArray(cnt); michael@0: return format(tmpPtr, NULL, cnt, appendTo, &ignore, success); michael@0: } michael@0: michael@0: UnicodeString& michael@0: MessageFormat::format(const UnicodeString* argumentNames, michael@0: const Formattable* arguments, michael@0: int32_t count, michael@0: UnicodeString& appendTo, michael@0: UErrorCode& success) const { michael@0: return format(arguments, argumentNames, count, appendTo, NULL, success); michael@0: } michael@0: michael@0: // Does linear search to find the match for an ArgName. michael@0: const Formattable* MessageFormat::getArgFromListByName(const Formattable* arguments, michael@0: const UnicodeString *argumentNames, michael@0: int32_t cnt, UnicodeString& name) const { michael@0: for (int32_t i = 0; i < cnt; ++i) { michael@0: if (0 == argumentNames[i].compare(name)) { michael@0: return arguments + i; michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: UnicodeString& michael@0: MessageFormat::format(const Formattable* arguments, michael@0: const UnicodeString *argumentNames, michael@0: int32_t cnt, michael@0: UnicodeString& appendTo, michael@0: FieldPosition* pos, michael@0: UErrorCode& status) const { michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: michael@0: UnicodeStringAppendable usapp(appendTo); michael@0: AppendableWrapper app(usapp); michael@0: format(0, NULL, arguments, argumentNames, cnt, app, pos, status); michael@0: return appendTo; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * Mutable input/output values for the PluralSelectorProvider. michael@0: * Separate so that it is possible to make MessageFormat Freezable. michael@0: */ michael@0: class PluralSelectorContext { michael@0: public: michael@0: PluralSelectorContext(int32_t start, const UnicodeString &name, michael@0: const Formattable &num, double off, UErrorCode &errorCode) michael@0: : startIndex(start), argName(name), offset(off), michael@0: numberArgIndex(-1), formatter(NULL), forReplaceNumber(FALSE) { michael@0: // number needs to be set even when select() is not called. michael@0: // Keep it as a Number/Formattable: michael@0: // For format() methods, and to preserve information (e.g., BigDecimal). michael@0: if(off == 0) { michael@0: number = num; michael@0: } else { michael@0: number = num.getDouble(errorCode) - off; michael@0: } michael@0: } michael@0: michael@0: // Input values for plural selection with decimals. michael@0: int32_t startIndex; michael@0: const UnicodeString &argName; michael@0: /** argument number - plural offset */ michael@0: Formattable number; michael@0: double offset; michael@0: // Output values for plural selection with decimals. michael@0: /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */ michael@0: int32_t numberArgIndex; michael@0: const Format *formatter; michael@0: /** formatted argument number - plural offset */ michael@0: UnicodeString numberString; michael@0: /** TRUE if number-offset was formatted with the stock number formatter */ michael@0: UBool forReplaceNumber; michael@0: }; michael@0: michael@0: } // namespace michael@0: michael@0: // if argumentNames is NULL, this means arguments is a numeric array. michael@0: // arguments can not be NULL. michael@0: // We use const void *plNumber rather than const PluralSelectorContext *pluralNumber michael@0: // so that we need not declare the PluralSelectorContext in the public header file. michael@0: void MessageFormat::format(int32_t msgStart, const void *plNumber, michael@0: const Formattable* arguments, michael@0: const UnicodeString *argumentNames, michael@0: int32_t cnt, michael@0: AppendableWrapper& appendTo, michael@0: FieldPosition* ignore, michael@0: UErrorCode& success) const { michael@0: if (U_FAILURE(success)) { michael@0: return; michael@0: } michael@0: michael@0: const UnicodeString& msgString = msgPattern.getPatternString(); michael@0: int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); michael@0: for (int32_t i = msgStart + 1; U_SUCCESS(success) ; ++i) { michael@0: const MessagePattern::Part* part = &msgPattern.getPart(i); michael@0: const UMessagePatternPartType type = part->getType(); michael@0: int32_t index = part->getIndex(); michael@0: appendTo.append(msgString, prevIndex, index - prevIndex); michael@0: if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { michael@0: return; michael@0: } michael@0: prevIndex = part->getLimit(); michael@0: if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { michael@0: const PluralSelectorContext &pluralNumber = michael@0: *static_cast(plNumber); michael@0: if(pluralNumber.forReplaceNumber) { michael@0: // number-offset was already formatted. michael@0: appendTo.formatAndAppend(pluralNumber.formatter, michael@0: pluralNumber.number, pluralNumber.numberString, success); michael@0: } else { michael@0: const NumberFormat* nf = getDefaultNumberFormat(success); michael@0: appendTo.formatAndAppend(nf, pluralNumber.number, success); michael@0: } michael@0: continue; michael@0: } michael@0: if (type != UMSGPAT_PART_TYPE_ARG_START) { michael@0: continue; michael@0: } michael@0: int32_t argLimit = msgPattern.getLimitPartIndex(i); michael@0: UMessagePatternArgType argType = part->getArgType(); michael@0: part = &msgPattern.getPart(++i); michael@0: const Formattable* arg; michael@0: UBool noArg = FALSE; michael@0: UnicodeString argName = msgPattern.getSubstring(*part); michael@0: if (argumentNames == NULL) { michael@0: int32_t argNumber = part->getValue(); // ARG_NUMBER michael@0: if (0 <= argNumber && argNumber < cnt) { michael@0: arg = arguments + argNumber; michael@0: } else { michael@0: arg = NULL; michael@0: noArg = TRUE; michael@0: } michael@0: } else { michael@0: arg = getArgFromListByName(arguments, argumentNames, cnt, argName); michael@0: if (arg == NULL) { michael@0: noArg = TRUE; michael@0: } michael@0: } michael@0: ++i; michael@0: int32_t prevDestLength = appendTo.length(); michael@0: const Format* formatter = NULL; michael@0: if (noArg) { michael@0: appendTo.append( michael@0: UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE)); michael@0: } else if (arg == NULL) { michael@0: appendTo.append(NULL_STRING, 4); michael@0: } else if(plNumber!=NULL && michael@0: static_cast(plNumber)->numberArgIndex==(i-2)) { michael@0: const PluralSelectorContext &pluralNumber = michael@0: *static_cast(plNumber); michael@0: if(pluralNumber.offset == 0) { michael@0: // The number was already formatted with this formatter. michael@0: appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number, michael@0: pluralNumber.numberString, success); michael@0: } else { michael@0: // Do not use the formatted (number-offset) string for a named argument michael@0: // that formats the number without subtracting the offset. michael@0: appendTo.formatAndAppend(pluralNumber.formatter, *arg, success); michael@0: } michael@0: } else if ((formatter = getCachedFormatter(i -2))) { michael@0: // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings. michael@0: if (dynamic_cast(formatter) || michael@0: dynamic_cast(formatter) || michael@0: dynamic_cast(formatter)) { michael@0: // We only handle nested formats here if they were provided via michael@0: // setFormat() or its siblings. Otherwise they are not cached and instead michael@0: // handled below according to argType. michael@0: UnicodeString subMsgString; michael@0: formatter->format(*arg, subMsgString, success); michael@0: if (subMsgString.indexOf(LEFT_CURLY_BRACE) >= 0 || michael@0: (subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern)) michael@0: ) { michael@0: MessageFormat subMsgFormat(subMsgString, fLocale, success); michael@0: subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, ignore, success); michael@0: } else { michael@0: appendTo.append(subMsgString); michael@0: } michael@0: } else { michael@0: appendTo.formatAndAppend(formatter, *arg, success); michael@0: } michael@0: } else if (argType == UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i - 2))) { michael@0: // We arrive here if getCachedFormatter returned NULL, but there was actually an element in the hash table. michael@0: // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check michael@0: // for the hash table containind DummyFormat. michael@0: if (arg->isNumeric()) { michael@0: const NumberFormat* nf = getDefaultNumberFormat(success); michael@0: appendTo.formatAndAppend(nf, *arg, success); michael@0: } else if (arg->getType() == Formattable::kDate) { michael@0: const DateFormat* df = getDefaultDateFormat(success); michael@0: appendTo.formatAndAppend(df, *arg, success); michael@0: } else { michael@0: appendTo.append(arg->getString(success)); michael@0: } michael@0: } else if (argType == UMSGPAT_ARG_TYPE_CHOICE) { michael@0: if (!arg->isNumeric()) { michael@0: success = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: // We must use the Formattable::getDouble() variant with the UErrorCode parameter michael@0: // because only this one converts non-double numeric types to double. michael@0: const double number = arg->getDouble(success); michael@0: int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number); michael@0: formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames, michael@0: cnt, appendTo, success); michael@0: } else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) { michael@0: if (!arg->isNumeric()) { michael@0: success = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: const PluralSelectorProvider &selector = michael@0: argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider; michael@0: // We must use the Formattable::getDouble() variant with the UErrorCode parameter michael@0: // because only this one converts non-double numeric types to double. michael@0: double offset = msgPattern.getPluralOffset(i); michael@0: PluralSelectorContext context(i, argName, *arg, offset, success); michael@0: int32_t subMsgStart = PluralFormat::findSubMessage( michael@0: msgPattern, i, selector, &context, arg->getDouble(success), success); michael@0: formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames, michael@0: cnt, appendTo, success); michael@0: } else if (argType == UMSGPAT_ARG_TYPE_SELECT) { michael@0: int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success); michael@0: formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames, michael@0: cnt, appendTo, success); michael@0: } else { michael@0: // This should never happen. michael@0: success = U_INTERNAL_PROGRAM_ERROR; michael@0: return; michael@0: } michael@0: ignore = updateMetaData(appendTo, prevDestLength, ignore, arg); michael@0: prevIndex = msgPattern.getPart(argLimit).getLimit(); michael@0: i = argLimit; michael@0: } michael@0: } michael@0: michael@0: michael@0: void MessageFormat::formatComplexSubMessage(int32_t msgStart, michael@0: const void *plNumber, michael@0: const Formattable* arguments, michael@0: const UnicodeString *argumentNames, michael@0: int32_t cnt, michael@0: AppendableWrapper& appendTo, michael@0: UErrorCode& success) const { michael@0: if (U_FAILURE(success)) { michael@0: return; michael@0: } michael@0: michael@0: if (!MessageImpl::jdkAposMode(msgPattern)) { michael@0: format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, NULL, success); michael@0: return; michael@0: } michael@0: michael@0: // JDK compatibility mode: (see JDK MessageFormat.format() API docs) michael@0: // - remove SKIP_SYNTAX; that is, remove half of the apostrophes michael@0: // - if the result string contains an open curly brace '{' then michael@0: // instantiate a temporary MessageFormat object and format again; michael@0: // otherwise just append the result string michael@0: const UnicodeString& msgString = msgPattern.getPatternString(); michael@0: UnicodeString sb; michael@0: int32_t prevIndex = msgPattern.getPart(msgStart).getLimit(); michael@0: for (int32_t i = msgStart;;) { michael@0: const MessagePattern::Part& part = msgPattern.getPart(++i); michael@0: const UMessagePatternPartType type = part.getType(); michael@0: int32_t index = part.getIndex(); michael@0: if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { michael@0: sb.append(msgString, prevIndex, index - prevIndex); michael@0: break; michael@0: } else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) { michael@0: sb.append(msgString, prevIndex, index - prevIndex); michael@0: if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { michael@0: const PluralSelectorContext &pluralNumber = michael@0: *static_cast(plNumber); michael@0: if(pluralNumber.forReplaceNumber) { michael@0: // number-offset was already formatted. michael@0: sb.append(pluralNumber.numberString); michael@0: } else { michael@0: const NumberFormat* nf = getDefaultNumberFormat(success); michael@0: sb.append(nf->format(pluralNumber.number, sb, success)); michael@0: } michael@0: } michael@0: prevIndex = part.getLimit(); michael@0: } else if (type == UMSGPAT_PART_TYPE_ARG_START) { michael@0: sb.append(msgString, prevIndex, index - prevIndex); michael@0: prevIndex = index; michael@0: i = msgPattern.getLimitPartIndex(i); michael@0: index = msgPattern.getPart(i).getLimit(); michael@0: MessageImpl::appendReducedApostrophes(msgString, prevIndex, index, sb); michael@0: prevIndex = index; michael@0: } michael@0: } michael@0: if (sb.indexOf(LEFT_CURLY_BRACE) >= 0) { michael@0: UnicodeString emptyPattern; // gcc 3.3.3 fails with "UnicodeString()" as the first parameter. michael@0: MessageFormat subMsgFormat(emptyPattern, fLocale, success); michael@0: subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, success); michael@0: subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, NULL, success); michael@0: } else { michael@0: appendTo.append(sb); michael@0: } michael@0: } michael@0: michael@0: michael@0: UnicodeString MessageFormat::getLiteralStringUntilNextArgument(int32_t from) const { michael@0: const UnicodeString& msgString=msgPattern.getPatternString(); michael@0: int32_t prevIndex=msgPattern.getPart(from).getLimit(); michael@0: UnicodeString b; michael@0: for (int32_t i = from + 1; ; ++i) { michael@0: const MessagePattern::Part& part = msgPattern.getPart(i); michael@0: const UMessagePatternPartType type=part.getType(); michael@0: int32_t index=part.getIndex(); michael@0: b.append(msgString, prevIndex, index - prevIndex); michael@0: if(type==UMSGPAT_PART_TYPE_ARG_START || type==UMSGPAT_PART_TYPE_MSG_LIMIT) { michael@0: return b; michael@0: } michael@0: // Unexpected Part "part" in parsed message. michael@0: U_ASSERT(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR); michael@0: prevIndex=part.getLimit(); michael@0: } michael@0: } michael@0: michael@0: michael@0: FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_t /*prevLength*/, michael@0: FieldPosition* /*fp*/, const Formattable* /*argId*/) const { michael@0: // Unlike in Java, there are no field attributes defined for MessageFormat. Do nothing. michael@0: return NULL; michael@0: /* michael@0: if (fp != NULL && Field.ARGUMENT.equals(fp.getFieldAttribute())) { michael@0: fp->setBeginIndex(prevLength); michael@0: fp->setEndIndex(dest.get_length()); michael@0: return NULL; michael@0: } michael@0: return fp; michael@0: */ michael@0: } michael@0: michael@0: int32_t michael@0: MessageFormat::findOtherSubMessage(int32_t partIndex) const { michael@0: int32_t count=msgPattern.countParts(); michael@0: const MessagePattern::Part *part = &msgPattern.getPart(partIndex); michael@0: if(MessagePattern::Part::hasNumericValue(part->getType())) { michael@0: ++partIndex; michael@0: } michael@0: // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples michael@0: // until ARG_LIMIT or end of plural-only pattern. michael@0: UnicodeString other(FALSE, OTHER_STRING, 5); michael@0: do { michael@0: part=&msgPattern.getPart(partIndex++); michael@0: UMessagePatternPartType type=part->getType(); michael@0: if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { michael@0: break; michael@0: } michael@0: U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR); michael@0: // part is an ARG_SELECTOR followed by an optional explicit value, and then a message michael@0: if(msgPattern.partSubstringMatches(*part, other)) { michael@0: return partIndex; michael@0: } michael@0: if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) { michael@0: ++partIndex; // skip the numeric-value part of "=1" etc. michael@0: } michael@0: partIndex=msgPattern.getLimitPartIndex(partIndex); michael@0: } while(++partIndex 0) { michael@0: if (!allocateArgTypes(argTypeCount, ec)) { michael@0: return; michael@0: } michael@0: uprv_memcpy(argTypes, that.argTypes, argTypeCount * sizeof(argTypes[0])); michael@0: } michael@0: if (cachedFormatters != NULL) { michael@0: uhash_removeAll(cachedFormatters); michael@0: } michael@0: if (customFormatArgStarts != NULL) { michael@0: uhash_removeAll(customFormatArgStarts); michael@0: } michael@0: if (that.cachedFormatters) { michael@0: if (cachedFormatters == NULL) { michael@0: cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong, michael@0: equalFormatsForHash, &ec); michael@0: if (U_FAILURE(ec)) { michael@0: return; michael@0: } michael@0: uhash_setValueDeleter(cachedFormatters, uprv_deleteUObject); michael@0: } michael@0: michael@0: const int32_t count = uhash_count(that.cachedFormatters); michael@0: int32_t pos, idx; michael@0: for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) { michael@0: const UHashElement* cur = uhash_nextElement(that.cachedFormatters, &pos); michael@0: Format* newFormat = ((Format*)(cur->value.pointer))->clone(); michael@0: if (newFormat) { michael@0: uhash_iput(cachedFormatters, cur->key.integer, newFormat, &ec); michael@0: } else { michael@0: ec = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: if (that.customFormatArgStarts) { michael@0: if (customFormatArgStarts == NULL) { michael@0: customFormatArgStarts=uhash_open(uhash_hashLong, uhash_compareLong, michael@0: NULL, &ec); michael@0: } michael@0: const int32_t count = uhash_count(that.customFormatArgStarts); michael@0: int32_t pos, idx; michael@0: for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) { michael@0: const UHashElement* cur = uhash_nextElement(that.customFormatArgStarts, &pos); michael@0: uhash_iputi(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: Formattable* michael@0: MessageFormat::parse(int32_t msgStart, michael@0: const UnicodeString& source, michael@0: ParsePosition& pos, michael@0: int32_t& count, michael@0: UErrorCode& ec) const { michael@0: count = 0; michael@0: if (U_FAILURE(ec)) { michael@0: pos.setErrorIndex(pos.getIndex()); michael@0: return NULL; michael@0: } michael@0: // parse() does not work with named arguments. michael@0: if (msgPattern.hasNamedArguments()) { michael@0: ec = U_ARGUMENT_TYPE_MISMATCH; michael@0: pos.setErrorIndex(pos.getIndex()); michael@0: return NULL; michael@0: } michael@0: LocalArray resultArray(new Formattable[argTypeCount ? argTypeCount : 1]); michael@0: const UnicodeString& msgString=msgPattern.getPatternString(); michael@0: int32_t prevIndex=msgPattern.getPart(msgStart).getLimit(); michael@0: int32_t sourceOffset = pos.getIndex(); michael@0: ParsePosition tempStatus(0); michael@0: michael@0: for(int32_t i=msgStart+1; ; ++i) { michael@0: UBool haveArgResult = FALSE; michael@0: const MessagePattern::Part* part=&msgPattern.getPart(i); michael@0: const UMessagePatternPartType type=part->getType(); michael@0: int32_t index=part->getIndex(); michael@0: // Make sure the literal string matches. michael@0: int32_t len = index - prevIndex; michael@0: if (len == 0 || (0 == msgString.compare(prevIndex, len, source, sourceOffset, len))) { michael@0: sourceOffset += len; michael@0: prevIndex += len; michael@0: } else { michael@0: pos.setErrorIndex(sourceOffset); michael@0: return NULL; // leave index as is to signal error michael@0: } michael@0: if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) { michael@0: // Things went well! Done. michael@0: pos.setIndex(sourceOffset); michael@0: return resultArray.orphan(); michael@0: } michael@0: if(type==UMSGPAT_PART_TYPE_SKIP_SYNTAX || type==UMSGPAT_PART_TYPE_INSERT_CHAR) { michael@0: prevIndex=part->getLimit(); michael@0: continue; michael@0: } michael@0: // We do not support parsing Plural formats. (No REPLACE_NUMBER here.) michael@0: // Unexpected Part "part" in parsed message. michael@0: U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_START); michael@0: int32_t argLimit=msgPattern.getLimitPartIndex(i); michael@0: michael@0: UMessagePatternArgType argType=part->getArgType(); michael@0: part=&msgPattern.getPart(++i); michael@0: int32_t argNumber = part->getValue(); // ARG_NUMBER michael@0: UnicodeString key; michael@0: ++i; michael@0: const Format* formatter = NULL; michael@0: Formattable& argResult = resultArray[argNumber]; michael@0: michael@0: if(cachedFormatters!=NULL && (formatter = getCachedFormatter(i - 2))!=NULL) { michael@0: // Just parse using the formatter. michael@0: tempStatus.setIndex(sourceOffset); michael@0: formatter->parseObject(source, argResult, tempStatus); michael@0: if (tempStatus.getIndex() == sourceOffset) { michael@0: pos.setErrorIndex(sourceOffset); michael@0: return NULL; // leave index as is to signal error michael@0: } michael@0: sourceOffset = tempStatus.getIndex(); michael@0: haveArgResult = TRUE; michael@0: } else if( michael@0: argType==UMSGPAT_ARG_TYPE_NONE || (cachedFormatters && uhash_iget(cachedFormatters, i -2))) { michael@0: // We arrive here if getCachedFormatter returned NULL, but there was actually an element in the hash table. michael@0: // This can only happen if the hash table contained a DummyFormat, so the if statement above is a check michael@0: // for the hash table containind DummyFormat. michael@0: michael@0: // Match as a string. michael@0: // if at end, use longest possible match michael@0: // otherwise uses first match to intervening string michael@0: // does NOT recursively try all possibilities michael@0: UnicodeString stringAfterArgument = getLiteralStringUntilNextArgument(argLimit); michael@0: int32_t next; michael@0: if (!stringAfterArgument.isEmpty()) { michael@0: next = source.indexOf(stringAfterArgument, sourceOffset); michael@0: } else { michael@0: next = source.length(); michael@0: } michael@0: if (next < 0) { michael@0: pos.setErrorIndex(sourceOffset); michael@0: return NULL; // leave index as is to signal error michael@0: } else { michael@0: UnicodeString strValue(source.tempSubString(sourceOffset, next - sourceOffset)); michael@0: UnicodeString compValue; michael@0: compValue.append(LEFT_CURLY_BRACE); michael@0: itos(argNumber, compValue); michael@0: compValue.append(RIGHT_CURLY_BRACE); michael@0: if (0 != strValue.compare(compValue)) { michael@0: argResult.setString(strValue); michael@0: haveArgResult = TRUE; michael@0: } michael@0: sourceOffset = next; michael@0: } michael@0: } else if(argType==UMSGPAT_ARG_TYPE_CHOICE) { michael@0: tempStatus.setIndex(sourceOffset); michael@0: double choiceResult = ChoiceFormat::parseArgument(msgPattern, i, source, tempStatus); michael@0: if (tempStatus.getIndex() == sourceOffset) { michael@0: pos.setErrorIndex(sourceOffset); michael@0: return NULL; // leave index as is to signal error michael@0: } michael@0: argResult.setDouble(choiceResult); michael@0: haveArgResult = TRUE; michael@0: sourceOffset = tempStatus.getIndex(); michael@0: } else if(UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType) || argType==UMSGPAT_ARG_TYPE_SELECT) { michael@0: // Parsing not supported. michael@0: ec = U_UNSUPPORTED_ERROR; michael@0: return NULL; michael@0: } else { michael@0: // This should never happen. michael@0: ec = U_INTERNAL_PROGRAM_ERROR; michael@0: return NULL; michael@0: } michael@0: if (haveArgResult && count <= argNumber) { michael@0: count = argNumber + 1; michael@0: } michael@0: prevIndex=msgPattern.getPart(argLimit).getLimit(); michael@0: i=argLimit; michael@0: } michael@0: } michael@0: // ------------------------------------- michael@0: // Parses the source pattern and returns the Formattable objects array, michael@0: // the array count and the ending parse position. The caller of this method michael@0: // owns the array. michael@0: michael@0: Formattable* michael@0: MessageFormat::parse(const UnicodeString& source, michael@0: ParsePosition& pos, michael@0: int32_t& count) const { michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: return parse(0, source, pos, count, ec); michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Parses the source string and returns the array of michael@0: // Formattable objects and the array count. The caller michael@0: // owns the returned array. michael@0: michael@0: Formattable* michael@0: MessageFormat::parse(const UnicodeString& source, michael@0: int32_t& cnt, michael@0: UErrorCode& success) const michael@0: { michael@0: if (msgPattern.hasNamedArguments()) { michael@0: success = U_ARGUMENT_TYPE_MISMATCH; michael@0: return NULL; michael@0: } michael@0: ParsePosition status(0); michael@0: // Calls the actual implementation method and starts michael@0: // from zero offset of the source text. michael@0: Formattable* result = parse(source, status, cnt); michael@0: if (status.getIndex() == 0) { michael@0: success = U_MESSAGE_PARSE_ERROR; michael@0: delete[] result; michael@0: return NULL; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: // Parses the source text and copy into the result buffer. michael@0: michael@0: void michael@0: MessageFormat::parseObject( const UnicodeString& source, michael@0: Formattable& result, michael@0: ParsePosition& status) const michael@0: { michael@0: int32_t cnt = 0; michael@0: Formattable* tmpResult = parse(source, status, cnt); michael@0: if (tmpResult != NULL) michael@0: result.adoptArray(tmpResult, cnt); michael@0: } michael@0: michael@0: UnicodeString michael@0: MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) { michael@0: UnicodeString result; michael@0: if (U_SUCCESS(status)) { michael@0: int32_t plen = pattern.length(); michael@0: const UChar* pat = pattern.getBuffer(); michael@0: int32_t blen = plen * 2 + 1; // space for null termination, convenience michael@0: UChar* buf = result.getBuffer(blen); michael@0: if (buf == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } else { michael@0: int32_t len = umsg_autoQuoteApostrophe(pat, plen, buf, blen, &status); michael@0: result.releaseBuffer(U_SUCCESS(status) ? len : 0); michael@0: } michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: result.setToBogus(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // ------------------------------------- michael@0: michael@0: static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) { michael@0: RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec); michael@0: if (fmt == NULL) { michael@0: ec = U_MEMORY_ALLOCATION_ERROR; michael@0: } else if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) { michael@0: UErrorCode localStatus = U_ZERO_ERROR; // ignore unrecognized default rule set michael@0: fmt->setDefaultRuleSet(defaultRuleSet, localStatus); michael@0: } michael@0: return fmt; michael@0: } michael@0: michael@0: void MessageFormat::cacheExplicitFormats(UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: if (cachedFormatters != NULL) { michael@0: uhash_removeAll(cachedFormatters); michael@0: } michael@0: if (customFormatArgStarts != NULL) { michael@0: uhash_removeAll(customFormatArgStarts); michael@0: } michael@0: michael@0: // The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT michael@0: // which we need not examine. michael@0: int32_t limit = msgPattern.countParts() - 2; michael@0: argTypeCount = 0; michael@0: // We also need not look at the first two "parts" michael@0: // (at most MSG_START and ARG_START) in this loop. michael@0: // We determine the argTypeCount first so that we can allocateArgTypes michael@0: // so that the next loop can set argTypes[argNumber]. michael@0: // (This is for the C API which needs the argTypes to read its va_arg list.) michael@0: for (int32_t i = 2; i < limit && U_SUCCESS(status); ++i) { michael@0: const MessagePattern::Part& part = msgPattern.getPart(i); michael@0: if (part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { michael@0: const int argNumber = part.getValue(); michael@0: if (argNumber >= argTypeCount) { michael@0: argTypeCount = argNumber + 1; michael@0: } michael@0: } michael@0: } michael@0: if (!allocateArgTypes(argTypeCount, status)) { michael@0: return; michael@0: } michael@0: // Set all argTypes to kObject, as a "none" value, for lack of any better value. michael@0: // We never use kObject for real arguments. michael@0: // We use it as "no argument yet" for the check for hasArgTypeConflicts. michael@0: for (int32_t i = 0; i < argTypeCount; ++i) { michael@0: argTypes[i] = Formattable::kObject; michael@0: } michael@0: hasArgTypeConflicts = FALSE; michael@0: michael@0: // This loop starts at part index 1 because we do need to examine michael@0: // ARG_START parts. (But we can ignore the MSG_START.) michael@0: for (int32_t i = 1; i < limit && U_SUCCESS(status); ++i) { michael@0: const MessagePattern::Part* part = &msgPattern.getPart(i); michael@0: if (part->getType() != UMSGPAT_PART_TYPE_ARG_START) { michael@0: continue; michael@0: } michael@0: UMessagePatternArgType argType = part->getArgType(); michael@0: michael@0: int32_t argNumber = -1; michael@0: part = &msgPattern.getPart(i + 1); michael@0: if (part->getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) { michael@0: argNumber = part->getValue(); michael@0: } michael@0: Formattable::Type formattableType; michael@0: michael@0: switch (argType) { michael@0: case UMSGPAT_ARG_TYPE_NONE: michael@0: formattableType = Formattable::kString; michael@0: break; michael@0: case UMSGPAT_ARG_TYPE_SIMPLE: { michael@0: int32_t index = i; michael@0: i += 2; michael@0: UnicodeString explicitType = msgPattern.getSubstring(msgPattern.getPart(i++)); michael@0: UnicodeString style; michael@0: if ((part = &msgPattern.getPart(i))->getType() == UMSGPAT_PART_TYPE_ARG_STYLE) { michael@0: style = msgPattern.getSubstring(*part); michael@0: ++i; michael@0: } michael@0: UParseError parseError; michael@0: Format* formatter = createAppropriateFormat(explicitType, style, formattableType, parseError, status); michael@0: setArgStartFormat(index, formatter, status); michael@0: break; michael@0: } michael@0: case UMSGPAT_ARG_TYPE_CHOICE: michael@0: case UMSGPAT_ARG_TYPE_PLURAL: michael@0: case UMSGPAT_ARG_TYPE_SELECTORDINAL: michael@0: formattableType = Formattable::kDouble; michael@0: break; michael@0: case UMSGPAT_ARG_TYPE_SELECT: michael@0: formattableType = Formattable::kString; michael@0: break; michael@0: default: michael@0: status = U_INTERNAL_PROGRAM_ERROR; // Should be unreachable. michael@0: formattableType = Formattable::kString; michael@0: break; michael@0: } michael@0: if (argNumber != -1) { michael@0: if (argTypes[argNumber] != Formattable::kObject && argTypes[argNumber] != formattableType) { michael@0: hasArgTypeConflicts = TRUE; michael@0: } michael@0: argTypes[argNumber] = formattableType; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style, michael@0: Formattable::Type& formattableType, UParseError& parseError, michael@0: UErrorCode& ec) { michael@0: if (U_FAILURE(ec)) { michael@0: return NULL; michael@0: } michael@0: Format* fmt = NULL; michael@0: int32_t typeID, styleID; michael@0: DateFormat::EStyle date_style; michael@0: michael@0: switch (typeID = findKeyword(type, TYPE_IDS)) { michael@0: case 0: // number michael@0: formattableType = Formattable::kDouble; michael@0: switch (findKeyword(style, NUMBER_STYLE_IDS)) { michael@0: case 0: // default michael@0: fmt = NumberFormat::createInstance(fLocale, ec); michael@0: break; michael@0: case 1: // currency michael@0: fmt = NumberFormat::createCurrencyInstance(fLocale, ec); michael@0: break; michael@0: case 2: // percent michael@0: fmt = NumberFormat::createPercentInstance(fLocale, ec); michael@0: break; michael@0: case 3: // integer michael@0: formattableType = Formattable::kLong; michael@0: fmt = createIntegerFormat(fLocale, ec); michael@0: break; michael@0: default: // pattern michael@0: fmt = NumberFormat::createInstance(fLocale, ec); michael@0: if (fmt) { michael@0: DecimalFormat* decfmt = dynamic_cast(fmt); michael@0: if (decfmt != NULL) { michael@0: decfmt->applyPattern(style,parseError,ec); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case 1: // date michael@0: case 2: // time michael@0: formattableType = Formattable::kDate; michael@0: styleID = findKeyword(style, DATE_STYLE_IDS); michael@0: date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault; michael@0: michael@0: if (typeID == 1) { michael@0: fmt = DateFormat::createDateInstance(date_style, fLocale); michael@0: } else { michael@0: fmt = DateFormat::createTimeInstance(date_style, fLocale); michael@0: } michael@0: michael@0: if (styleID < 0 && fmt != NULL) { michael@0: SimpleDateFormat* sdtfmt = dynamic_cast(fmt); michael@0: if (sdtfmt != NULL) { michael@0: sdtfmt->applyPattern(style); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case 3: // spellout michael@0: formattableType = Formattable::kDouble; michael@0: fmt = makeRBNF(URBNF_SPELLOUT, fLocale, style, ec); michael@0: break; michael@0: case 4: // ordinal michael@0: formattableType = Formattable::kDouble; michael@0: fmt = makeRBNF(URBNF_ORDINAL, fLocale, style, ec); michael@0: break; michael@0: case 5: // duration michael@0: formattableType = Formattable::kDouble; michael@0: fmt = makeRBNF(URBNF_DURATION, fLocale, style, ec); michael@0: break; michael@0: default: michael@0: formattableType = Formattable::kString; michael@0: ec = U_ILLEGAL_ARGUMENT_ERROR; michael@0: break; michael@0: } michael@0: michael@0: return fmt; michael@0: } michael@0: michael@0: michael@0: //------------------------------------- michael@0: // Finds the string, s, in the string array, list. michael@0: int32_t MessageFormat::findKeyword(const UnicodeString& s, michael@0: const UChar * const *list) michael@0: { michael@0: if (s.isEmpty()) { michael@0: return 0; // default michael@0: } michael@0: michael@0: int32_t length = s.length(); michael@0: const UChar *ps = PatternProps::trimWhiteSpace(s.getBuffer(), length); michael@0: UnicodeString buffer(FALSE, ps, length); michael@0: // Trims the space characters and turns all characters michael@0: // in s to lower case. michael@0: buffer.toLower(""); michael@0: for (int32_t i = 0; list[i]; ++i) { michael@0: if (!buffer.compare(list[i], u_strlen(list[i]))) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: /** michael@0: * Convenience method that ought to be in NumberFormat michael@0: */ michael@0: NumberFormat* michael@0: MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const { michael@0: NumberFormat *temp = NumberFormat::createInstance(locale, status); michael@0: DecimalFormat *temp2; michael@0: if (temp != NULL && (temp2 = dynamic_cast(temp)) != NULL) { michael@0: temp2->setMaximumFractionDigits(0); michael@0: temp2->setDecimalSeparatorAlwaysShown(FALSE); michael@0: temp2->setParseIntegerOnly(TRUE); michael@0: } michael@0: michael@0: return temp; michael@0: } michael@0: michael@0: /** michael@0: * Return the default number format. Used to format a numeric michael@0: * argument when subformats[i].format is NULL. Returns NULL michael@0: * on failure. michael@0: * michael@0: * Semantically const but may modify *this. michael@0: */ michael@0: const NumberFormat* MessageFormat::getDefaultNumberFormat(UErrorCode& ec) const { michael@0: if (defaultNumberFormat == NULL) { michael@0: MessageFormat* t = (MessageFormat*) this; michael@0: t->defaultNumberFormat = NumberFormat::createInstance(fLocale, ec); michael@0: if (U_FAILURE(ec)) { michael@0: delete t->defaultNumberFormat; michael@0: t->defaultNumberFormat = NULL; michael@0: } else if (t->defaultNumberFormat == NULL) { michael@0: ec = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: return defaultNumberFormat; michael@0: } michael@0: michael@0: /** michael@0: * Return the default date format. Used to format a date michael@0: * argument when subformats[i].format is NULL. Returns NULL michael@0: * on failure. michael@0: * michael@0: * Semantically const but may modify *this. michael@0: */ michael@0: const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const { michael@0: if (defaultDateFormat == NULL) { michael@0: MessageFormat* t = (MessageFormat*) this; michael@0: t->defaultDateFormat = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale); michael@0: if (t->defaultDateFormat == NULL) { michael@0: ec = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: } michael@0: return defaultDateFormat; michael@0: } michael@0: michael@0: UBool michael@0: MessageFormat::usesNamedArguments() const { michael@0: return msgPattern.hasNamedArguments(); michael@0: } michael@0: michael@0: int32_t michael@0: MessageFormat::getArgTypeCount() const { michael@0: return argTypeCount; michael@0: } michael@0: michael@0: UBool MessageFormat::equalFormats(const void* left, const void* right) { michael@0: return *(const Format*)left==*(const Format*)right; michael@0: } michael@0: michael@0: michael@0: UBool MessageFormat::DummyFormat::operator==(const Format&) const { michael@0: return TRUE; michael@0: } michael@0: michael@0: Format* MessageFormat::DummyFormat::clone() const { michael@0: return new DummyFormat(); michael@0: } michael@0: michael@0: UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, michael@0: UnicodeString& appendTo, michael@0: UErrorCode& status) const { michael@0: if (U_SUCCESS(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return appendTo; michael@0: } michael@0: michael@0: UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, michael@0: UnicodeString& appendTo, michael@0: FieldPosition&, michael@0: UErrorCode& status) const { michael@0: if (U_SUCCESS(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return appendTo; michael@0: } michael@0: michael@0: UnicodeString& MessageFormat::DummyFormat::format(const Formattable&, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator*, michael@0: UErrorCode& status) const { michael@0: if (U_SUCCESS(status)) { michael@0: status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return appendTo; michael@0: } michael@0: michael@0: void MessageFormat::DummyFormat::parseObject(const UnicodeString&, michael@0: Formattable&, michael@0: ParsePosition& ) const { michael@0: } michael@0: michael@0: michael@0: FormatNameEnumeration::FormatNameEnumeration(UVector *fNameList, UErrorCode& /*status*/) { michael@0: pos=0; michael@0: fFormatNames = fNameList; michael@0: } michael@0: michael@0: const UnicodeString* michael@0: FormatNameEnumeration::snext(UErrorCode& status) { michael@0: if (U_SUCCESS(status) && pos < fFormatNames->size()) { michael@0: return (const UnicodeString*)fFormatNames->elementAt(pos++); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void michael@0: FormatNameEnumeration::reset(UErrorCode& /*status*/) { michael@0: pos=0; michael@0: } michael@0: michael@0: int32_t michael@0: FormatNameEnumeration::count(UErrorCode& /*status*/) const { michael@0: return (fFormatNames==NULL) ? 0 : fFormatNames->size(); michael@0: } michael@0: michael@0: FormatNameEnumeration::~FormatNameEnumeration() { michael@0: delete fFormatNames; michael@0: } michael@0: michael@0: MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t) michael@0: : msgFormat(mf), rules(NULL), type(t) { michael@0: } michael@0: michael@0: MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() { michael@0: delete rules; michael@0: } michael@0: michael@0: UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number, michael@0: UErrorCode& ec) const { michael@0: if (U_FAILURE(ec)) { michael@0: return UnicodeString(FALSE, OTHER_STRING, 5); michael@0: } michael@0: MessageFormat::PluralSelectorProvider* t = const_cast(this); michael@0: if(rules == NULL) { michael@0: t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec); michael@0: if (U_FAILURE(ec)) { michael@0: return UnicodeString(FALSE, OTHER_STRING, 5); michael@0: } michael@0: } michael@0: // Select a sub-message according to how the number is formatted, michael@0: // which is specified in the selected sub-message. michael@0: // We avoid this circle by looking at how michael@0: // the number is formatted in the "other" sub-message michael@0: // which must always be present and usually contains the number. michael@0: // Message authors should be consistent across sub-messages. michael@0: PluralSelectorContext &context = *static_cast(ctx); michael@0: int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex); michael@0: context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName); michael@0: if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != NULL) { michael@0: context.formatter = michael@0: (const Format*)uhash_iget(msgFormat.cachedFormatters, context.numberArgIndex); michael@0: } michael@0: if(context.formatter == NULL) { michael@0: context.formatter = msgFormat.getDefaultNumberFormat(ec); michael@0: context.forReplaceNumber = TRUE; michael@0: } michael@0: U_ASSERT(context.number.getDouble(ec) == number); // argument number minus the offset michael@0: context.formatter->format(context.number, context.numberString, ec); michael@0: const DecimalFormat *decFmt = dynamic_cast(context.formatter); michael@0: if(decFmt != NULL) { michael@0: FixedDecimal dec = decFmt->getFixedDecimal(context.number, ec); michael@0: return rules->select(dec); michael@0: } else { michael@0: return rules->select(number); michael@0: } michael@0: } michael@0: michael@0: void MessageFormat::PluralSelectorProvider::reset() { michael@0: delete rules; michael@0: rules = NULL; michael@0: } michael@0: michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof