/******************************************************************** * COPYRIGHT: * Copyright (c) 1997-2010, 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 * 11/01/09 kirtig Added SelectFormat ********************************************************************/ #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/plurfmt.h" #include "unicode/selfmt.h" #include "unicode/ustring.h" #include "unicode/ucnv_err.h" #include "unicode/uchar.h" #include "unicode/umsg.h" #include "unicode/rbnf.h" #include "cmemory.h" #include "msgfmt_impl.h" #include "util.h" #include "uassert.h" #include "ustrfmt.h" #include "uvector.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" */ }; static const UChar ID_PLURAL[] = { 0x70, 0x6c, 0x75, 0x72, 0x61, 0x6c, 0 /* "plural" */ }; static const UChar ID_SELECT[] = { 0x73, 0x65, 0x6C, 0x65, 0x63, 0x74, 0 /* "select" */ }; // 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, ID_PLURAL, ID_SELECT, 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 U_NAMESPACE_QUALIFIER DateFormat::EStyle DATE_STYLES[] = { U_NAMESPACE_QUALIFIER DateFormat::kDefault, U_NAMESPACE_QUALIFIER DateFormat::kShort, U_NAMESPACE_QUALIFIER DateFormat::kMedium, U_NAMESPACE_QUALIFIER DateFormat::kLong, U_NAMESPACE_QUALIFIER DateFormat::kFull, }; static const int32_t DEFAULT_INITIAL_CAPACITY = 10; U_NAMESPACE_BEGIN // ------------------------------------- UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat) UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration) //-------------------------------------------------------------------- /** * 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; } /* * A structure representing one subformat of this MessageFormat. * Each subformat has a Format object, an offset into the plain * pattern text fPattern, and an argument number. The argument * number corresponds to the array of arguments to be formatted. * @internal */ class MessageFormat::Subformat : public UMemory { public: /** * @internal */ Format* format; // formatter /** * @internal */ int32_t offset; // offset into fPattern /** * @internal */ // TODO (claireho) or save the number to argName and use itos to convert to number.=> we need this number int32_t argNum; // 0-based argument number /** * @internal */ UnicodeString* argName; // argument name or number /** * Clone that.format and assign it to this.format * Do NOT delete this.format * @internal */ Subformat& operator=(const Subformat& that) { if (this != &that) { format = that.format ? that.format->clone() : NULL; offset = that.offset; argNum = that.argNum; argName = (that.argNum==-1) ? new UnicodeString(*that.argName): NULL; } return *this; } /** * @internal */ UBool operator==(const Subformat& that) const { // Do cheap comparisons first return offset == that.offset && argNum == that.argNum && ((argName == that.argName) || (*argName == *that.argName)) && ((format == that.format) || // handles NULL (*format == *that.format)); } /** * @internal */ UBool operator!=(const Subformat& that) const { return !operator==(that); } }; // ------------------------------------- // 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), idStart(UCHAR_ID_START), idContinue(UCHAR_ID_CONTINUE), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), isArgNumeric(TRUE), 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), idStart(UCHAR_ID_START), idContinue(UCHAR_ID_CONTINUE), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), isArgNumeric(TRUE), 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), idStart(UCHAR_ID_START), idContinue(UCHAR_ID_CONTINUE), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), isArgNumeric(TRUE), 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), idStart(UCHAR_ID_START), idContinue(UCHAR_ID_CONTINUE), subformats(NULL), subformatCount(0), subformatCapacity(0), argTypes(NULL), argTypeCount(0), argTypeCapacity(0), isArgNumeric(TRUE), defaultNumberFormat(NULL), defaultDateFormat(NULL) { *this = that; } MessageFormat::~MessageFormat() { int32_t idx; for (idx = 0; idx < subformatCount; idx++) { delete subformats[idx].format; delete subformats[idx].argName; } 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; i(fmt)) != NULL) { UErrorCode ec = U_ZERO_ERROR; NumberFormat& formatAlias = *decfmt; 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 += decfmt->toPattern(buffer); } } delete defaultTemplate; delete currencyTemplate; delete percentTemplate; delete integerTemplate; } else if ((sdtfmt = dynamic_cast(fmt)) != NULL) { DateFormat& formatAlias = *sdtfmt; 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 += sdtfmt->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 ((chcfmt = dynamic_cast(fmt)) != NULL) { UnicodeString buffer; appendTo += COMMA; appendTo += ID_CHOICE; appendTo += COMMA; appendTo += ((ChoiceFormat*)fmt)->toPattern(buffer); } else if ((plfmt = dynamic_cast(fmt)) != NULL) { UnicodeString buffer; appendTo += plfmt->toPattern(buffer); } else if ((selfmt = dynamic_cast(fmt)) != NULL) { UnicodeString buffer; appendTo += ((SelectFormat*)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 by format number. // Do nothing if 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; } } // ------------------------------------- // Adopt a single format by format name. // Do nothing if there is no match of formatName. void MessageFormat::adoptFormat(const UnicodeString& formatName, Format* formatToAdopt, UErrorCode& status) { if (isArgNumeric ) { int32_t argumentNumber = stou(formatName); if (argumentNumber<0) { status = U_ARGUMENT_TYPE_MISMATCH; return; } adoptFormat(argumentNumber, formatToAdopt); return; } for (int32_t i=0; i= 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(); } } } // ------------------------------------- // Get a single format by format name. // Do nothing if the variable is not less than the array count. Format * MessageFormat::getFormat(const UnicodeString& formatName, UErrorCode& status) { if (U_FAILURE(status)) return NULL; if (isArgNumeric ) { int32_t argumentNumber = stou(formatName); if (argumentNumber<0) { status = U_ARGUMENT_TYPE_MISMATCH; return NULL; } if (argumentNumber < 0 || argumentNumber >= subformatCount) { return subformats[argumentNumber].format; } else { return NULL; } } for (int32_t i=0; iformatAliasesCapacity = (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; } StringEnumeration* MessageFormat::getFormatNames(UErrorCode& status) { if (U_FAILURE(status)) return NULL; if (isArgNumeric) { status = U_ARGUMENT_TYPE_MISMATCH; return NULL; } UVector *fFormatNames = new UVector(status); if (U_FAILURE(status)) { status = U_MEMORY_ALLOCATION_ERROR; return NULL; } for (int32_t i=0; iaddElement(new UnicodeString(*subformats[i].argName), status); } StringEnumeration* nameEnumerator = new FormatNameEnumeration(fFormatNames, status); return nameEnumerator; } // ------------------------------------- // 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); } UnicodeString& MessageFormat::format(const UnicodeString* argumentNames, const Formattable* arguments, int32_t count, UnicodeString& appendTo, UErrorCode& success) const { FieldPosition ignore(0); return format(arguments, argumentNames, count, appendTo, ignore, 0, success); } UnicodeString& MessageFormat::format(const Formattable* arguments, int32_t cnt, UnicodeString& appendTo, FieldPosition& status, int32_t recursionProtection, UErrorCode& success) const { return format(arguments, NULL, cnt, appendTo, status, recursionProtection, 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, const UnicodeString *argumentNames, int32_t cnt, UnicodeString& appendTo, FieldPosition& status, int32_t recursionProtection, UErrorCode& success) const { int32_t lastOffset = 0; int32_t argumentNumber=0; if (cnt < 0 || (cnt && arguments == NULL)) { success = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } if ( !isArgNumeric && argumentNames== NULL ) { success = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; } const Formattable *obj=NULL; for (int32_t i=0; i= cnt) { appendTo += LEFT_CURLY_BRACE; itos(argumentNumber, appendTo); appendTo += RIGHT_CURLY_BRACE; continue; } obj = arguments+argumentNumber; } else { for (int32_t j=0; jgetType(); // Recursively calling the format process only if the current // format argument refers to either of the following: // a ChoiceFormat object, a PluralFormat object, a SelectFormat object. Format* fmt = subformats[i].format; if (fmt != NULL) { UnicodeString argNum; fmt->format(*obj, argNum, success); // Needs to reprocess the ChoiceFormat and PluralFormat and SelectFormat option by using the // MessageFormat pattern application. if ((dynamic_cast(fmt) != NULL || dynamic_cast(fmt) != NULL || dynamic_cast(fmt) != NULL) && argNum.indexOf(LEFT_CURLY_BRACE) >= 0 ) { MessageFormat temp(argNum, fLocale, success); // TODO: Implement recursion protection if ( isArgNumeric ) { temp.format(arguments, NULL, cnt, appendTo, status, recursionProtection, success); } else { temp.format(arguments, argumentNames, cnt, appendTo, status, recursionProtection, success); } if (U_FAILURE(success)) { return appendTo; } } else { appendTo += argNum; } } // 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; // If resultArray could not be created, exit out. // Avoid crossing initialization of variables above. if (resultArray == NULL) { goto PARSE_ERROR; } 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 argNum = subformats[i].argNum; 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 if (isArgNumeric) { itos(argNum, temp); } else { temp+=(*subformats[i].argName); } temp += RIGHT_CURLY_BRACE; if (strValue != temp) { source.extract(sourceOffset,next - sourceOffset, buffer); resultArray[argNum].setString(buffer); // {sfb} not sure about this if ((argNum + 1) > count) { count = argNum + 1; } } sourceOffset = next; } } else { tempPos.setIndex(sourceOffset); fmt->parseObject(source, resultArray[argNum], tempPos); if (tempPos.getIndex() == sourceOffset) { goto PARSE_ERROR; } if ((argNum + 1) > count) { count = argNum + 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 { if (!isArgNumeric ) { success = U_ARGUMENT_TYPE_MISMATCH; return NULL; } 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); result.releaseBuffer(U_SUCCESS(status) ? len : 0); } } 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 (fmt == NULL) { ec = U_MEMORY_ALLOCATION_ERROR; } else if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) { UErrorCode localStatus = U_ZERO_ERROR; // ignore unrecognized default rule set fmt->setDefaultRuleSet(defaultRuleSet, localStatus); } 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! UnicodeString argumentName; if (argumentNumber < 0) { if ( (isArgNumeric==TRUE) && (formatNumber !=0) ) { ec = U_INVALID_FORMAT_ERROR; return; } isArgNumeric = FALSE; argumentNumber=formatNumber; } if (!isArgNumeric) { if ( !isLegalArgName(segments[1]) ) { ec = U_INVALID_FORMAT_ERROR; return; } argumentName = segments[1]; } // 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; UnicodeString unquotedPattern, quotedPattern; UBool inQuote = FALSE; 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) { DecimalFormat* decfmt = dynamic_cast(fmt); if (decfmt != NULL) { decfmt->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) { SimpleDateFormat* sdtfmt = dynamic_cast(fmt); if (sdtfmt != NULL) { sdtfmt->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; case 8: // plural case 9: // Select if(typeID == 8) argType = Formattable::kDouble; else argType = Formattable::kString; quotedPattern = segments[3]; for (int32_t i = 0; i < quotedPattern.length(); ++i) { UChar ch = quotedPattern.charAt(i); if (ch == SINGLE_QUOTE) { if (i+1 < quotedPattern.length() && quotedPattern.charAt(i+1)==SINGLE_QUOTE) { unquotedPattern+=ch; ++i; } else { inQuote = !inQuote; } } else { unquotedPattern += ch; } } if(typeID == 8) fmt = new PluralFormat(fLocale, unquotedPattern, ec); else fmt = new SelectFormat(unquotedPattern, 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(); if (isArgNumeric) { subformats[formatNumber].argName = NULL; subformats[formatNumber].argNum = argumentNumber; } else { subformats[formatNumber].argName = new UnicodeString(argumentName); subformats[formatNumber].argNum = -1; } 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); DecimalFormat *temp2; if (temp != NULL && (temp2 = dynamic_cast(temp)) != NULL) { 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; } UBool MessageFormat::usesNamedArguments() const { return !isArgNumeric; } UBool MessageFormat::isLegalArgName(const UnicodeString& argName) const { if(!u_hasBinaryProperty(argName.charAt(0), idStart)) { return FALSE; } for (int32_t i=1; isize()) { return (const UnicodeString*)fFormatNames->elementAt(pos++); } return NULL; } void FormatNameEnumeration::reset(UErrorCode& /*status*/) { pos=0; } int32_t FormatNameEnumeration::count(UErrorCode& /*status*/) const { return (fFormatNames==NULL) ? 0 : fFormatNames->size(); } FormatNameEnumeration::~FormatNameEnumeration() { UnicodeString *s; for (int32_t i=0; isize(); ++i) { if ((s=(UnicodeString *)fFormatNames->elementAt(i))!=NULL) { delete s; } } delete fFormatNames; } U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ //eof