summaryrefslogtreecommitdiff
path: root/Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp')
-rw-r--r--Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp1858
1 files changed, 1858 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp b/Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp
new file mode 100644
index 00000000000..b529246683e
--- /dev/null
+++ b/Build/source/libs/icu/icu-4.6/i18n/msgfmt.cpp
@@ -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