]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/msgfmt.cpp
ICU-400.40.tar.gz
[apple/icu.git] / icuSources / i18n / msgfmt.cpp
index 477a72debac68cd80edd32985150b4b7e43e3f48..359c8cedc1581a9401b6aa1ad0e829a348e7df9d 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* 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" */
 };
@@ -60,14 +69,31 @@ static const UChar ID_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" */
+};
 
 // 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[]  = {
@@ -82,13 +108,13 @@ static const UChar ID_INTEGER[]   = {
 
 // 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" */
 };
@@ -104,29 +130,29 @@ static const UChar ID_FULL[]      = {
 
 // 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)
 
 //--------------------------------------------------------------------
 
@@ -164,6 +190,69 @@ static UnicodeString& itos(int32_t i, UnicodeString& appendTo) {
     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.
 
@@ -172,12 +261,15 @@ MessageFormat::MessageFormat(const UnicodeString& 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)
 {
@@ -187,6 +279,7 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
         return;
     }
     applyPattern(pattern, success);
+    setLocaleIDs(fLocale.getName(), fLocale.getName());
 }
  
 MessageFormat::MessageFormat(const UnicodeString& pattern,
@@ -195,12 +288,15 @@ 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)
 {
@@ -210,6 +306,7 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
         return;
     }
     applyPattern(pattern, success);
+    setLocaleIDs(fLocale.getName(), fLocale.getName());
 }
 
 MessageFormat::MessageFormat(const UnicodeString& pattern,
@@ -219,12 +316,15 @@ 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)
 {
@@ -234,18 +334,22 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
         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)
 {
@@ -257,6 +361,7 @@ MessageFormat::~MessageFormat()
     int32_t idx;
     for (idx = 0; idx < subformatCount; idx++) {
         delete subformats[idx].format;
+        delete subformats[idx].argName;
     }
     uprv_free(subformats);
     subformats = NULL;
@@ -359,7 +464,7 @@ MessageFormat::operator=(const MessageFormat& that)
 
         fPattern = that.fPattern;
         setLocale(that.fLocale);
-        
+        isArgNumeric = that.isArgNumeric;
         int32_t j;
         for (j=0; j<subformatCount; ++j) {
             delete subformats[j].format;
@@ -389,9 +494,9 @@ MessageFormat::operator==(const Format& rhs) const
 
     // 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;
     }
 
@@ -401,7 +506,7 @@ MessageFormat::operator==(const Format& rhs) const
             return FALSE;
         }
     }
-
+    
     return TRUE;
 }
 
@@ -427,6 +532,7 @@ MessageFormat::setLocale(const Locale& theLocale)
         defaultDateFormat = NULL;
     }
     fLocale = theLocale;
+    setLocaleIDs(fLocale.getName(), fLocale.getName());
 }
  
 // -------------------------------------
@@ -602,7 +708,12 @@ MessageFormat::toPattern(UnicodeString& appendTo) const {
         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
@@ -722,6 +833,10 @@ MessageFormat::toPattern(UnicodeString& appendTo) const {
             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";
@@ -760,7 +875,7 @@ MessageFormat::adoptFormats(Format** newFormats,
         }
     }
 
-    // TODO: What about the .offset and .arg fields?
+    // TODO: What about the .offset and .argNum fields?
 }   
 
 // -------------------------------------
@@ -791,8 +906,8 @@ MessageFormat::setFormats(const Format** newFormats,
 }   
  
 // -------------------------------------
-// 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) {
@@ -804,9 +919,38 @@ 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) {
@@ -820,7 +964,64 @@ 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.
  
@@ -858,6 +1059,28 @@ MessageFormat::getFormats(int32_t& cnt) const
     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.
@@ -916,74 +1139,132 @@ MessageFormat::format(const Formattable& source,
     
     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.
@@ -1026,6 +1307,11 @@ MessageFormat::parse(const UnicodeString& source,
     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;
@@ -1040,7 +1326,7 @@ MessageFormat::parse(const UnicodeString& source,
         
         // 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
@@ -1067,14 +1353,19 @@ MessageFormat::parse(const UnicodeString& source,
                 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;
@@ -1082,13 +1373,13 @@ MessageFormat::parse(const UnicodeString& source,
         } 
         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
         }
@@ -1118,6 +1409,10 @@ 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.
@@ -1144,7 +1439,39 @@ MessageFormat::parseObject( const UnicodeString& source,
         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
@@ -1168,9 +1495,21 @@ MessageFormat::makeFormat(int32_t formatNumber,
 
     // 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
@@ -1179,6 +1518,8 @@ MessageFormat::makeFormat(int32_t formatNumber,
     Format *fmt = NULL;
     int32_t typeID, styleID;
     DateFormat::EStyle style;
+    UnicodeString unquotedPattern, quotedPattern;
+    UBool inQuote = FALSE;
 
     switch (typeID = findKeyword(segments[2], TYPE_IDS)) {
 
@@ -1238,6 +1579,38 @@ MessageFormat::makeFormat(int32_t formatNumber,
         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;
@@ -1261,7 +1634,14 @@ MessageFormat::makeFormat(int32_t formatNumber,
     // 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
@@ -1278,15 +1658,16 @@ int32_t MessageFormat::findKeyword(const UnicodeString& s,
                                    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;
 }
@@ -1339,7 +1720,7 @@ MessageFormat::copyAndFixQuotes(const UnicodeString& source,
 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);
@@ -1388,6 +1769,56 @@ const DateFormat* MessageFormat::getDefaultDateFormat(UErrorCode& ec) const {
     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 */