/* ******************************************************************************* * Copyright (C) 1997-2004, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* * * File MSGFMT.CPP * * Modification History: * * Date Name Description * 02/19/97 aliu Converted from java. * 03/20/97 helena Finished first cut of implementation. * 04/10/97 aliu Made to work on AIX. Added stoi to replace wtoi. * 06/11/97 helena Fixed addPattern to take the pattern correctly. * 06/17/97 helena Fixed the getPattern to return the correct pattern. * 07/09/97 helena Made ParsePosition into a class. * 02/22/99 stephen Removed character literals for EBCDIC safety ******************************************************************************** */ #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #include "unicode/msgfmt.h" #include "unicode/decimfmt.h" #include "unicode/datefmt.h" #include "unicode/smpdtfmt.h" #include "unicode/choicfmt.h" #include "unicode/ustring.h" #include "unicode/ucnv_err.h" #include "unicode/uchar.h" #include "unicode/umsg.h" #include "unicode/rbnf.h" #include "ustrfmt.h" #include "cmemory.h" #include "util.h" #include "uassert.h" // ***************************************************************************** // class MessageFormat // ***************************************************************************** #define COMMA ((UChar)0x002C) #define SINGLE_QUOTE ((UChar)0x0027) #define LEFT_CURLY_BRACE ((UChar)0x007B) #define RIGHT_CURLY_BRACE ((UChar)0x007D) //--------------------------------------- // static data static const UChar ID_EMPTY[] = { 0 /* empty string, used for default so that null can mark end of list */ }; static const UChar ID_NUMBER[] = { 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */ }; static const UChar ID_DATE[] = { 0x64, 0x61, 0x74, 0x65, 0 /* "date" */ }; static const UChar ID_TIME[] = { 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */ }; static const UChar ID_CHOICE[] = { 0x63, 0x68, 0x6F, 0x69, 0x63, 0x65, 0 /* "choice" */ }; static const UChar ID_SPELLOUT[] = { 0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */ }; static const UChar ID_ORDINAL[] = { 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */ }; static const UChar ID_DURATION[] = { 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */ }; // MessageFormat Type List Number, Date, Time or Choice static const UChar * const TYPE_IDS[] = { ID_EMPTY, ID_NUMBER, ID_DATE, ID_TIME, ID_CHOICE, ID_SPELLOUT, ID_ORDINAL, ID_DURATION, NULL, }; static const UChar ID_CURRENCY[] = { 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */ }; static const UChar ID_PERCENT[] = { 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */ }; static const UChar ID_INTEGER[] = { 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */ }; // NumberFormat modifier list, default, currency, percent or integer static const UChar * const NUMBER_STYLE_IDS[] = { ID_EMPTY, ID_CURRENCY, ID_PERCENT, ID_INTEGER, NULL, }; static const UChar ID_SHORT[] = { 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */ }; static const UChar ID_MEDIUM[] = { 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */ }; static const UChar ID_LONG[] = { 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */ }; static const UChar ID_FULL[] = { 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */ }; // DateFormat modifier list, default, short, medium, long or full static const UChar * const DATE_STYLE_IDS[] = { ID_EMPTY, ID_SHORT, ID_MEDIUM, ID_LONG, ID_FULL, NULL, }; static const DateFormat::EStyle DATE_STYLES[] = { DateFormat::kDefault, DateFormat::kShort, DateFormat::kMedium, DateFormat::kLong, DateFormat::kFull, }; static const int32_t DEFAULT_INITIAL_CAPACITY = 10; U_NAMESPACE_BEGIN // ------------------------------------- UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat) //-------------------------------------------------------------------- /** * Convert a string to an unsigned decimal, ignoring rule whitespace. * @return a non-negative number if successful, or a negative number * upon failure. */ static int32_t stou(const UnicodeString& string) { int32_t n = 0; int32_t count = 0; UChar32 c; for (int32_t i=0; i 10) { return -1; } n = 10*n + d; } return n; } /** * Convert an integer value to a string and append the result to * the given UnicodeString. */ static UnicodeString& itos(int32_t i, UnicodeString& appendTo) { UChar temp[16]; uprv_itou(temp,16,i,10,0); // 10 == radix appendTo.append(temp); return appendTo; } // ------------------------------------- // Creates a MessageFormat instance based on the pattern. MessageFormat::MessageFormat(const UnicodeString& pattern, UErrorCode& success) : fLocale(Locale::getDefault()), // Uses the default locale formatAliases(NULL), formatAliasesCapacity(0), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), defaultNumberFormat(NULL), defaultDateFormat(NULL) { if (!allocateSubformats(DEFAULT_INITIAL_CAPACITY) || !allocateArgTypes(DEFAULT_INITIAL_CAPACITY)) { success = U_MEMORY_ALLOCATION_ERROR; return; } applyPattern(pattern, success); setLocaleIDs(fLocale.getName(), fLocale.getName()); } MessageFormat::MessageFormat(const UnicodeString& pattern, const Locale& newLocale, UErrorCode& success) : fLocale(newLocale), formatAliases(NULL), formatAliasesCapacity(0), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), defaultNumberFormat(NULL), defaultDateFormat(NULL) { if (!allocateSubformats(DEFAULT_INITIAL_CAPACITY) || !allocateArgTypes(DEFAULT_INITIAL_CAPACITY)) { success = U_MEMORY_ALLOCATION_ERROR; return; } applyPattern(pattern, success); setLocaleIDs(fLocale.getName(), fLocale.getName()); } MessageFormat::MessageFormat(const UnicodeString& pattern, const Locale& newLocale, UParseError& parseError, UErrorCode& success) : fLocale(newLocale), formatAliases(NULL), formatAliasesCapacity(0), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), defaultNumberFormat(NULL), defaultDateFormat(NULL) { if (!allocateSubformats(DEFAULT_INITIAL_CAPACITY) || !allocateArgTypes(DEFAULT_INITIAL_CAPACITY)) { success = U_MEMORY_ALLOCATION_ERROR; return; } applyPattern(pattern, parseError, success); setLocaleIDs(fLocale.getName(), fLocale.getName()); } MessageFormat::MessageFormat(const MessageFormat& that) : Format(that), formatAliases(NULL), formatAliasesCapacity(0), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), defaultNumberFormat(NULL), defaultDateFormat(NULL) { *this = that; } MessageFormat::~MessageFormat() { int32_t idx; for (idx = 0; idx < subformatCount; idx++) { delete subformats[idx].format; } uprv_free(subformats); subformats = NULL; subformatCount = subformatCapacity = 0; uprv_free(argTypes); argTypes = NULL; argTypeCount = argTypeCapacity = 0; uprv_free(formatAliases); delete defaultNumberFormat; delete defaultDateFormat; } //-------------------------------------------------------------------- // Variable-size array management /** * Allocate subformats[] to at least the given capacity and return * TRUE if successful. If not, leave subformats[] unchanged. * * If subformats is NULL, allocate it. If it is not NULL, enlarge it * if necessary to be at least as large as specified. */ UBool MessageFormat::allocateSubformats(int32_t capacity) { if (subformats == NULL) { subformats = (Subformat*) uprv_malloc(sizeof(*subformats) * capacity); subformatCapacity = capacity; subformatCount = 0; if (subformats == NULL) { subformatCapacity = 0; return FALSE; } } else if (subformatCapacity < capacity) { if (capacity < 2*subformatCapacity) { capacity = 2*subformatCapacity; } Subformat* a = (Subformat*) uprv_realloc(subformats, sizeof(*subformats) * capacity); if (a == NULL) { return FALSE; // request failed } subformats = a; subformatCapacity = capacity; } return TRUE; } /** * Allocate argTypes[] to at least the given capacity and return * TRUE if successful. If not, leave argTypes[] unchanged. * * If argTypes is NULL, allocate it. If it is not NULL, enlarge it * if necessary to be at least as large as specified. */ UBool MessageFormat::allocateArgTypes(int32_t capacity) { if (argTypes == NULL) { argTypes = (Formattable::Type*) uprv_malloc(sizeof(*argTypes) * capacity); argTypeCount = 0; argTypeCapacity = capacity; if (argTypes == NULL) { argTypeCapacity = 0; return FALSE; } for (int32_t i=0; igetDynamicClassID() == DecimalFormat::getStaticClassID()) { UErrorCode ec = U_ZERO_ERROR; NumberFormat& formatAlias = *(NumberFormat*)fmt; NumberFormat *defaultTemplate = NumberFormat::createInstance(fLocale, ec); NumberFormat *currencyTemplate = NumberFormat::createCurrencyInstance(fLocale, ec); NumberFormat *percentTemplate = NumberFormat::createPercentInstance(fLocale, ec); NumberFormat *integerTemplate = createIntegerFormat(fLocale, ec); appendTo += COMMA; appendTo += ID_NUMBER; if (formatAlias != *defaultTemplate) { appendTo += COMMA; if (formatAlias == *currencyTemplate) { appendTo += ID_CURRENCY; } else if (formatAlias == *percentTemplate) { appendTo += ID_PERCENT; } else if (formatAlias == *integerTemplate) { appendTo += ID_INTEGER; } else { UnicodeString buffer; appendTo += ((DecimalFormat*)fmt)->toPattern(buffer); } } delete defaultTemplate; delete currencyTemplate; delete percentTemplate; delete integerTemplate; } else if (fmt->getDynamicClassID() == SimpleDateFormat::getStaticClassID()) { DateFormat& formatAlias = *(DateFormat*)fmt; DateFormat *defaultDateTemplate = DateFormat::createDateInstance(DateFormat::kDefault, fLocale); DateFormat *shortDateTemplate = DateFormat::createDateInstance(DateFormat::kShort, fLocale); DateFormat *longDateTemplate = DateFormat::createDateInstance(DateFormat::kLong, fLocale); DateFormat *fullDateTemplate = DateFormat::createDateInstance(DateFormat::kFull, fLocale); DateFormat *defaultTimeTemplate = DateFormat::createTimeInstance(DateFormat::kDefault, fLocale); DateFormat *shortTimeTemplate = DateFormat::createTimeInstance(DateFormat::kShort, fLocale); DateFormat *longTimeTemplate = DateFormat::createTimeInstance(DateFormat::kLong, fLocale); DateFormat *fullTimeTemplate = DateFormat::createTimeInstance(DateFormat::kFull, fLocale); appendTo += COMMA; if (formatAlias == *defaultDateTemplate) { appendTo += ID_DATE; } else if (formatAlias == *shortDateTemplate) { appendTo += ID_DATE; appendTo += COMMA; appendTo += ID_SHORT; } else if (formatAlias == *defaultDateTemplate) { appendTo += ID_DATE; appendTo += COMMA; appendTo += ID_MEDIUM; } else if (formatAlias == *longDateTemplate) { appendTo += ID_DATE; appendTo += COMMA; appendTo += ID_LONG; } else if (formatAlias == *fullDateTemplate) { appendTo += ID_DATE; appendTo += COMMA; appendTo += ID_FULL; } else if (formatAlias == *defaultTimeTemplate) { appendTo += ID_TIME; } else if (formatAlias == *shortTimeTemplate) { appendTo += ID_TIME; appendTo += COMMA; appendTo += ID_SHORT; } else if (formatAlias == *defaultTimeTemplate) { appendTo += ID_TIME; appendTo += COMMA; appendTo += ID_MEDIUM; } else if (formatAlias == *longTimeTemplate) { appendTo += ID_TIME; appendTo += COMMA; appendTo += ID_LONG; } else if (formatAlias == *fullTimeTemplate) { appendTo += ID_TIME; appendTo += COMMA; appendTo += ID_FULL; } else { UnicodeString buffer; appendTo += ID_DATE; appendTo += COMMA; appendTo += ((SimpleDateFormat*)fmt)->toPattern(buffer); } delete defaultDateTemplate; delete shortDateTemplate; delete longDateTemplate; delete fullDateTemplate; delete defaultTimeTemplate; delete shortTimeTemplate; delete longTimeTemplate; delete fullTimeTemplate; // {sfb} there should be a more efficient way to do this! } else if (fmt->getDynamicClassID() == ChoiceFormat::getStaticClassID()) { UnicodeString buffer; appendTo += COMMA; appendTo += ID_CHOICE; appendTo += COMMA; appendTo += ((ChoiceFormat*)fmt)->toPattern(buffer); } else { //appendTo += ", unknown"; } appendTo += RIGHT_CURLY_BRACE; } copyAndFixQuotes(fPattern, lastOffset, fPattern.length(), appendTo); return appendTo; } // ------------------------------------- // Adopts the new formats array and updates the array count. // This MessageFormat instance owns the new formats. void MessageFormat::adoptFormats(Format** newFormats, int32_t count) { if (newFormats == NULL || count < 0) { return; } int32_t i; if (allocateSubformats(count)) { for (i=0; iclone() : NULL; } subformatCount = count; } // TODO: What about the .offset and .arg fields? } // ------------------------------------- // Adopt a single format. // Do nothing is the format number is not less than the array count. void MessageFormat::adoptFormat(int32_t n, Format *newFormat) { if (n < 0 || n >= subformatCount) { delete newFormat; } else { delete subformats[n].format; subformats[n].format = newFormat; } } // ------------------------------------- // Set a single format. // Do nothing is the variable is not less than the array count. void MessageFormat::setFormat(int32_t n, const Format& newFormat) { if (n >= 0 && n < subformatCount) { delete subformats[n].format; if (&newFormat == NULL) { // This should never happen -- but we'll be nice if it does subformats[n].format = NULL; } else { subformats[n].format = newFormat.clone(); } } } // ------------------------------------- // Gets the format array. const Format** MessageFormat::getFormats(int32_t& cnt) const { // This old API returns an array (which we hold) of Format* // pointers. The array is valid up to the next call to any // method on this object. We construct and resize an array // on demand that contains aliases to the subformats[i].format // pointers. MessageFormat* t = (MessageFormat*) this; cnt = 0; if (formatAliases == NULL) { t->formatAliasesCapacity = (subformatCount<10) ? 10 : subformatCount; Format** a = (Format**) uprv_malloc(sizeof(Format*) * formatAliasesCapacity); if (a == NULL) { return NULL; } t->formatAliases = a; } else if (subformatCount > formatAliasesCapacity) { Format** a = (Format**) uprv_realloc(formatAliases, sizeof(Format*) * subformatCount); if (a == NULL) { return NULL; } t->formatAliases = a; t->formatAliasesCapacity = subformatCount; } for (int32_t i=0; iformatAliases[i] = subformats[i].format; } cnt = subformatCount; return (const Format**)formatAliases; } // ------------------------------------- // Formats the source Formattable array and copy into the result buffer. // Ignore the FieldPosition result for error checking. UnicodeString& MessageFormat::format(const Formattable* source, int32_t cnt, UnicodeString& appendTo, FieldPosition& ignore, UErrorCode& success) const { if (U_FAILURE(success)) return appendTo; return format(source, cnt, appendTo, ignore, 0, success); } // ------------------------------------- // Internally creates a MessageFormat instance based on the // pattern and formats the arguments Formattable array and // copy into the appendTo buffer. UnicodeString& MessageFormat::format( const UnicodeString& pattern, const Formattable* arguments, int32_t cnt, UnicodeString& appendTo, UErrorCode& success) { MessageFormat temp(pattern, success); FieldPosition ignore(0); temp.format(arguments, cnt, appendTo, ignore, success); return appendTo; } // ------------------------------------- // Formats the source Formattable object and copy into the // appendTo buffer. The Formattable object must be an array // of Formattable instances, returns error otherwise. UnicodeString& MessageFormat::format(const Formattable& source, UnicodeString& appendTo, FieldPosition& ignore, UErrorCode& success) const { int32_t cnt; if (U_FAILURE(success)) return appendTo; if (source.getType() != Formattable::kArray) { success = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } const Formattable* tmpPtr = source.getArray(cnt); return format(tmpPtr, cnt, appendTo, ignore, 0, success); } // ------------------------------------- // Formats the arguments Formattable array and copy into the appendTo buffer. // Ignore the FieldPosition result for error checking. UnicodeString& MessageFormat::format(const Formattable* arguments, int32_t cnt, UnicodeString& appendTo, FieldPosition& status, int32_t recursionProtection, UErrorCode& success) const { // Allow NULL array only if cnt == 0 if (cnt < 0 || (cnt && arguments == NULL)) { success = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } int32_t lastOffset = 0; for (int32_t i=0; i= cnt) { appendTo += LEFT_CURLY_BRACE; itos(argumentNumber, appendTo); appendTo += RIGHT_CURLY_BRACE; continue; } const Formattable *obj = arguments + argumentNumber; Formattable::Type type = obj->getType(); // Recursively calling the format process only if the current // format argument refers to a ChoiceFormat object. Format* fmt = subformats[i].format; if (fmt != NULL) { UnicodeString arg; fmt->format(*obj, arg, success); // Needs to reprocess the ChoiceFormat option by using the // MessageFormat pattern application. if (fmt->getDynamicClassID() == ChoiceFormat::getStaticClassID() && arg.indexOf(LEFT_CURLY_BRACE) >= 0) { MessageFormat temp(arg, fLocale, success); // TODO: Implement recursion protection temp.format(arguments, cnt, appendTo, status, recursionProtection, success); if (U_FAILURE(success)) { return appendTo; } } else { appendTo += arg; } } // If the obj data type is a number, use a NumberFormat instance. else if ((type == Formattable::kDouble) || (type == Formattable::kLong) || (type == Formattable::kInt64)) { const NumberFormat* nf = getDefaultNumberFormat(success); if (nf == NULL) { return appendTo; } if (type == Formattable::kDouble) { nf->format(obj->getDouble(), appendTo); } else if (type == Formattable::kLong) { nf->format(obj->getLong(), appendTo); } else { nf->format(obj->getInt64(), appendTo); } } // If the obj data type is a Date instance, use a DateFormat instance. else if (type == Formattable::kDate) { const DateFormat* df = getDefaultDateFormat(success); if (df == NULL) { return appendTo; } df->format(obj->getDate(), appendTo); } else if (type == Formattable::kString) { appendTo += obj->getString(); } else { success = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } } // Appends the rest of the pattern characters after the real last offset. appendTo.append(fPattern, lastOffset, 0x7fffffff); return appendTo; } // ------------------------------------- // Parses the source pattern and returns the Formattable objects array, // the array count and the ending parse position. The caller of this method // owns the array. Formattable* MessageFormat::parse(const UnicodeString& source, ParsePosition& pos, int32_t& count) const { // Allocate at least one element. Allocating an array of length // zero causes problems on some platforms (e.g. Win32). Formattable *resultArray = new Formattable[argTypeCount ? argTypeCount : 1]; int32_t patternOffset = 0; int32_t sourceOffset = pos.getIndex(); ParsePosition tempPos(0); count = 0; // {sfb} reset to zero int32_t len; for (int32_t i = 0; i < subformatCount; ++i) { // match up to format len = subformats[i].offset - patternOffset; if (len == 0 || fPattern.compare(patternOffset, len, source, sourceOffset, len) == 0) { sourceOffset += len; patternOffset += len; } else { goto PARSE_ERROR; } // now use format Format* fmt = subformats[i].format; int32_t arg = subformats[i].arg; if (fmt == NULL) { // string format // if at end, use longest possible match // otherwise uses first match to intervening string // does NOT recursively try all possibilities int32_t tempLength = (i+1= tempLength) { next = source.length(); } else { UnicodeString buffer; fPattern.extract(patternOffset,tempLength - patternOffset, buffer); next = source.indexOf(buffer, sourceOffset); } if (next < 0) { goto PARSE_ERROR; } else { UnicodeString buffer; source.extract(sourceOffset,next - sourceOffset, buffer); UnicodeString strValue = buffer; UnicodeString temp(LEFT_CURLY_BRACE); // {sfb} check this later itos(arg, temp); temp += RIGHT_CURLY_BRACE; if (strValue != temp) { source.extract(sourceOffset,next - sourceOffset, buffer); resultArray[arg].setString(buffer); // {sfb} not sure about this if ((arg + 1) > count) { count = arg + 1; } } sourceOffset = next; } } else { tempPos.setIndex(sourceOffset); fmt->parseObject(source, resultArray[arg], tempPos); if (tempPos.getIndex() == sourceOffset) { goto PARSE_ERROR; } if ((arg + 1) > count) { count = arg + 1; } sourceOffset = tempPos.getIndex(); // update } } len = fPattern.length() - patternOffset; if (len == 0 || fPattern.compare(patternOffset, len, source, sourceOffset, len) == 0) { pos.setIndex(sourceOffset + len); return resultArray; } // else fall through... PARSE_ERROR: pos.setErrorIndex(sourceOffset); delete [] resultArray; count = 0; return NULL; // leave index as is to signal error } // ------------------------------------- // Parses the source string and returns the array of // Formattable objects and the array count. The caller // owns the returned array. Formattable* MessageFormat::parse(const UnicodeString& source, int32_t& cnt, UErrorCode& success) const { ParsePosition status(0); // Calls the actual implementation method and starts // from zero offset of the source text. Formattable* result = parse(source, status, cnt); if (status.getIndex() == 0) { success = U_MESSAGE_PARSE_ERROR; delete[] result; return NULL; } return result; } // ------------------------------------- // Parses the source text and copy into the result buffer. void MessageFormat::parseObject( const UnicodeString& source, Formattable& result, ParsePosition& status) const { int32_t cnt = 0; Formattable* tmpResult = parse(source, status, cnt); if (tmpResult != NULL) result.adoptArray(tmpResult, cnt); } UnicodeString MessageFormat::autoQuoteApostrophe(const UnicodeString& pattern, UErrorCode& status) { UnicodeString result; if (U_SUCCESS(status)) { int32_t plen = pattern.length(); const UChar* pat = pattern.getBuffer(); int32_t blen = plen * 2 + 1; // space for null termination, convenience UChar* buf = result.getBuffer(blen); if (buf == NULL) { status = U_MEMORY_ALLOCATION_ERROR; } else { int32_t len = umsg_autoQuoteApostrophe(pat, plen, buf, blen, &status); if (U_SUCCESS(status)) { result.releaseBuffer(len); } } } if (U_FAILURE(status)) { result.setToBogus(); } return result; } // ------------------------------------- static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) { RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec); if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) { fmt->setDefaultRuleSet(defaultRuleSet, ec); if (U_FAILURE(ec)) { // ignore unrecognized default rule set ec = U_ZERO_ERROR; } } return fmt; } /** * Reads the segments[] array (see applyPattern()) and parses the * segments[1..3] into a Format* object. Stores the format object in * the subformats[] array. Updates the argTypes[] array type * information for the corresponding argument. * * @param formatNumber index into subformats[] for this format * @param segments array of strings with the parsed pattern segments * @param parseError parse error data (output param) * @param ec error code */ void MessageFormat::makeFormat(int32_t formatNumber, UnicodeString* segments, UParseError& parseError, UErrorCode& ec) { if (U_FAILURE(ec)) { return; } // Parse the argument number int32_t argumentNumber = stou(segments[1]); // always unlocalized! if (argumentNumber < 0) { ec = U_INVALID_FORMAT_ERROR; return; } // Parse the format, recording the argument type and creating a // new Format object (except for string arguments). Formattable::Type argType; Format *fmt = NULL; int32_t typeID, styleID; DateFormat::EStyle style; switch (typeID = findKeyword(segments[2], TYPE_IDS)) { case 0: // string argType = Formattable::kString; break; case 1: // number argType = Formattable::kDouble; switch (findKeyword(segments[3], NUMBER_STYLE_IDS)) { case 0: // default fmt = NumberFormat::createInstance(fLocale, ec); break; case 1: // currency fmt = NumberFormat::createCurrencyInstance(fLocale, ec); break; case 2: // percent fmt = NumberFormat::createPercentInstance(fLocale, ec); break; case 3: // integer argType = Formattable::kLong; fmt = createIntegerFormat(fLocale, ec); break; default: // pattern fmt = NumberFormat::createInstance(fLocale, ec); if (fmt && fmt->getDynamicClassID() == DecimalFormat::getStaticClassID()) { ((DecimalFormat*)fmt)->applyPattern(segments[3],parseError,ec); } break; } break; case 2: // date case 3: // time argType = Formattable::kDate; styleID = findKeyword(segments[3], DATE_STYLE_IDS); style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault; if (typeID == 2) { fmt = DateFormat::createDateInstance(style, fLocale); } else { fmt = DateFormat::createTimeInstance(style, fLocale); } if (styleID < 0 && fmt != NULL && fmt->getDynamicClassID() == SimpleDateFormat::getStaticClassID()) { ((SimpleDateFormat*)fmt)->applyPattern(segments[3]); } break; case 4: // choice argType = Formattable::kDouble; fmt = new ChoiceFormat(segments[3], parseError, ec); break; case 5: // spellout argType = Formattable::kDouble; fmt = makeRBNF(URBNF_SPELLOUT, fLocale, segments[3], ec); break; case 6: // ordinal argType = Formattable::kDouble; fmt = makeRBNF(URBNF_ORDINAL, fLocale, segments[3], ec); break; case 7: // duration argType = Formattable::kDouble; fmt = makeRBNF(URBNF_DURATION, fLocale, segments[3], ec); break; default: argType = Formattable::kString; ec = U_ILLEGAL_ARGUMENT_ERROR; break; } if (fmt==NULL && argType!=Formattable::kString && U_SUCCESS(ec)) { ec = U_MEMORY_ALLOCATION_ERROR; } if (!allocateSubformats(formatNumber+1) || !allocateArgTypes(argumentNumber+1)) { ec = U_MEMORY_ALLOCATION_ERROR; } if (U_FAILURE(ec)) { delete fmt; return; } // Parse succeeded; record results in our arrays subformats[formatNumber].format = fmt; subformats[formatNumber].offset = segments[0].length(); subformats[formatNumber].arg = argumentNumber; subformatCount = formatNumber+1; // Careful here: argumentNumber may in general arrive out of // sequence, e.g., "There was {2} on {0,date} (see {1,number})." argTypes[argumentNumber] = argType; if (argumentNumber+1 > argTypeCount) { argTypeCount = argumentNumber+1; } } // ------------------------------------- // Finds the string, s, in the string array, list. int32_t MessageFormat::findKeyword(const UnicodeString& s, const UChar * const *list) { if (s.length() == 0) return 0; // default UnicodeString buffer = s; // Trims the space characters and turns all characters // in s to lower case. buffer.trim().toLower(""); for (int32_t i = 0; list[i]; ++i) { if (!buffer.compare(list[i], u_strlen(list[i]))) { return i; } } return -1; } // ------------------------------------- // Checks the range of the source text to quote the special // characters, { and ' and copy to target buffer. void MessageFormat::copyAndFixQuotes(const UnicodeString& source, int32_t start, int32_t end, UnicodeString& appendTo) { UBool gotLB = FALSE; for (int32_t i = start; i < end; ++i) { UChar ch = source[i]; if (ch == LEFT_CURLY_BRACE) { appendTo += SINGLE_QUOTE; appendTo += LEFT_CURLY_BRACE; appendTo += SINGLE_QUOTE; gotLB = TRUE; } else if (ch == RIGHT_CURLY_BRACE) { if(gotLB) { appendTo += RIGHT_CURLY_BRACE; gotLB = FALSE; } else { // orig code. appendTo += SINGLE_QUOTE; appendTo += RIGHT_CURLY_BRACE; appendTo += SINGLE_QUOTE; } } else if (ch == SINGLE_QUOTE) { appendTo += SINGLE_QUOTE; appendTo += SINGLE_QUOTE; } else { appendTo += ch; } } } /** * Convenience method that ought to be in NumberFormat */ NumberFormat* MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const { NumberFormat *temp = NumberFormat::createInstance(locale, status); if (temp->getDynamicClassID() == DecimalFormat::getStaticClassID()) { DecimalFormat *temp2 = (DecimalFormat*) temp; temp2->setMaximumFractionDigits(0); temp2->setDecimalSeparatorAlwaysShown(FALSE); temp2->setParseIntegerOnly(TRUE); } return temp; } /** * Return the default number format. Used to format a numeric * argument when subformats[i].format is NULL. Returns NULL * on failure. * * Semantically const but may modify *this. */ const NumberFormat* MessageFormat::getDefaultNumberFormat(UErrorCode& ec) const { if (defaultNumberFormat == NULL) { MessageFormat* t = (MessageFormat*) this; t->defaultNumberFormat = NumberFormat::createInstance(fLocale, ec); if (U_FAILURE(ec)) { delete t->defaultNumberFormat; t->defaultNumberFormat = NULL; } else if (t->defaultNumberFormat == NULL) { ec = U_MEMORY_ALLOCATION_ERROR; } } return defaultNumberFormat; } /** * Return the default date format. Used to format a date * argument when subformats[i].format is NULL. Returns NULL * on failure. * * Semantically const but may modify *this. */ const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const { if (defaultDateFormat == NULL) { MessageFormat* t = (MessageFormat*) this; t->defaultDateFormat = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale); if (t->defaultDateFormat == NULL) { ec = U_MEMORY_ALLOCATION_ERROR; } } return defaultDateFormat; } U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ //eof