/*
*******************************************************************************
-* Copyright (C) 1997-2003, International Business Machines Corporation and *
+* Copyright (C) 2007-2008, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
#include "unicode/datefmt.h"
#include "unicode/smpdtfmt.h"
#include "unicode/choicfmt.h"
+#include "unicode/plurfmt.h"
#include "unicode/ustring.h"
#include "unicode/ucnv_err.h"
#include "unicode/uchar.h"
-#include "ustrfmt.h"
+#include "unicode/umsg.h"
+#include "unicode/rbnf.h"
#include "cmemory.h"
-#include "uprops.h"
+#include "msgfmt_impl.h"
+#include "util.h"
#include "uassert.h"
+#include "ustrfmt.h"
+#include "uvector.h"
// *****************************************************************************
// class MessageFormat
//---------------------------------------
// 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_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" */
+};
// MessageFormat Type List Number, Date, Time or Choice
static const UChar * const TYPE_IDS[] = {
- NULL,
+ ID_EMPTY,
ID_NUMBER,
ID_DATE,
ID_TIME,
- ID_CHOICE
+ ID_CHOICE,
+ ID_SPELLOUT,
+ ID_ORDINAL,
+ ID_DURATION,
+ ID_PLURAL,
+ NULL,
};
static const UChar ID_CURRENCY[] = {
// NumberFormat modifier list, default, currency, percent or integer
static const UChar * const NUMBER_STYLE_IDS[] = {
- NULL,
+ ID_EMPTY,
ID_CURRENCY,
ID_PERCENT,
ID_INTEGER,
NULL,
};
-
+
static const UChar ID_SHORT[] = {
0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */
};
// DateFormat modifier list, default, short, medium, long or full
static const UChar * const DATE_STYLE_IDS[] = {
- NULL,
+ ID_EMPTY,
ID_SHORT,
ID_MEDIUM,
ID_LONG,
- ID_FULL
+ ID_FULL,
+ NULL,
};
-static const DateFormat::EStyle DATE_STYLES[] = {
- DateFormat::kDefault,
- DateFormat::kShort,
- DateFormat::kMedium,
- DateFormat::kLong,
- DateFormat::kFull,
+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 ID_LIST_LENGTH = 5;
-
static const int32_t DEFAULT_INITIAL_CAPACITY = 10;
U_NAMESPACE_BEGIN
// -------------------------------------
-const char MessageFormat::fgClassID = 0; // Value is irrelevant
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MessageFormat)
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatNameEnumeration)
//--------------------------------------------------------------------
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.
: 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)
{
return;
}
applyPattern(pattern, success);
+ setLocaleIDs(fLocale.getName(), fLocale.getName());
}
MessageFormat::MessageFormat(const UnicodeString& pattern,
: 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)
{
return;
}
applyPattern(pattern, success);
+ setLocaleIDs(fLocale.getName(), fLocale.getName());
}
MessageFormat::MessageFormat(const UnicodeString& pattern,
: 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)
{
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)
{
int32_t idx;
for (idx = 0; idx < subformatCount; idx++) {
delete subformats[idx].format;
+ delete subformats[idx].argName;
}
uprv_free(subformats);
subformats = NULL;
fPattern = that.fPattern;
setLocale(that.fLocale);
-
+ isArgNumeric = that.isArgNumeric;
int32_t j;
for (j=0; j<subformatCount; ++j) {
delete subformats[j].format;
// Check class ID before checking MessageFormat members
if (!Format::operator==(rhs) ||
- getDynamicClassID() != that.getDynamicClassID() ||
fPattern != that.fPattern ||
- fLocale != that.fLocale) {
+ fLocale != that.fLocale ||
+ isArgNumeric != that.isArgNumeric) {
return FALSE;
}
return FALSE;
}
}
-
+
return TRUE;
}
defaultDateFormat = NULL;
}
fLocale = theLocale;
+ setLocaleIDs(fLocale.getName(), fLocale.getName());
}
// -------------------------------------
copyAndFixQuotes(fPattern, lastOffset, subformats[i].offset, appendTo);
lastOffset = subformats[i].offset;
appendTo += LEFT_CURLY_BRACE;
- itos(subformats[i].arg, appendTo);
+ if (isArgNumeric) {
+ itos(subformats[i].argNum, appendTo);
+ }
+ else {
+ appendTo += *subformats[i].argName;
+ }
Format* fmt = subformats[i].format;
if (fmt == NULL) {
// do nothing, string format
appendTo += ID_CHOICE;
appendTo += COMMA;
appendTo += ((ChoiceFormat*)fmt)->toPattern(buffer);
+ }
+ else if (fmt->getDynamicClassID() == PluralFormat::getStaticClassID()) {
+ UnicodeString buffer;
+ appendTo += ((PluralFormat*)fmt)->toPattern(buffer);
}
else {
//appendTo += ", unknown";
}
}
- // TODO: What about the .offset and .arg fields?
+ // TODO: What about the .offset and .argNum fields?
}
// -------------------------------------
}
// -------------------------------------
-// Adopt a single format.
-// Do nothing is the format number is not less than the array count.
+// 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) {
}
}
+// -------------------------------------
+// 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 is the variable is not less than the array count.
+// Do nothing if the variable is not less than the array count.
void
MessageFormat::setFormat(int32_t n, const Format& newFormat) {
}
}
}
-
+
+// -------------------------------------
+// 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.
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.
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,
+MessageFormat::format(const Formattable* arguments,
+ const UnicodeString *argumentNames,
int32_t cnt,
UnicodeString& appendTo,
FieldPosition& status,
int32_t recursionProtection,
UErrorCode& success) const
-{
- // Allow NULL array only if cnt == 0
+{
+ int32_t lastOffset = 0;
+ int32_t argumentNumber=0;
if (cnt < 0 || (cnt && arguments == NULL)) {
success = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
-
- int32_t lastOffset = 0;
+
+ 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;
- int32_t argumentNumber = subformats[i].arg;
- // Checks the scope of the argument number.
- if (argumentNumber >= cnt) {
- appendTo += LEFT_CURLY_BRACE;
- itos(argumentNumber, appendTo);
- appendTo += RIGHT_CURLY_BRACE;
- continue;
+ 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;
+
+ }
}
-
- const Formattable *obj = arguments + argumentNumber;
Formattable::Type type = obj->getType();
// Recursively calling the format process only if the current
// format argument refers to a ChoiceFormat object.
Format* fmt = subformats[i].format;
if (fmt != NULL) {
- UnicodeString arg;
- fmt->format(*obj, arg, success);
+ UnicodeString argNum;
+ fmt->format(*obj, argNum, success);
// Needs to reprocess the ChoiceFormat option by using the
// MessageFormat pattern application.
- if (fmt->getDynamicClassID() == ChoiceFormat::getStaticClassID() &&
- arg.indexOf(LEFT_CURLY_BRACE) >= 0) {
- MessageFormat temp(arg, fLocale, success);
+ if ((fmt->getDynamicClassID() == ChoiceFormat::getStaticClassID() ||
+ fmt->getDynamicClassID() == PluralFormat::getStaticClassID()) &&
+ argNum.indexOf(LEFT_CURLY_BRACE) >= 0) {
+ MessageFormat temp(argNum, fLocale, success);
// TODO: Implement recursion protection
- temp.format(arguments, cnt, appendTo, status, recursionProtection, success);
+ 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 += arg;
+ appendTo += argNum;
}
}
// If the obj data type is a number, use a NumberFormat instance.
- else if ((type == Formattable::kDouble) || (type == Formattable::kLong)) {
+ 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 {
+ } 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.
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;
// now use format
Format* fmt = subformats[i].format;
- int32_t arg = subformats[i].arg;
+ 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
UnicodeString strValue = buffer;
UnicodeString temp(LEFT_CURLY_BRACE);
// {sfb} check this later
- itos(arg, temp);
+ if (isArgNumeric) {
+ itos(argNum, temp);
+ }
+ else {
+ temp+=(*subformats[i].argName);
+ }
temp += RIGHT_CURLY_BRACE;
if (strValue != temp) {
source.extract(sourceOffset,next - sourceOffset, buffer);
- resultArray[arg].setString(buffer);
+ resultArray[argNum].setString(buffer);
// {sfb} not sure about this
- if ((arg + 1) > count) {
- count = arg + 1;
+ if ((argNum + 1) > count) {
+ count = argNum + 1;
}
}
sourceOffset = next;
}
else {
tempPos.setIndex(sourceOffset);
- fmt->parseObject(source, resultArray[arg], tempPos);
+ fmt->parseObject(source, resultArray[argNum], tempPos);
if (tempPos.getIndex() == sourceOffset) {
goto PARSE_ERROR;
}
- if ((arg + 1) > count) {
- count = arg + 1;
+ if ((argNum + 1) > count) {
+ count = argNum + 1;
}
sourceOffset = tempPos.getIndex(); // update
}
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.
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
// Parse the argument number
int32_t argumentNumber = stou(segments[1]); // always unlocalized!
+ UnicodeString argumentName;
if (argumentNumber < 0) {
- ec = U_INVALID_FORMAT_ERROR;
- return;
+ 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
Format *fmt = NULL;
int32_t typeID, styleID;
DateFormat::EStyle style;
+ UnicodeString unquotedPattern, quotedPattern;
+ UBool inQuote = FALSE;
switch (typeID = findKeyword(segments[2], TYPE_IDS)) {
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
+ argType = Formattable::kDouble;
+ 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;
+ }
+ }
+ fmt = new PluralFormat(fLocale, unquotedPattern, ec);
+ break;
default:
argType = Formattable::kString;
ec = U_ILLEGAL_ARGUMENT_ERROR;
// Parse succeeded; record results in our arrays
subformats[formatNumber].format = fmt;
subformats[formatNumber].offset = segments[0].length();
- subformats[formatNumber].arg = argumentNumber;
+ 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
const UChar * const *list)
{
if (s.length() == 0)
- return 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; i < ID_LIST_LENGTH; ++i) {
- if (list[i] && !buffer.compare(list[i], u_strlen(list[i])))
+ buffer.trim().toLower("");
+ for (int32_t i = 0; list[i]; ++i) {
+ if (!buffer.compare(list[i], u_strlen(list[i]))) {
return i;
+ }
}
return -1;
}
NumberFormat*
MessageFormat::createIntegerFormat(const Locale& locale, UErrorCode& status) const {
NumberFormat *temp = NumberFormat::createInstance(locale, status);
- if (temp->getDynamicClassID() == DecimalFormat::getStaticClassID()) {
+ if (temp != NULL && temp->getDynamicClassID() == DecimalFormat::getStaticClassID()) {
DecimalFormat *temp2 = (DecimalFormat*) temp;
temp2->setMaximumFractionDigits(0);
temp2->setDecimalSeparatorAlwaysShown(FALSE);
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;
+}
+
+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 */