| Index: icu46/source/i18n/msgfmt.cpp
|
| ===================================================================
|
| --- icu46/source/i18n/msgfmt.cpp (revision 0)
|
| +++ icu46/source/i18n/msgfmt.cpp (revision 0)
|
| @@ -0,0 +1,1858 @@
|
| +/********************************************************************
|
| + * 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<string.length(); i+=U16_LENGTH(c)) {
|
| + c = string.char32At(i);
|
| + if (uprv_isRuleWhiteSpace(c)) {
|
| + continue;
|
| + }
|
| + int32_t d = u_digit(c, 10);
|
| + if (d < 0 || ++count > 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<capacity; ++i) {
|
| + argTypes[i] = Formattable::kString;
|
| + }
|
| + } else if (argTypeCapacity < capacity) {
|
| + if (capacity < 2*argTypeCapacity) {
|
| + capacity = 2*argTypeCapacity;
|
| + }
|
| + Formattable::Type* a = (Formattable::Type*)
|
| + uprv_realloc(argTypes, sizeof(*argTypes) * capacity);
|
| + if (a == NULL) {
|
| + return FALSE; // request failed
|
| + }
|
| + for (int32_t i=argTypeCapacity; i<capacity; ++i) {
|
| + a[i] = Formattable::kString;
|
| + }
|
| + argTypes = a;
|
| + argTypeCapacity = capacity;
|
| + }
|
| + return TRUE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// assignment operator
|
| +
|
| +const MessageFormat&
|
| +MessageFormat::operator=(const MessageFormat& that)
|
| +{
|
| + // Reallocate the arrays BEFORE changing this object
|
| + if (this != &that &&
|
| + allocateSubformats(that.subformatCount) &&
|
| + allocateArgTypes(that.argTypeCount)) {
|
| +
|
| + // Calls the super class for assignment first.
|
| + Format::operator=(that);
|
| +
|
| + fPattern = that.fPattern;
|
| + setLocale(that.fLocale);
|
| + isArgNumeric = that.isArgNumeric;
|
| + int32_t j;
|
| + for (j=0; j<subformatCount; ++j) {
|
| + delete subformats[j].format;
|
| + }
|
| + subformatCount = 0;
|
| +
|
| + for (j=0; j<that.subformatCount; ++j) {
|
| + // Subformat::operator= does NOT delete this.format
|
| + subformats[j] = that.subformats[j];
|
| + }
|
| + subformatCount = that.subformatCount;
|
| +
|
| + for (j=0; j<that.argTypeCount; ++j) {
|
| + argTypes[j] = that.argTypes[j];
|
| + }
|
| + argTypeCount = that.argTypeCount;
|
| + }
|
| + return *this;
|
| +}
|
| +
|
| +UBool
|
| +MessageFormat::operator==(const Format& rhs) const
|
| +{
|
| + if (this == &rhs) return TRUE;
|
| +
|
| + MessageFormat& that = (MessageFormat&)rhs;
|
| +
|
| + // Check class ID before checking MessageFormat members
|
| + if (!Format::operator==(rhs) ||
|
| + fPattern != that.fPattern ||
|
| + fLocale != that.fLocale ||
|
| + isArgNumeric != that.isArgNumeric) {
|
| + return FALSE;
|
| + }
|
| +
|
| + int32_t j;
|
| + for (j=0; j<subformatCount; ++j) {
|
| + if (subformats[j] != that.subformats[j]) {
|
| + return FALSE;
|
| + }
|
| + }
|
| +
|
| + return TRUE;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Creates a copy of this MessageFormat, the caller owns the copy.
|
| +
|
| +Format*
|
| +MessageFormat::clone() const
|
| +{
|
| + return new MessageFormat(*this);
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Sets the locale of this MessageFormat object to theLocale.
|
| +
|
| +void
|
| +MessageFormat::setLocale(const Locale& theLocale)
|
| +{
|
| + if (fLocale != theLocale) {
|
| + delete defaultNumberFormat;
|
| + defaultNumberFormat = NULL;
|
| + delete defaultDateFormat;
|
| + defaultDateFormat = NULL;
|
| + }
|
| + fLocale = theLocale;
|
| + setLocaleIDs(fLocale.getName(), fLocale.getName());
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Gets the locale of this MessageFormat object.
|
| +
|
| +const Locale&
|
| +MessageFormat::getLocale() const
|
| +{
|
| + return fLocale;
|
| +}
|
| +
|
| +
|
| +
|
| +
|
| +void
|
| +MessageFormat::applyPattern(const UnicodeString& newPattern,
|
| + UErrorCode& status)
|
| +{
|
| + UParseError parseError;
|
| + applyPattern(newPattern,parseError,status);
|
| +}
|
| +
|
| +
|
| +// -------------------------------------
|
| +// Applies the new pattern and returns an error if the pattern
|
| +// is not correct.
|
| +void
|
| +MessageFormat::applyPattern(const UnicodeString& pattern,
|
| + UParseError& parseError,
|
| + UErrorCode& ec)
|
| +{
|
| + if(U_FAILURE(ec)) {
|
| + return;
|
| + }
|
| + // The pattern is broken up into segments. Each time a subformat
|
| + // is encountered, 4 segments are recorded. For example, consider
|
| + // the pattern:
|
| + // "There {0,choice,0.0#are no files|1.0#is one file|1.0<are {0, number} files} on disk {1}."
|
| + // The first set of segments is:
|
| + // segments[0] = "There "
|
| + // segments[1] = "0"
|
| + // segments[2] = "choice"
|
| + // segments[3] = "0.0#are no files|1.0#is one file|1.0<are {0, number} files"
|
| +
|
| + // During parsing, the plain text is accumulated into segments[0].
|
| + // Segments 1..3 are used to parse each subpattern. Each time a
|
| + // subpattern is parsed, it creates a format object that is stored
|
| + // in the subformats array, together with an offset and argument
|
| + // number. The offset into the plain text stored in
|
| + // segments[0].
|
| +
|
| + // Quotes in segment 0 are handled normally. They are removed.
|
| + // Quotes may not occur in segments 1 or 2.
|
| + // Quotes in segment 3 are parsed and _copied_. This makes
|
| + // subformat patterns work, e.g., {1,number,'#'.##} passes
|
| + // the pattern "'#'.##" to DecimalFormat.
|
| +
|
| + UnicodeString segments[4];
|
| + int32_t part = 0; // segment we are in, 0..3
|
| + // Record the highest argument number in the pattern. (In the
|
| + // subpattern {3,number} the argument number is 3.)
|
| + int32_t formatNumber = 0;
|
| + UBool inQuote = FALSE;
|
| + int32_t braceStack = 0;
|
| + // Clear error struct
|
| + parseError.offset = -1;
|
| + parseError.preContext[0] = parseError.postContext[0] = (UChar)0;
|
| + int32_t patLen = pattern.length();
|
| + int32_t i;
|
| +
|
| + for (i=0; i<subformatCount; ++i) {
|
| + delete subformats[i].format;
|
| + }
|
| + subformatCount = 0;
|
| + argTypeCount = 0;
|
| +
|
| + for (i=0; i<patLen; ++i) {
|
| + UChar ch = pattern[i];
|
| + if (part == 0) {
|
| + // In segment 0, recognize and remove quotes
|
| + if (ch == SINGLE_QUOTE) {
|
| + if (i+1 < patLen && pattern[i+1] == SINGLE_QUOTE) {
|
| + segments[0] += ch;
|
| + ++i;
|
| + } else {
|
| + inQuote = !inQuote;
|
| + }
|
| + } else if (ch == LEFT_CURLY_BRACE && !inQuote) {
|
| + // The only way we get from segment 0 to 1 is via an
|
| + // unquoted '{'.
|
| + part = 1;
|
| + } else {
|
| + segments[0] += ch;
|
| + }
|
| + } else if (inQuote) {
|
| + // In segments 1..3, recognize quoted matter, and copy it
|
| + // into the segment, together with the quotes. This takes
|
| + // care of '' as well.
|
| + segments[part] += ch;
|
| + if (ch == SINGLE_QUOTE) {
|
| + inQuote = FALSE;
|
| + }
|
| + } else {
|
| + // We have an unquoted character in segment 1..3
|
| + switch (ch) {
|
| + case COMMA:
|
| + // Commas bump us to the next segment, except for segment 3,
|
| + // which can contain commas. See example above.
|
| + if (part < 3)
|
| + part += 1;
|
| + else
|
| + segments[3] += ch;
|
| + break;
|
| + case LEFT_CURLY_BRACE:
|
| + // Handle '{' within segment 3. The initial '{'
|
| + // before segment 1 is handled above.
|
| + if (part != 3) {
|
| + ec = U_PATTERN_SYNTAX_ERROR;
|
| + goto SYNTAX_ERROR;
|
| + }
|
| + ++braceStack;
|
| + segments[part] += ch;
|
| + break;
|
| + case RIGHT_CURLY_BRACE:
|
| + if (braceStack == 0) {
|
| + makeFormat(formatNumber, segments, parseError,ec);
|
| + if (U_FAILURE(ec)){
|
| + goto SYNTAX_ERROR;
|
| + }
|
| + formatNumber++;
|
| +
|
| + segments[1].remove();
|
| + segments[2].remove();
|
| + segments[3].remove();
|
| + part = 0;
|
| + } else {
|
| + --braceStack;
|
| + segments[part] += ch;
|
| + }
|
| + break;
|
| + case SINGLE_QUOTE:
|
| + inQuote = TRUE;
|
| + // fall through (copy quote chars in segments 1..3)
|
| + default:
|
| + segments[part] += ch;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + if (braceStack != 0 || part != 0) {
|
| + // Unmatched braces in the pattern
|
| + ec = U_UNMATCHED_BRACES;
|
| + goto SYNTAX_ERROR;
|
| + }
|
| + fPattern = segments[0];
|
| + return;
|
| +
|
| + SYNTAX_ERROR:
|
| + syntaxError(pattern, i, parseError);
|
| + for (i=0; i<subformatCount; ++i) {
|
| + delete subformats[i].format;
|
| + }
|
| + argTypeCount = subformatCount = 0;
|
| +}
|
| +// -------------------------------------
|
| +// Converts this MessageFormat instance to a pattern.
|
| +
|
| +UnicodeString&
|
| +MessageFormat::toPattern(UnicodeString& appendTo) const {
|
| + // later, make this more extensible
|
| + int32_t lastOffset = 0;
|
| + int32_t i;
|
| + for (i=0; i<subformatCount; ++i) {
|
| + copyAndFixQuotes(fPattern, lastOffset, subformats[i].offset, appendTo);
|
| + lastOffset = subformats[i].offset;
|
| + appendTo += LEFT_CURLY_BRACE;
|
| + if (isArgNumeric) {
|
| + itos(subformats[i].argNum, appendTo);
|
| + }
|
| + else {
|
| + appendTo += *subformats[i].argName;
|
| + }
|
| + Format* fmt = subformats[i].format;
|
| + DecimalFormat* decfmt;
|
| + SimpleDateFormat* sdtfmt;
|
| + ChoiceFormat* chcfmt;
|
| + PluralFormat* plfmt;
|
| + SelectFormat* selfmt;
|
| + if (fmt == NULL) {
|
| + // do nothing, string format
|
| + }
|
| + else if ((decfmt = dynamic_cast<DecimalFormat*>(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<SimpleDateFormat*>(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<ChoiceFormat*>(fmt)) != NULL) {
|
| + UnicodeString buffer;
|
| + appendTo += COMMA;
|
| + appendTo += ID_CHOICE;
|
| + appendTo += COMMA;
|
| + appendTo += ((ChoiceFormat*)fmt)->toPattern(buffer);
|
| + }
|
| + else if ((plfmt = dynamic_cast<PluralFormat*>(fmt)) != NULL) {
|
| + UnicodeString buffer;
|
| + appendTo += plfmt->toPattern(buffer);
|
| + }
|
| + else if ((selfmt = dynamic_cast<SelectFormat*>(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; i<subformatCount; ++i) {
|
| + delete subformats[i].format;
|
| + }
|
| + for (i=0; i<count; ++i) {
|
| + subformats[i].format = newFormats[i];
|
| + }
|
| + subformatCount = count;
|
| + } else {
|
| + // An adopt method must always take ownership. Delete
|
| + // the incoming format objects and return unchanged.
|
| + for (i=0; i<count; ++i) {
|
| + delete newFormats[i];
|
| + }
|
| + }
|
| +
|
| + // TODO: What about the .offset and .argNum fields?
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// 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 count) {
|
| + if (newFormats == NULL || count < 0) {
|
| + return;
|
| + }
|
| +
|
| + if (allocateSubformats(count)) {
|
| + int32_t i;
|
| + for (i=0; i<subformatCount; ++i) {
|
| + delete subformats[i].format;
|
| + }
|
| + subformatCount = 0;
|
| +
|
| + for (i=0; i<count; ++i) {
|
| + subformats[i].format = newFormats[i] ? newFormats[i]->clone() : 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<subformatCount; ++i) {
|
| + if (formatName==*subformats[i].argName) {
|
| + delete subformats[i].format;
|
| + if ( formatToAdopt== NULL) {
|
| + // This should never happen -- but we'll be nice if it does
|
| + subformats[i].format = NULL;
|
| + } else {
|
| + subformats[i].format = formatToAdopt;
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Set a single format.
|
| +// Do nothing if 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();
|
| + }
|
| + }
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// 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; i<subformatCount; ++i) {
|
| + if (formatName==*subformats[i].argName)
|
| + {
|
| + return subformats[i].format;
|
| + }
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// Set a single format by format name
|
| +// Do nothing if the variable is not less than the array count.
|
| +void
|
| +MessageFormat::setFormat(const UnicodeString& formatName,
|
| + const Format& newFormat,
|
| + UErrorCode& status) {
|
| + if (isArgNumeric) {
|
| + status = U_ARGUMENT_TYPE_MISMATCH;
|
| + return;
|
| + }
|
| + for (int32_t i=0; i<subformatCount; ++i) {
|
| + if (formatName==*subformats[i].argName)
|
| + {
|
| + delete subformats[i].format;
|
| + if (&newFormat == NULL) {
|
| + // This should never happen -- but we'll be nice if it does
|
| + subformats[i].format = NULL;
|
| + } else {
|
| + subformats[i].format = newFormat.clone();
|
| + }
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// -------------------------------------
|
| +// 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; i<subformatCount; ++i) {
|
| + t->formatAliases[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; i<subformatCount; ++i) {
|
| + fFormatNames->addElement(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<subformatCount; ++i) {
|
| + // Append the prefix of current format element.
|
| + appendTo.append(fPattern, lastOffset, subformats[i].offset - lastOffset);
|
| + lastOffset = subformats[i].offset;
|
| + obj = NULL;
|
| + if (isArgNumeric) {
|
| + argumentNumber = subformats[i].argNum;
|
| +
|
| + // Checks the scope of the argument number.
|
| + if (argumentNumber >= cnt) {
|
| + appendTo += LEFT_CURLY_BRACE;
|
| + itos(argumentNumber, appendTo);
|
| + appendTo += RIGHT_CURLY_BRACE;
|
| + continue;
|
| + }
|
| + obj = arguments+argumentNumber;
|
| + }
|
| + else {
|
| + for (int32_t j=0; j<cnt; ++j) {
|
| + if (argumentNames[j]== *subformats[i].argName ) {
|
| + obj = arguments+j;
|
| + break;
|
| + }
|
| + }
|
| + if (obj == NULL ) {
|
| + appendTo += LEFT_CURLY_BRACE;
|
| + appendTo += *subformats[i].argName;
|
| + appendTo += RIGHT_CURLY_BRACE;
|
| + continue;
|
| +
|
| + }
|
| + }
|
| + Formattable::Type type = obj->getType();
|
| +
|
| + // 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<ChoiceFormat*>(fmt) != NULL ||
|
| + dynamic_cast<PluralFormat*>(fmt) != NULL ||
|
| + dynamic_cast<SelectFormat*>(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<subformatCount) ?
|
| + subformats[i+1].offset : 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) {
|
| + 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<DecimalFormat*>(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<SimpleDateFormat*>(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<DecimalFormat*>(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; i<argName.length(); ++i) {
|
| + if(!u_hasBinaryProperty(argName.charAt(i), idContinue)) {
|
| + return FALSE;
|
| + }
|
| + }
|
| + return TRUE;
|
| +}
|
| +
|
| +int32_t
|
| +MessageFormat::getArgTypeCount() const {
|
| + return argTypeCount;
|
| +}
|
| +
|
| +FormatNameEnumeration::FormatNameEnumeration(UVector *fNameList, UErrorCode& /*status*/) {
|
| + pos=0;
|
| + fFormatNames = fNameList;
|
| +}
|
| +
|
| +const UnicodeString*
|
| +FormatNameEnumeration::snext(UErrorCode& status) {
|
| + if (U_SUCCESS(status) && pos < fFormatNames->size()) {
|
| + 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; i<fFormatNames->size(); ++i) {
|
| + if ((s=(UnicodeString *)fFormatNames->elementAt(i))!=NULL) {
|
| + delete s;
|
| + }
|
| + }
|
| + delete fFormatNames;
|
| +}
|
| +U_NAMESPACE_END
|
| +
|
| +#endif /* #if !UCONFIG_NO_FORMATTING */
|
| +
|
| +//eof
|
|
|
| Property changes on: icu46/source/i18n/msgfmt.cpp
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|