/* ******************************************************************************* * Copyright (C) 1997-1999, 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/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 "ustrfmt.h" #include "cmemory.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 g_umsg_number[] = { 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */ }; static const UChar g_umsg_date[] = { 0x64, 0x61, 0x74, 0x65, 0 /* "date" */ }; static const UChar g_umsg_time[] = { 0x74, 0x69, 0x6D, 0x65, 0 /* "time" */ }; static const UChar g_umsg_choice[] = { 0x63, 0x68, 0x6F, 0x69, 0x63, 0x65, 0 /* "choice" */ }; // MessageFormat Type List Number, Date, Time or Choice static const UChar * const g_umsgTypeList[] = { NULL, NULL, g_umsg_number, NULL, g_umsg_date, NULL, g_umsg_time, NULL, g_umsg_choice }; static const UChar g_umsg_currency[] = { 0x63, 0x75, 0x72, 0x72, 0x65, 0x6E, 0x63, 0x79, 0 /* "currency" */ }; static const UChar g_umsg_percent[] = { 0x70, 0x65, 0x72, 0x63, 0x65, 0x6E, 0x74, 0 /* "percent" */ }; static const UChar g_umsg_integer[] = { 0x69, 0x6E, 0x74, 0x65, 0x67, 0x65, 0x72, 0 /* "integer" */ }; // NumberFormat modifier list, default, currency, percent or integer static const UChar * const g_umsgModifierList[] = { NULL, NULL, g_umsg_currency, NULL, g_umsg_percent, NULL, g_umsg_integer, NULL, NULL }; static const UChar g_umsg_short[] = { 0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */ }; static const UChar g_umsg_medium[] = { 0x6D, 0x65, 0x64, 0x69, 0x75, 0x6D, 0 /* "medium" */ }; static const UChar g_umsg_long[] = { 0x6C, 0x6F, 0x6E, 0x67, 0 /* "long" */ }; static const UChar g_umsg_full[] = { 0x66, 0x75, 0x6C, 0x6C, 0 /* "full" */ }; // DateFormat modifier list, default, short, medium, long or full static const UChar * const g_umsgDateModifierList[] = { NULL, NULL, g_umsg_short, NULL, g_umsg_medium, NULL, g_umsg_long, NULL, g_umsg_full }; static const int32_t g_umsgListLength = 9; U_NAMESPACE_BEGIN // ------------------------------------- const char MessageFormat::fgClassID = 0; // Value is irrelevant // ------------------------------------- // Creates a MessageFormat instance based on the pattern. MessageFormat::MessageFormat(const UnicodeString& pattern, UErrorCode& success) : fLocale(Locale::getDefault()), // Uses the default locale fOffsets(NULL), fCount(kMaxFormat), fArgumentNumbers(NULL) { fOffsets = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fOffsets == 0) { success = U_MEMORY_ALLOCATION_ERROR; return; } fArgumentNumbers = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fArgumentNumbers == 0) { success = U_MEMORY_ALLOCATION_ERROR; uprv_free(fOffsets); fOffsets = NULL; return; } for (int32_t i = 0; i < fCount; i++) { fFormats[i] = NULL; // Format instances fOffsets[i] = 0; // Starting offset fArgumentNumbers[i] = 0; // Argument numbers. } applyPattern(pattern, success); } MessageFormat::MessageFormat(const UnicodeString& pattern, const Locale& newLocale, UErrorCode& success) : fLocale(newLocale), // Uses the default locale fOffsets(NULL), fCount(kMaxFormat), fArgumentNumbers(NULL) { fOffsets = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fOffsets == 0) { success = U_MEMORY_ALLOCATION_ERROR; return; } fArgumentNumbers = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fArgumentNumbers == 0) { success = U_MEMORY_ALLOCATION_ERROR; uprv_free(fOffsets); fOffsets = NULL; return; } for (int32_t i = 0; i < fCount; i++) { fFormats[i] = NULL; // Format instances fOffsets[i] = 0; // Starting offset fArgumentNumbers[i] = 0; // Argument numbers. } applyPattern(pattern, success); } MessageFormat::MessageFormat(const UnicodeString& pattern, const Locale& newLocale, UParseError& parseError, UErrorCode& success) : fLocale(newLocale), // Uses the default locale fOffsets(NULL), fCount(kMaxFormat), fArgumentNumbers(NULL) { fOffsets = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fOffsets == 0) { success = U_MEMORY_ALLOCATION_ERROR; return; } fArgumentNumbers = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); /* test for NULL */ if (fArgumentNumbers == 0) { success = U_MEMORY_ALLOCATION_ERROR; uprv_free(fOffsets); fOffsets = NULL; return; } for (int32_t i = 0; i < fCount; i++) { fFormats[i] = NULL; // Format instances fOffsets[i] = 0; // Starting offset fArgumentNumbers[i] = 0; // Argument numbers. } applyPattern(pattern,parseError, success); } MessageFormat::~MessageFormat() { for (int32_t i = 0; i < fCount; i++) { if (fFormats[i]) { delete fFormats[i]; } } uprv_free(fOffsets); uprv_free(fArgumentNumbers); fCount = 0; } // ------------------------------------- // copy constructor MessageFormat::MessageFormat(const MessageFormat& that) : Format(that), fLocale(that.fLocale), fPattern(that.fPattern), fOffsets((int32_t *)uprv_malloc(that.fCount * sizeof(int32_t))), fCount(that.fCount), fArgumentNumbers((int32_t *)uprv_malloc(that.fCount * sizeof(int32_t))), fMaxOffset(that.fMaxOffset) { // Sets up the format instance array, offsets and argument numbers. for (int32_t i = 0; i < fCount; i++) { fFormats[i] = NULL; // init since delete may be called if (that.fFormats[i] != NULL) { setFormat(i, *(that.fFormats[i]) ); // setFormat clones the format } fOffsets[i] = that.fOffsets[i]; fArgumentNumbers[i] = that.fArgumentNumbers[i]; } } // ------------------------------------- // assignment operator const MessageFormat& MessageFormat::operator=(const MessageFormat& that) { if (this != &that) { // Calls the super class for assignment first. Format::operator=(that); // Cleans up the format array and the offsets, argument numbers. for (int32_t j = 0; j < fCount; j++) { delete fFormats[j]; fFormats[j] = NULL; } uprv_free(fOffsets); fOffsets = NULL; uprv_free(fArgumentNumbers); fArgumentNumbers = NULL; fPattern = that.fPattern; fLocale = that.fLocale; fCount = that.fCount; fMaxOffset = that.fMaxOffset; fOffsets = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); fArgumentNumbers = (int32_t*) uprv_malloc( sizeof(int32_t) * fCount ); // Sets up the format instance array, offsets and argument numbers. for (int32_t i = 0; i < fCount; i++) { if (that.fFormats[i] == NULL) { fFormats[i] = NULL; }else{ adoptFormat(i, that.fFormats[i]->clone()); } fOffsets[i] = that.fOffsets[i]; fArgumentNumbers[i] = that.fArgumentNumbers[i]; } } return *this; } UBool MessageFormat::operator==(const Format& that) const { if (this == &that) return TRUE; // Are the instances derived from the same Format class? if (getStaticClassID() != that.getDynamicClassID()) return FALSE; // not the same class // Calls the super class for equality check first. if (!Format::operator==(that)) return FALSE; MessageFormat& thatAlias = (MessageFormat&)that; // Checks the pattern, locale and array count of this MessageFormat object. if (fMaxOffset != thatAlias.fMaxOffset) return FALSE; if (fPattern != thatAlias.fPattern) return FALSE; if (fLocale != thatAlias.fLocale) return FALSE; if (fCount != thatAlias.fCount) return FALSE; // Checks each element in the arrays for equality last. for (int32_t i = 0; i < fCount; i++) { if ((fFormats[i] != thatAlias.fFormats[i]) || (fOffsets[i] != thatAlias.fOffsets[i]) || (fArgumentNumbers[i] != thatAlias.fArgumentNumbers[i])) return FALSE; } return TRUE; } // ------------------------------------- // Creates a copy of this MessageFormat, the caller owns the copy. Format* MessageFormat::clone() const { MessageFormat *aCopy = new MessageFormat(*this); return aCopy; } // ------------------------------------- // Sets the locale of this MessageFormat object to theLocale. void MessageFormat::setLocale(const Locale& theLocale) { fLocale = theLocale; } // ------------------------------------- // Gets the locale of this MessageFormat object. const Locale& MessageFormat::getLocale() const { return fLocale; } #if 0 // ------------------------------------- // Applies the new pattern and returns an error if the pattern // is not correct. // For example, consider the pattern, // "There {0,choice,0#are no files|1#is one file|1 maxFormatNumber) { maxFormatNumber = currFormatNumber; } } else { --braceStack; segments[part] += ch; } break; case SINGLE_QUOTE: inQuote = TRUE; // fall through, so we keep quotes in other parts default: segments[part] += ch; break; } } } if (braceStack == 0 && part != 0) { fMaxOffset = -1; success = U_UNMATCHED_BRACES; syntaxError(newPattern,i,parseError); return; //throw new IllegalArgumentException("Unmatched braces in the pattern."); } fPattern = segments[0]; fListCount = maxFormatNumber + 1; } // ------------------------------------- // Converts this MessageFormat instance to a pattern. UnicodeString& MessageFormat::toPattern(UnicodeString& result) const { // later, make this more extensible int32_t lastOffset = 0; for (int i = 0; i <= fMaxOffset; ++i) { copyAndFixQuotes(fPattern, lastOffset, fOffsets[i], result); lastOffset = fOffsets[i]; result += LEFT_CURLY_BRACE; // {sfb} check this later //result += (UChar) (fArgumentNumbers[i] + '0'); UnicodeString temp; result += itos(fArgumentNumbers[i], temp); if (fFormats[i] == NULL) { // do nothing, string format } else if (fFormats[i]->getDynamicClassID() == DecimalFormat::getStaticClassID()) { UErrorCode status = U_ZERO_ERROR; NumberFormat& formatAlias = *(NumberFormat*)fFormats[i]; NumberFormat *numberTemplate = NumberFormat::createInstance(fLocale, status); NumberFormat *currencyTemplate = NumberFormat::createCurrencyInstance(fLocale, status); NumberFormat *percentTemplate = NumberFormat::createPercentInstance(fLocale, status); NumberFormat *integerTemplate = createIntegerFormat(fLocale, status); result += COMMA; result += g_umsg_number; if (formatAlias != *numberTemplate) { result += COMMA; if (formatAlias == *currencyTemplate) { result += g_umsg_currency; } else if (formatAlias == *percentTemplate) { result += g_umsg_percent; } else if (formatAlias == *integerTemplate) { result += g_umsg_integer; } else { UnicodeString buffer; result += ((DecimalFormat*)fFormats[i])->toPattern(buffer); } } delete numberTemplate; delete currencyTemplate; delete percentTemplate; delete integerTemplate; } else if (fFormats[i]->getDynamicClassID() == SimpleDateFormat::getStaticClassID()) { DateFormat& formatAlias = *(DateFormat*)fFormats[i]; 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); result += COMMA; if (formatAlias == *defaultDateTemplate) { result += g_umsg_date; } else if (formatAlias == *shortDateTemplate) { result += g_umsg_date; result += COMMA; result += g_umsg_short; } else if (formatAlias == *defaultDateTemplate) { result += g_umsg_date; result += COMMA; result += g_umsg_medium; } else if (formatAlias == *longDateTemplate) { result += g_umsg_date; result += COMMA; result += g_umsg_long; } else if (formatAlias == *fullDateTemplate) { result += g_umsg_date; result += COMMA; result += g_umsg_full; } else if (formatAlias == *defaultTimeTemplate) { result += g_umsg_time; } else if (formatAlias == *shortTimeTemplate) { result += g_umsg_time; result += COMMA; result += g_umsg_short; } else if (formatAlias == *defaultTimeTemplate) { result += g_umsg_time; result += COMMA; result += g_umsg_medium; } else if (formatAlias == *longTimeTemplate) { result += g_umsg_time; result += COMMA; result += g_umsg_long; } else if (formatAlias == *fullTimeTemplate) { result += g_umsg_time; result += COMMA; result += g_umsg_full; } else { UnicodeString buffer; result += g_umsg_date; result += COMMA; result += ((SimpleDateFormat*)fFormats[i])->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 (fFormats[i]->getDynamicClassID() == ChoiceFormat::getStaticClassID()) { UnicodeString buffer; result += COMMA; result += g_umsg_choice; result += COMMA; result += ((ChoiceFormat*)fFormats[i])->toPattern(buffer); } else { //result += ", unknown"; } result += RIGHT_CURLY_BRACE; } copyAndFixQuotes(fPattern, lastOffset, fPattern.length(), result); return result; } // ------------------------------------- // Adopts the new formats array and updates the array count. // This MessageFormat instance owns the new formats. void MessageFormat::adoptFormats(Format** newFormats, int32_t cnt) { if(newFormats == NULL || cnt < 0) return; int32_t i; // Cleans up first. for (i = 0; i < fCount; i++) delete fFormats[i]; fCount = (cnt > kMaxFormat) ? kMaxFormat : cnt; for (i = 0; i < fCount; i++) fFormats[i] = newFormats[i]; for (i = kMaxFormat; i < cnt; i++) delete newFormats[i]; } // ------------------------------------- // Sets the new formats array and updates the array count. // This MessageFormat instance maks a copy of the new formats. void MessageFormat::setFormats(const Format** newFormats, int32_t cnt) { if(newFormats == NULL || cnt < 0) return; int32_t i; // Cleans up first. for (i = 0; i < fCount; i++) { delete fFormats[i]; } fCount = (cnt > kMaxFormat) ? kMaxFormat : cnt; for (i = 0; i < fCount; i++) { if (newFormats[i] == NULL) { fFormats[i] = NULL; } else{ fFormats[i] = newFormats[i]->clone(); } } } // ------------------------------------- // Adopts the first *variable* formats in the format array. // This MessageFormat instance owns the new formats. // Do nothing is the variable is not less than the array count. void MessageFormat::adoptFormat(int32_t variable, Format *newFormat) { if(variable < 0) return; if (variable < fCount) { // Deletes the old formats. delete fFormats[variable]; fFormats[variable] = newFormat; } } // ------------------------------------- // Sets the first *variable* formats in the format array, this // MessageFormat instance makes copies of the new formats. // Do nothing is the variable is not less than the array count. void MessageFormat::setFormat(int32_t variable, const Format& newFormat) { if (variable < fCount) { delete fFormats[variable]; if (&(newFormat) == NULL) { fFormats[variable] = NULL; } else{ fFormats[variable] = newFormat.clone(); } } } // ------------------------------------- // Gets the format array. const Format** MessageFormat::getFormats(int32_t& cnt) const { cnt = fCount; return (const Format**)fFormats; } // ------------------------------------- // 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& result, FieldPosition& ignore, UErrorCode& success) const { if (U_FAILURE(success)) return result; return format(source, cnt, result, ignore, 0, success); } // ------------------------------------- // Internally creates a MessageFormat instance based on the // pattern and formats the arguments Formattable array and // copy into the result buffer. UnicodeString& MessageFormat::format( const UnicodeString& pattern, const Formattable* arguments, int32_t cnt, UnicodeString& result, UErrorCode& success) { // {sfb} why does this use a local when so many other places use a static? MessageFormat *temp = new MessageFormat(pattern, success); /* test for NULL */ if (temp == 0) { success = U_MEMORY_ALLOCATION_ERROR; return result; } if (U_FAILURE(success)) return result; FieldPosition ignore(0); temp->format(arguments, cnt, result, ignore, success); delete temp; return result; } // ------------------------------------- // Formats the source Formattable object and copy into the // result buffer. The Formattable object must be an array // of Formattable instances, returns error otherwise. UnicodeString& MessageFormat::format(const Formattable& source, UnicodeString& result, FieldPosition& ignore, UErrorCode& success) const { int32_t cnt; if (U_FAILURE(success)) return result; if (source.getType() != Formattable::kArray) { success = U_ILLEGAL_ARGUMENT_ERROR; return result; } const Formattable* tmpPtr = source.getArray(cnt); return format(tmpPtr, cnt, result, ignore, 0, success); } // ------------------------------------- // Formats the arguments Formattable array and copy into the result buffer. // Ignore the FieldPosition result for error checking. UnicodeString& MessageFormat::format(const Formattable* arguments, int32_t cnt, UnicodeString& result, FieldPosition& status, int32_t recursionProtection, UErrorCode& success) const { if(/*arguments == NULL ||*/ cnt < 0) { success = U_ILLEGAL_ARGUMENT_ERROR; return result; } UnicodeString buffer; int32_t lastOffset = 0; for (int32_t i = 0; i <= fMaxOffset;++i) { // Cleans up the temp buffer for each formattable arguments. buffer.remove(); // Append the prefix of current format element. fPattern.extract(lastOffset, fOffsets[i] - lastOffset, buffer); result += buffer; lastOffset = fOffsets[i]; int32_t argumentNumber = fArgumentNumbers[i]; // Checks the scope of the argument number. if (argumentNumber >= cnt) { /*success = U_ILLEGAL_ARGUMENT_ERROR; return result;*/ result += LEFT_CURLY_BRACE; UnicodeString temp; result += itos(argumentNumber, temp); result += RIGHT_CURLY_BRACE; continue; } Formattable obj = arguments[argumentNumber]; UnicodeString arg; UBool tryRecursion = FALSE; // Recursively calling the format process only if the current format argument // refers to a ChoiceFormat object. if (fFormats[i] != NULL) { fFormats[i]->format(obj, arg, success); tryRecursion = (fFormats[i]->getDynamicClassID() == ChoiceFormat::getStaticClassID()); } // If the obj data type if a number, use a NumberFormat instance. else if ((obj.getType() == Formattable::kDouble) || (obj.getType() == Formattable::kLong)) { NumberFormat *numTemplate = NULL; numTemplate = NumberFormat::createInstance(fLocale, success); if (U_FAILURE(success)) { delete numTemplate; return result; } numTemplate->format((obj.getType() == Formattable::kDouble) ? obj.getDouble() : obj.getLong(), arg); delete numTemplate; if (U_FAILURE(success)) return result; } // If the obj data type is a Date instance, use a DateFormat instance. else if (obj.getType() == Formattable::kDate) { DateFormat *dateTemplate = NULL; dateTemplate = DateFormat::createDateTimeInstance(DateFormat::kShort, DateFormat::kShort, fLocale); dateTemplate->format(obj.getDate(), arg); delete dateTemplate; } else if (obj.getType() == Formattable::kString) { obj.getString(arg); } else { #ifdef LIUDEBUG cerr << "Unknown object of type:" << obj.getType() << endl; #endif success = U_ILLEGAL_ARGUMENT_ERROR; return result; } // Needs to reprocess the ChoiceFormat option by using the MessageFormat // pattern application. if (tryRecursion && arg.indexOf(LEFT_CURLY_BRACE) >= 0) { MessageFormat *temp = NULL; temp = new MessageFormat(arg, fLocale, success); /* test for NULL */ if (temp == 0) { status = U_MEMORY_ALLOCATION_ERROR; return result; } if (U_FAILURE(success)) return result; temp->format(arguments, cnt, result, status, recursionProtection, success); if (U_FAILURE(success)) { delete temp; return result; } delete temp; } else { result += arg; } } buffer.remove(); // Appends the rest of the pattern characters after the real last offset. fPattern.extract(lastOffset, fPattern.length(), buffer); result += buffer; return result; } // ------------------------------------- // 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& status, int32_t& count) const { Formattable *resultArray = new Formattable[(size_t)kMaxFormat]; int32_t patternOffset = 0; int32_t sourceOffset = status.getIndex(); ParsePosition tempStatus(0); count = 0; // {sfb} reset to zero for (int32_t i = 0; i <= fMaxOffset; ++i) { // match up to format int32_t len = fOffsets[i] - patternOffset; if (len == 0 || fPattern.compare(patternOffset, len, source, sourceOffset, len) == 0) { sourceOffset += len; patternOffset += len; } else { status.setErrorIndex(sourceOffset); delete [] resultArray; count = 0; return NULL; // leave index as is to signal error } // now use format if (fFormats[i] == 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 != fMaxOffset) ? fOffsets[i+1] : fPattern.length(); int32_t next; if (patternOffset >= tempLength) { next = source.length(); } else { UnicodeString buffer; fPattern.extract(patternOffset,tempLength - patternOffset, buffer); next = source.indexOf(buffer, sourceOffset); } if (next < 0) { status.setErrorIndex(sourceOffset); delete [] resultArray; count = 0; return NULL; // leave index as is to signal error } else { UnicodeString buffer; source.extract(sourceOffset,next - sourceOffset, buffer); UnicodeString strValue = buffer; UnicodeString temp(LEFT_CURLY_BRACE); // {sfb} check this later UnicodeString temp1; temp += itos(fArgumentNumbers[i], temp1); temp += RIGHT_CURLY_BRACE; if (strValue != temp) { source.extract(sourceOffset,next - sourceOffset, buffer); resultArray[fArgumentNumbers[i]].setString(buffer); // {sfb} not sure about this if ((fArgumentNumbers[i] + 1) > count) { count = (fArgumentNumbers[i] + 1); } } sourceOffset = next; } } else { tempStatus.setIndex(sourceOffset); fFormats[i]->parseObject(source, resultArray[fArgumentNumbers[i]], tempStatus); if (tempStatus.getIndex() == sourceOffset) { status.setErrorIndex(sourceOffset); delete [] resultArray; count = 0; return NULL; // leave index as is to signal error } if ((fArgumentNumbers[i] + 1) > count) count = (fArgumentNumbers[i] + 1); sourceOffset = tempStatus.getIndex(); // update } } int32_t len = fPattern.length() - patternOffset; if (len == 0 || fPattern.compare(patternOffset, len, source, sourceOffset, len) == 0) { status.setIndex(sourceOffset + len); } else { status.setErrorIndex(sourceOffset); delete [] resultArray; count = 0; return NULL; // leave index as is to signal error } return resultArray; } // ------------------------------------- // 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; 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); } // ------------------------------------- // NumberFormat cache management /* NumberFormat* MessageFormat::getNumberFormat(UErrorCode &status) { NumberFormat *theFormat = 0; if (fgNumberFormat != 0) // if there's something in the cache { Mutex lock; if (fgNumberFormat != 0) // Someone might have grabbed it. { theFormat = fgNumberFormat; fgNumberFormat = 0; // We have exclusive right to this formatter. } } if(theFormat == 0) // If we weren't able to pull it out of the cache, then we have to create it. { theFormat = NumberFormat::createInstance(Locale::US, status); if(U_FAILURE(status)) return 0; theFormat->setParseIntegerOnly(TRUE); } return theFormat; } void MessageFormat::releaseNumberFormat(NumberFormat *adopt) { if(fgNumberFormat == 0) // If the cache is empty we must add it back. { Mutex lock; if(fgNumberFormat == 0) { fgNumberFormat = adopt; adopt = 0; } } delete adopt; } */ /** * Converts a string to an integer value using a default NumberFormat object * which is static (shared by all MessageFormat instances). This replaces * a call to wtoi(). */ int32_t MessageFormat::stoi(const UnicodeString& string) { /* NumberFormat *myFormat = getNumberFormat(status); if(U_FAILURE(status)) return -1; // OK? Formattable result; // Uses the global number formatter to parse the string. // Note: We assume here that parse() is thread-safe. myFormat->parse(string, result, status); releaseNumberFormat(myFormat); int32_t value = 0; if (U_SUCCESS(status) && result.getType() == Formattable::kLong) value = result.getLong(); return value; */ /* this ignores any white spaces between '{' and digit char * so now we can have { 0, date} {0 , date } */ for(int i=0;i" static const UChar ERROR[] = {0x3C, 0x45, 0x52, 0x52, 0x4F, 0x52, 0x3E, 0}; return string = ERROR; // TODO: maybe toPattern should take an errorcode. } UnicodeString &retval = myFormat->format(i, string); releaseNumberFormat(myFormat); return retval; */ UChar temp[10] = { '\0' }; uprv_itou(temp,i,16,0); string.append(temp); return string; } // ------------------------------------- // Checks which format instance we are really using based on the segments. int32_t MessageFormat::makeFormat(/*int32_t position, */ int32_t offsetNumber, UnicodeString* segments, UParseError& parseError, UErrorCode& success) { if(U_FAILURE(success)) return -1; // get the number int32_t argumentNumber = stoi(segments[1]); // always unlocalized! int32_t oldMaxOffset = fMaxOffset; if (argumentNumber < 0 || argumentNumber > 9) { success = U_INVALID_FORMAT_ERROR; return argumentNumber; } fMaxOffset = offsetNumber; fOffsets[offsetNumber] = segments[0].length(); fArgumentNumbers[offsetNumber] = argumentNumber; int test = 0; // now get the format Format *newFormat = NULL; switch (findKeyword(segments[2], g_umsgTypeList)) { case 0: fFormatTypeList[argumentNumber] = Formattable::kString; break; case 1: case 2:// number test=findKeyword(segments[3], g_umsgModifierList); fFormatTypeList[argumentNumber] = Formattable::kDouble; switch (test) { case 0: // default; newFormat = NumberFormat::createInstance(fLocale, success); break; case 1: case 2:// currency newFormat = NumberFormat::createCurrencyInstance(fLocale, success); break; case 3: case 4:// percent newFormat = NumberFormat::createPercentInstance(fLocale, success); break; case 5: case 6:// integer fFormatTypeList[argumentNumber] = Formattable::kLong; newFormat = createIntegerFormat(fLocale, success); break; default: // pattern newFormat = NumberFormat::createInstance(fLocale, success); if(U_FAILURE(success)) { newFormat = NULL; return argumentNumber; } if(newFormat->getDynamicClassID() == DecimalFormat::getStaticClassID()){ ((DecimalFormat*)newFormat)->applyPattern(segments[3],parseError,success); } if(U_FAILURE(success)) { fMaxOffset = oldMaxOffset; return argumentNumber; } break; } break; case 3: case 4: // date fFormatTypeList[argumentNumber] = Formattable::kDate; switch (findKeyword(segments[3], g_umsgDateModifierList)) { case 0: // default newFormat = DateFormat::createDateInstance(DateFormat::kDefault, fLocale); break; case 1: case 2: // short newFormat = DateFormat::createDateInstance(DateFormat::kShort, fLocale); break; case 3: case 4: // medium newFormat = DateFormat::createDateInstance(DateFormat::kDefault, fLocale); break; case 5: case 6: // long newFormat = DateFormat::createDateInstance(DateFormat::kLong, fLocale); break; case 7: case 8: // full newFormat = DateFormat::createDateInstance(DateFormat::kFull, fLocale); break; default: newFormat = DateFormat::createDateInstance(DateFormat::kDefault, fLocale); if(newFormat && newFormat->getDynamicClassID() == SimpleDateFormat::getStaticClassID()){ ((SimpleDateFormat*)newFormat)->applyPattern(segments[3]); } break; } if (!newFormat) { /* TODO: get the real error from createDateInstance */ success = U_MEMORY_ALLOCATION_ERROR; } break; case 5: case 6:// time fFormatTypeList[argumentNumber]= Formattable::kDate; switch (findKeyword(segments[3], g_umsgDateModifierList)) { case 0: // default newFormat = DateFormat::createTimeInstance(DateFormat::kDefault, fLocale); break; case 1: case 2: // short newFormat = DateFormat::createTimeInstance(DateFormat::kShort, fLocale); break; case 3: case 4: // medium newFormat = DateFormat::createTimeInstance(DateFormat::kDefault, fLocale); break; case 5: case 6: // long newFormat = DateFormat::createTimeInstance(DateFormat::kLong, fLocale); break; case 7: case 8: // full newFormat = DateFormat::createTimeInstance(DateFormat::kFull, fLocale); break; default: newFormat = DateFormat::createTimeInstance(DateFormat::kDefault, fLocale); if(newFormat && newFormat->getDynamicClassID() == SimpleDateFormat::getStaticClassID()){ ((SimpleDateFormat*)newFormat)->applyPattern(segments[3]); } break; } if (!newFormat) { /* TODO: get the real error from createTimeInstance */ success = U_MEMORY_ALLOCATION_ERROR; } break; case 7: case 8:// choice fFormatTypeList[argumentNumber] = Formattable::kDouble; newFormat = new ChoiceFormat(segments[3], parseError, success); /* test for NULL */ if (newFormat == 0) { success = U_MEMORY_ALLOCATION_ERROR; fMaxOffset = oldMaxOffset; return argumentNumber; } if(U_FAILURE(success)) { fMaxOffset = oldMaxOffset; return argumentNumber; } break; default: fMaxOffset = oldMaxOffset; success = U_ILLEGAL_ARGUMENT_ERROR; return argumentNumber; } if(newFormat != NULL) { delete fFormats[offsetNumber]; fFormats[offsetNumber] = newFormat; } segments[1].remove(); // throw away other segments segments[2].remove(); segments[3].remove(); return argumentNumber; } // ------------------------------------- // 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; UnicodeString buffer = s; // Trims the space characters and turns all characters // in s to lower case. buffer.trim().toLower(); for (int32_t i = 0; i < g_umsgListLength; ++i) { if (list[i] && !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& target) { UBool gotLB = FALSE; for (int32_t i = start; i < end; ++i) { UChar ch = source[i]; if (ch == LEFT_CURLY_BRACE) { target += SINGLE_QUOTE; target += LEFT_CURLY_BRACE; target += SINGLE_QUOTE; gotLB = TRUE; } else if (ch == RIGHT_CURLY_BRACE) { if(gotLB) { target += RIGHT_CURLY_BRACE; gotLB = FALSE; } else { // orig code. target += SINGLE_QUOTE; target += RIGHT_CURLY_BRACE; target += SINGLE_QUOTE; } } else if (ch == SINGLE_QUOTE) { target += SINGLE_QUOTE; target += SINGLE_QUOTE; } else { target += 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; } U_NAMESPACE_END //eof