]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/rbnf.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / rbnf.cpp
index c53a7b0c16924e1515cc150ac94a93d67aa903e6..751070d020e119289bf2646539d87f6d8499df86 100644 (file)
@@ -1,14 +1,21 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
 /*
 *******************************************************************************
-* Copyright (C) 1997-2003, International Business Machines Corporation and others. All Rights Reserved.
+* Copyright (C) 1997-2015, International Business Machines Corporation
+* and others. All Rights Reserved.
 *******************************************************************************
 */
 
 *******************************************************************************
 */
 
+#include "unicode/utypes.h"
+#include "utypeinfo.h"  // for 'typeid' to work
+
 #include "unicode/rbnf.h"
 
 #if U_HAVE_RBNF
 
 #include "unicode/normlzr.h"
 #include "unicode/rbnf.h"
 
 #if U_HAVE_RBNF
 
 #include "unicode/normlzr.h"
+#include "unicode/plurfmt.h"
 #include "unicode/tblcoll.h"
 #include "unicode/uchar.h"
 #include "unicode/ucol.h"
 #include "unicode/tblcoll.h"
 #include "unicode/uchar.h"
 #include "unicode/ucol.h"
 #include "unicode/ures.h"
 #include "unicode/ustring.h"
 #include "unicode/utf16.h"
 #include "unicode/ures.h"
 #include "unicode/ustring.h"
 #include "unicode/utf16.h"
-
-#include "nfrs.h"
+#include "unicode/udata.h"
+#include "unicode/udisplaycontext.h"
+#include "unicode/brkiter.h"
+#include "unicode/ucasemap.h"
 
 #include "cmemory.h"
 #include "cstring.h"
 
 #include "cmemory.h"
 #include "cstring.h"
-#include "uprops.h"
+#include "patternprops.h"
+#include "uresimp.h"
+#include "nfrs.h"
+#include "number_decimalquantity.h"
+
+// debugging
+// #define RBNF_DEBUG
+
+#ifdef RBNF_DEBUG
+#include <stdio.h>
+#endif
+
+#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
 
 static const UChar gPercentPercent[] =
 {
 
 static const UChar gPercentPercent[] =
 {
@@ -45,80 +66,856 @@ static const UChar gSemiPercent[] =
 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
 
 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
 
-const char RuleBasedNumberFormat::fgClassID = 0;
+U_NAMESPACE_BEGIN
+
+using number::impl::DecimalQuantity;
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
+
+/*
+This is a utility class. It does not use ICU's RTTI.
+If ICU's RTTI is needed again, you can uncomment the RTTI code and derive from UObject.
+Please make sure that intltest passes on Windows in Release mode,
+since the string pooling per compilation unit will mess up how RTTI works.
+The RTTI code was also removed due to lack of code coverage.
+*/
+class LocalizationInfo : public UMemory {
+protected:
+    virtual ~LocalizationInfo();
+    uint32_t refcount;
+    
+public:
+    LocalizationInfo() : refcount(0) {}
+    
+    LocalizationInfo* ref(void) {
+        ++refcount;
+        return this;
+    }
+    
+    LocalizationInfo* unref(void) {
+        if (refcount && --refcount == 0) {
+            delete this;
+        }
+        return NULL;
+    }
+    
+    virtual UBool operator==(const LocalizationInfo* rhs) const;
+    inline  UBool operator!=(const LocalizationInfo* rhs) const { return !operator==(rhs); }
+    
+    virtual int32_t getNumberOfRuleSets(void) const = 0;
+    virtual const UChar* getRuleSetName(int32_t index) const = 0;
+    virtual int32_t getNumberOfDisplayLocales(void) const = 0;
+    virtual const UChar* getLocaleName(int32_t index) const = 0;
+    virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const = 0;
+    
+    virtual int32_t indexForLocale(const UChar* locale) const;
+    virtual int32_t indexForRuleSet(const UChar* ruleset) const;
+    
+//    virtual UClassID getDynamicClassID() const = 0;
+//    static UClassID getStaticClassID(void);
+};
+
+LocalizationInfo::~LocalizationInfo() {}
+
+//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
+
+// if both strings are NULL, this returns TRUE
+static UBool 
+streq(const UChar* lhs, const UChar* rhs) {
+    if (rhs == lhs) {
+        return TRUE;
+    }
+    if (lhs && rhs) {
+        return u_strcmp(lhs, rhs) == 0;
+    }
+    return FALSE;
+}
+
+UBool
+LocalizationInfo::operator==(const LocalizationInfo* rhs) const {
+    if (rhs) {
+        if (this == rhs) {
+            return TRUE;
+        }
+        
+        int32_t rsc = getNumberOfRuleSets();
+        if (rsc == rhs->getNumberOfRuleSets()) {
+            for (int i = 0; i < rsc; ++i) {
+                if (!streq(getRuleSetName(i), rhs->getRuleSetName(i))) {
+                    return FALSE;
+                }
+            }
+            int32_t dlc = getNumberOfDisplayLocales();
+            if (dlc == rhs->getNumberOfDisplayLocales()) {
+                for (int i = 0; i < dlc; ++i) {
+                    const UChar* locale = getLocaleName(i);
+                    int32_t ix = rhs->indexForLocale(locale);
+                    // if no locale, ix is -1, getLocaleName returns null, so streq returns false
+                    if (!streq(locale, rhs->getLocaleName(ix))) {
+                        return FALSE;
+                    }
+                    for (int j = 0; j < rsc; ++j) {
+                        if (!streq(getDisplayName(i, j), rhs->getDisplayName(ix, j))) {
+                            return FALSE;
+                        }
+                    }
+                }
+                return TRUE;
+            }
+        }
+    }
+    return FALSE;
+}
+
+int32_t
+LocalizationInfo::indexForLocale(const UChar* locale) const {
+    for (int i = 0; i < getNumberOfDisplayLocales(); ++i) {
+        if (streq(locale, getLocaleName(i))) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int32_t
+LocalizationInfo::indexForRuleSet(const UChar* ruleset) const {
+    if (ruleset) {
+        for (int i = 0; i < getNumberOfRuleSets(); ++i) {
+            if (streq(ruleset, getRuleSetName(i))) {
+                return i;
+            }
+        }
+    }
+    return -1;
+}
+
+
+typedef void (*Fn_Deleter)(void*);
+
+class VArray {
+    void** buf;
+    int32_t cap;
+    int32_t size;
+    Fn_Deleter deleter;
+public:
+    VArray() : buf(NULL), cap(0), size(0), deleter(NULL) {}
+    
+    VArray(Fn_Deleter del) : buf(NULL), cap(0), size(0), deleter(del) {}
+    
+    ~VArray() {
+        if (deleter) {
+            for (int i = 0; i < size; ++i) {
+                (*deleter)(buf[i]);
+            }
+        }
+        uprv_free(buf); 
+    }
+    
+    int32_t length() {
+        return size;
+    }
+    
+    void add(void* elem, UErrorCode& status) {
+        if (U_SUCCESS(status)) {
+            if (size == cap) {
+                if (cap == 0) {
+                    cap = 1;
+                } else if (cap < 256) {
+                    cap *= 2;
+                } else {
+                    cap += 256;
+                }
+                if (buf == NULL) {
+                    buf = (void**)uprv_malloc(cap * sizeof(void*));
+                } else {
+                    buf = (void**)uprv_realloc(buf, cap * sizeof(void*));
+                }
+                if (buf == NULL) {
+                    // if we couldn't realloc, we leak the memory we've already allocated, but we're in deep trouble anyway
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    return;
+                }
+                void* start = &buf[size];
+                size_t count = (cap - size) * sizeof(void*);
+                uprv_memset(start, 0, count); // fill with nulls, just because
+            }
+            buf[size++] = elem;
+        }
+    }
+    
+    void** release(void) {
+        void** result = buf;
+        buf = NULL;
+        cap = 0;
+        size = 0;
+        return result;
+    }
+};
+
+class LocDataParser;
+
+class StringLocalizationInfo : public LocalizationInfo {
+    UChar* info;
+    UChar*** data;
+    int32_t numRuleSets;
+    int32_t numLocales;
+
+friend class LocDataParser;
+
+    StringLocalizationInfo(UChar* i, UChar*** d, int32_t numRS, int32_t numLocs)
+        : info(i), data(d), numRuleSets(numRS), numLocales(numLocs)
+    {
+    }
+    
+public:
+    static StringLocalizationInfo* create(const UnicodeString& info, UParseError& perror, UErrorCode& status);
+    
+    virtual ~StringLocalizationInfo();
+    virtual int32_t getNumberOfRuleSets(void) const { return numRuleSets; }
+    virtual const UChar* getRuleSetName(int32_t index) const;
+    virtual int32_t getNumberOfDisplayLocales(void) const { return numLocales; }
+    virtual const UChar* getLocaleName(int32_t index) const;
+    virtual const UChar* getDisplayName(int32_t localeIndex, int32_t ruleIndex) const;
+    
+//    virtual UClassID getDynamicClassID() const;
+//    static UClassID getStaticClassID(void);
+    
+private:
+    void init(UErrorCode& status) const;
+};
+
+
+enum {
+    OPEN_ANGLE = 0x003c, /* '<' */
+    CLOSE_ANGLE = 0x003e, /* '>' */
+    COMMA = 0x002c,
+    TICK = 0x0027,
+    QUOTE = 0x0022,
+    SPACE = 0x0020
+};
+
+/**
+ * Utility for parsing a localization string and returning a StringLocalizationInfo*.
+ */
+class LocDataParser {
+    UChar* data;
+    const UChar* e;
+    UChar* p;
+    UChar ch;
+    UParseError& pe;
+    UErrorCode& ec;
+    
+public:
+    LocDataParser(UParseError& parseError, UErrorCode& status) 
+        : data(NULL), e(NULL), p(NULL), ch(0xffff), pe(parseError), ec(status) {}
+    ~LocDataParser() {}
+    
+    /*
+    * On a successful parse, return a StringLocalizationInfo*, otherwise delete locData, set perror and status,
+    * and return NULL.  The StringLocalizationInfo will adopt locData if it is created.
+    */
+    StringLocalizationInfo* parse(UChar* data, int32_t len);
+    
+private:
+    
+    inline void inc(void) {
+        ++p;
+        ch = 0xffff;
+    }
+    inline UBool checkInc(UChar c) {
+        if (p < e && (ch == c || *p == c)) {
+            inc();
+            return TRUE;
+        }
+        return FALSE;
+    }
+    inline UBool check(UChar c) {
+        return p < e && (ch == c || *p == c);
+    }
+    inline void skipWhitespace(void) {
+        while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) {
+            inc();
+        }
+    }
+    inline UBool inList(UChar c, const UChar* list) const {
+        if (*list == SPACE && PatternProps::isWhiteSpace(c)) {
+            return TRUE;
+        }
+        while (*list && *list != c) {
+            ++list;
+        }
+        return *list == c;
+    }
+    void parseError(const char* msg);
+    
+    StringLocalizationInfo* doParse(void);
+        
+    UChar** nextArray(int32_t& requiredLength);
+    UChar*  nextString(void);
+};
+
+#ifdef RBNF_DEBUG
+#define ERROR(msg) parseError(msg); return NULL;
+#define EXPLANATION_ARG explanationArg
+#else
+#define ERROR(msg) parseError(NULL); return NULL;
+#define EXPLANATION_ARG
+#endif
+        
+
+static const UChar DQUOTE_STOPLIST[] = { 
+    QUOTE, 0
+};
+
+static const UChar SQUOTE_STOPLIST[] = { 
+    TICK, 0
+};
+
+static const UChar NOQUOTE_STOPLIST[] = { 
+    SPACE, COMMA, CLOSE_ANGLE, OPEN_ANGLE, TICK, QUOTE, 0
+};
+
+static void
+DeleteFn(void* p) {
+  uprv_free(p);
+}
+
+StringLocalizationInfo*
+LocDataParser::parse(UChar* _data, int32_t len) {
+    if (U_FAILURE(ec)) {
+        if (_data) uprv_free(_data);
+        return NULL;
+    }
+
+    pe.line = 0;
+    pe.offset = -1;
+    pe.postContext[0] = 0;
+    pe.preContext[0] = 0;
+
+    if (_data == NULL) {
+        ec = U_ILLEGAL_ARGUMENT_ERROR;
+        return NULL;
+    }
+
+    if (len <= 0) {
+        ec = U_ILLEGAL_ARGUMENT_ERROR;
+        uprv_free(_data);
+        return NULL;
+    }
+
+    data = _data;
+    e = data + len;
+    p = _data;
+    ch = 0xffff;
+
+    return doParse();
+}
+
+
+StringLocalizationInfo*
+LocDataParser::doParse(void) {
+    skipWhitespace();
+    if (!checkInc(OPEN_ANGLE)) {
+        ERROR("Missing open angle");
+    } else {
+        VArray array(DeleteFn);
+        UBool mightHaveNext = TRUE;
+        int32_t requiredLength = -1;
+        while (mightHaveNext) {
+            mightHaveNext = FALSE;
+            UChar** elem = nextArray(requiredLength);
+            skipWhitespace();
+            UBool haveComma = check(COMMA);
+            if (elem) {
+                array.add(elem, ec);
+                if (haveComma) {
+                    inc();
+                    mightHaveNext = TRUE;
+                }
+            } else if (haveComma) {
+                ERROR("Unexpected character");
+            }
+        }
+
+        skipWhitespace();
+        if (!checkInc(CLOSE_ANGLE)) {
+            if (check(OPEN_ANGLE)) {
+                ERROR("Missing comma in outer array");
+            } else {
+                ERROR("Missing close angle bracket in outer array");
+            }
+        }
+
+        skipWhitespace();
+        if (p != e) {
+            ERROR("Extra text after close of localization data");
+        }
+
+        array.add(NULL, ec);
+        if (U_SUCCESS(ec)) {
+            int32_t numLocs = array.length() - 2; // subtract first, NULL
+            UChar*** result = (UChar***)array.release();
+            
+            return new StringLocalizationInfo(data, result, requiredLength-2, numLocs); // subtract first, NULL
+        }
+    }
+  
+    ERROR("Unknown error");
+}
+
+UChar**
+LocDataParser::nextArray(int32_t& requiredLength) {
+    if (U_FAILURE(ec)) {
+        return NULL;
+    }
+    
+    skipWhitespace();
+    if (!checkInc(OPEN_ANGLE)) {
+        ERROR("Missing open angle");
+    }
+
+    VArray array;
+    UBool mightHaveNext = TRUE;
+    while (mightHaveNext) {
+        mightHaveNext = FALSE;
+        UChar* elem = nextString();
+        skipWhitespace();
+        UBool haveComma = check(COMMA);
+        if (elem) {
+            array.add(elem, ec);
+            if (haveComma) {
+                inc();
+                mightHaveNext = TRUE;
+            }
+        } else if (haveComma) {
+            ERROR("Unexpected comma");
+        }
+    }
+    skipWhitespace();
+    if (!checkInc(CLOSE_ANGLE)) {
+        if (check(OPEN_ANGLE)) {
+            ERROR("Missing close angle bracket in inner array");
+        } else {
+            ERROR("Missing comma in inner array");
+        }
+    }
+
+    array.add(NULL, ec);
+    if (U_SUCCESS(ec)) {
+        if (requiredLength == -1) {
+            requiredLength = array.length() + 1;
+        } else if (array.length() != requiredLength) {
+            ec = U_ILLEGAL_ARGUMENT_ERROR;
+            ERROR("Array not of required length");
+        }
+        
+        return (UChar**)array.release();
+    }
+    ERROR("Unknown Error");
+}
+
+UChar*
+LocDataParser::nextString() {
+    UChar* result = NULL;
+    
+    skipWhitespace();
+    if (p < e) {
+        const UChar* terminators;
+        UChar c = *p;
+        UBool haveQuote = c == QUOTE || c == TICK;
+        if (haveQuote) {
+            inc();
+            terminators = c == QUOTE ? DQUOTE_STOPLIST : SQUOTE_STOPLIST;
+        } else {
+            terminators = NOQUOTE_STOPLIST;
+        }
+        UChar* start = p;
+        while (p < e && !inList(*p, terminators)) ++p;
+        if (p == e) {
+            ERROR("Unexpected end of data");
+        }
+        
+        UChar x = *p;
+        if (p > start) {
+            ch = x;
+            *p = 0x0; // terminate by writing to data
+            result = start; // just point into data
+        }
+        if (haveQuote) {
+            if (x != c) {
+                ERROR("Missing matching quote");
+            } else if (p == start) {
+                ERROR("Empty string");
+            }
+            inc();
+        } else if (x == OPEN_ANGLE || x == TICK || x == QUOTE) {
+            ERROR("Unexpected character in string");
+        }
+    }
+
+    // ok for there to be no next string
+    return result;
+}
+
+void LocDataParser::parseError(const char* EXPLANATION_ARG)
+{
+    if (!data) {
+        return;
+    }
+
+    const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;
+    if (start < data) {
+        start = data;
+    }
+    for (UChar* x = p; --x >= start;) {
+        if (!*x) {
+            start = x+1;
+            break;
+        }
+    }
+    const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;
+    if (limit > e) {
+        limit = e;
+    }
+    u_strncpy(pe.preContext, start, (int32_t)(p-start));
+    pe.preContext[p-start] = 0;
+    u_strncpy(pe.postContext, p, (int32_t)(limit-p));
+    pe.postContext[limit-p] = 0;
+    pe.offset = (int32_t)(p - data);
+    
+#ifdef RBNF_DEBUG
+    fprintf(stderr, "%s at or near character %ld: ", EXPLANATION_ARG, p-data);
+
+    UnicodeString msg;
+    msg.append(start, p - start);
+    msg.append((UChar)0x002f); /* SOLIDUS/SLASH */
+    msg.append(p, limit-p);
+    msg.append(UNICODE_STRING_SIMPLE("'"));
+    
+    char buf[128];
+    int32_t len = msg.extract(0, msg.length(), buf, 128);
+    if (len >= 128) {
+        buf[127] = 0;
+    } else {
+        buf[len] = 0;
+    }
+    fprintf(stderr, "%s\n", buf);
+    fflush(stderr);
+#endif
+    
+    uprv_free(data);
+    data = NULL;
+    p = NULL;
+    e = NULL;
+    
+    if (U_SUCCESS(ec)) {
+        ec = U_PARSE_ERROR;
+    }
+}
+
+//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
+
+StringLocalizationInfo* 
+StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    
+    int32_t len = info.length();
+    if (len == 0) {
+        return NULL; // no error;
+    }
+    
+    UChar* p = (UChar*)uprv_malloc(len * sizeof(UChar));
+    if (!p) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+    info.extract(p, len, status);
+    if (!U_FAILURE(status)) {
+        status = U_ZERO_ERROR; // clear warning about non-termination
+    }
+    
+    LocDataParser parser(perror, status);
+    return parser.parse(p, len);
+}
+
+StringLocalizationInfo::~StringLocalizationInfo() {
+    for (UChar*** p = (UChar***)data; *p; ++p) {
+        // remaining data is simply pointer into our unicode string data.
+        if (*p) uprv_free(*p);
+    }
+    if (data) uprv_free(data);
+    if (info) uprv_free(info);
+}
+
+
+const UChar*
+StringLocalizationInfo::getRuleSetName(int32_t index) const {
+    if (index >= 0 && index < getNumberOfRuleSets()) {
+        return data[0][index];
+    }
+    return NULL;
+}
+
+const UChar*
+StringLocalizationInfo::getLocaleName(int32_t index) const {
+    if (index >= 0 && index < getNumberOfDisplayLocales()) {
+        return data[index+1][0];
+    }
+    return NULL;
+}
+
+const UChar*
+StringLocalizationInfo::getDisplayName(int32_t localeIndex, int32_t ruleIndex) const {
+    if (localeIndex >= 0 && localeIndex < getNumberOfDisplayLocales() &&
+        ruleIndex >= 0 && ruleIndex < getNumberOfRuleSets()) {
+        return data[localeIndex+1][ruleIndex+1];
+    }
+    return NULL;
+}
+
+// ----------
+
+RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
+                                             const UnicodeString& locs,
+                                             const Locale& alocale, UParseError& perror, UErrorCode& status)
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
+  , defaultRuleSet(NULL)
+  , locale(alocale)
+  , collator(NULL)
+  , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
+  , lenient(FALSE)
+  , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
+{
+  LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
+  init(description, locinfo, perror, status);
+}
+
+RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
+                                             const UnicodeString& locs,
+                                             UParseError& perror, UErrorCode& status)
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
+  , defaultRuleSet(NULL)
+  , locale(Locale::getDefault())
+  , collator(NULL)
+  , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
+  , lenient(FALSE)
+  , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
+{
+  LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
+  init(description, locinfo, perror, status);
+}
 
 
-RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, const Locale& alocale, UParseError& perror, UErrorCode& status)
-  : ruleSets(NULL)
+RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
+                                             LocalizationInfo* info,
+                                             const Locale& alocale, UParseError& perror, UErrorCode& status)
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , lenient(FALSE)
   , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
 {
-    init(description, perror, status);
+  init(description, info, perror, status);
+}
+
+RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
+                         UParseError& perror, 
+                         UErrorCode& status) 
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
+  , defaultRuleSet(NULL)
+  , locale(Locale::getDefault())
+  , collator(NULL)
+  , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
+  , lenient(FALSE)
+  , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
+{
+    init(description, NULL, perror, status);
+}
+
+RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description, 
+                         const Locale& aLocale,
+                         UParseError& perror, 
+                         UErrorCode& status) 
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
+  , defaultRuleSet(NULL)
+  , locale(aLocale)
+  , collator(NULL)
+  , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
+  , lenient(FALSE)
+  , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
+{
+    init(description, NULL, perror, status);
 }
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
 }
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
-  : ruleSets(NULL)
+  : fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , lenient(FALSE)
   , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     if (U_FAILURE(status)) {
         return;
     }
 
 {
     if (U_FAILURE(status)) {
         return;
     }
 
+    const char* rules_tag = "RBNFRules";
     const char* fmt_tag = "";
     switch (tag) {
     case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break;
     case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break;
     case URBNF_DURATION: fmt_tag = "DurationRules"; break;
     const char* fmt_tag = "";
     switch (tag) {
     case URBNF_SPELLOUT: fmt_tag = "SpelloutRules"; break;
     case URBNF_ORDINAL: fmt_tag = "OrdinalRules"; break;
     case URBNF_DURATION: fmt_tag = "DurationRules"; break;
+    case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;
     default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
     }
 
     default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
     }
 
-    // the following didn't work for aliased resources, but Vladimir supposedly fixed it...
-    // const UChar* description = ures_getStringByKey(nfrb, fmt_tag, &len, &status);
-    int32_t len = 0;
-    UResourceBundle* nfrb = ures_open(NULL, locale.getName(), &status);
-    //    UResourceBundle* yuck = ures_getByKey(nfrb, fmt_tag, NULL, &status);
-    //    const UChar* description = ures_getString(yuck, &len, &status);
-    const UChar* description = ures_getStringByKey(nfrb, fmt_tag, &len, &status);
+    // TODO: read localization info from resource
+    LocalizationInfo* locinfo = NULL;
+
+    UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);
     if (U_SUCCESS(status)) {
     if (U_SUCCESS(status)) {
-        UnicodeString desc(description, len);
+        setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),
+                     ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));
+
+        UResourceBundle* rbnfRules = ures_getByKeyWithFallback(nfrb, rules_tag, NULL, &status);
+        if (U_FAILURE(status)) {
+            ures_close(nfrb);
+        }
+        UResourceBundle* ruleSets = ures_getByKeyWithFallback(rbnfRules, fmt_tag, NULL, &status);
+        if (U_FAILURE(status)) {
+            ures_close(rbnfRules);
+            ures_close(nfrb);
+            return;
+        }
+
+        UnicodeString desc;
+        while (ures_hasNext(ruleSets)) {
+           desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));
+        }
         UParseError perror;
         UParseError perror;
-        init (desc, perror, status);
+
+        init(desc, locinfo, perror, status);
+
+        ures_close(ruleSets);
+        ures_close(rbnfRules);
     }
     }
-    //    ures_close(yuck);
     ures_close(nfrb);
 }
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
   : NumberFormat(rhs)
     ures_close(nfrb);
 }
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
   : NumberFormat(rhs)
-  , ruleSets(NULL)
+  , fRuleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(rhs.locale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
   , defaultRuleSet(NULL)
   , locale(rhs.locale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , fRoundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , lenient(FALSE)
   , lenientParseRules(NULL)
+  , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     this->operator=(rhs);
 }
 
 {
     this->operator=(rhs);
 }
 
+// --------
+
 RuleBasedNumberFormat&
 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
 {
 RuleBasedNumberFormat&
 RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
 {
+    if (this == &rhs) {
+        return *this;
+    }
+    NumberFormat::operator=(rhs);
     UErrorCode status = U_ZERO_ERROR;
     dispose();
     locale = rhs.locale;
     UErrorCode status = U_ZERO_ERROR;
     dispose();
     locale = rhs.locale;
-    UnicodeString rules = rhs.getRules();
-    UParseError perror;
-    init(rules, perror, status);
     lenient = rhs.lenient;
     lenient = rhs.lenient;
+
+    UParseError perror;
+    setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
+    init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
+    setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
+    setRoundingMode(rhs.getRoundingMode());
+
+    capitalizationInfoSet = rhs.capitalizationInfoSet;
+    capitalizationForUIListMenu = rhs.capitalizationForUIListMenu;
+    capitalizationForStandAlone = rhs.capitalizationForStandAlone;
+#if !UCONFIG_NO_BREAK_ITERATION
+    capitalizationBrkIter = (rhs.capitalizationBrkIter!=NULL)? rhs.capitalizationBrkIter->clone(): NULL;
+#endif
+
     return *this;
 }
 
     return *this;
 }
 
@@ -130,23 +927,7 @@ RuleBasedNumberFormat::~RuleBasedNumberFormat()
 Format*
 RuleBasedNumberFormat::clone(void) const
 {
 Format*
 RuleBasedNumberFormat::clone(void) const
 {
-    RuleBasedNumberFormat * result = NULL;
-    UnicodeString rules = getRules();
-    UErrorCode status = U_ZERO_ERROR;
-    UParseError perror;
-    result = new RuleBasedNumberFormat(rules, locale, perror, status);
-    /* test for NULL */
-    if (result == 0) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        return 0;
-    }
-    if (U_FAILURE(status)) {
-        delete result;
-        result = 0;
-    } else {
-        result->lenient = lenient;
-    }
-    return result;
+    return new RuleBasedNumberFormat(*this);
 }
 
 UBool
 }
 
 UBool
@@ -156,16 +937,24 @@ RuleBasedNumberFormat::operator==(const Format& other) const
         return TRUE;
     }
 
         return TRUE;
     }
 
-    if (other.getDynamicClassID() == getStaticClassID()) {
+    if (typeid(*this) == typeid(other)) {
         const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
         const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
+        // test for capitalization info equality is adequately handled
+        // by the NumberFormat test for fCapitalizationContext equality;
+        // the info here is just derived from that.
         if (locale == rhs.locale &&
         if (locale == rhs.locale &&
-            lenient == rhs.lenient) {
-            NFRuleSet** p = ruleSets;
-            NFRuleSet** q = rhs.ruleSets;
-            if ((p == NULL) != (q == NULL)) {
-                return TRUE;
-            }
+            lenient == rhs.lenient &&
+            (localizations == NULL 
+                ? rhs.localizations == NULL 
+                : (rhs.localizations == NULL 
+                    ? FALSE
+                    : *localizations == rhs.localizations))) {
+
+            NFRuleSet** p = fRuleSets;
+            NFRuleSet** q = rhs.fRuleSets;
             if (p == NULL) {
             if (p == NULL) {
+                return q == NULL;
+            } else if (q == NULL) {
                 return FALSE;
             }
             while (*p && *q && (**p == **q)) {
                 return FALSE;
             }
             while (*p && *q && (**p == **q)) {
@@ -183,8 +972,8 @@ UnicodeString
 RuleBasedNumberFormat::getRules() const
 {
     UnicodeString result;
 RuleBasedNumberFormat::getRules() const
 {
     UnicodeString result;
-    if (ruleSets != NULL) {
-        for (NFRuleSet** p = ruleSets; *p; ++p) {
+    if (fRuleSets != NULL) {
+        for (NFRuleSet** p = fRuleSets; *p; ++p) {
             (*p)->appendRules(result);
         }
     }
             (*p)->appendRules(result);
         }
     }
@@ -194,9 +983,13 @@ RuleBasedNumberFormat::getRules() const
 UnicodeString
 RuleBasedNumberFormat::getRuleSetName(int32_t index) const
 {
 UnicodeString
 RuleBasedNumberFormat::getRuleSetName(int32_t index) const
 {
-    UnicodeString result;
-    if (ruleSets) {
-        for (NFRuleSet** p = ruleSets; *p; ++p) {
+    if (localizations) {
+        UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);
+        return string;
+    }
+    else if (fRuleSets) {
+        UnicodeString result;
+        for (NFRuleSet** p = fRuleSets; *p; ++p) {
             NFRuleSet* rs = *p;
             if (rs->isPublic()) {
                 if (--index == -1) {
             NFRuleSet* rs = *p;
             if (rs->isPublic()) {
                 if (--index == -1) {
@@ -206,15 +999,19 @@ RuleBasedNumberFormat::getRuleSetName(int32_t index) const
             }
         }
     }
             }
         }
     }
-    return result;
+    UnicodeString empty;
+    return empty;
 }
 
 int32_t
 RuleBasedNumberFormat::getNumberOfRuleSetNames() const
 {
     int32_t result = 0;
 }
 
 int32_t
 RuleBasedNumberFormat::getNumberOfRuleSetNames() const
 {
     int32_t result = 0;
-    if (ruleSets) {
-        for (NFRuleSet** p = ruleSets; *p; ++p) {
+    if (localizations) {
+        result = localizations->getNumberOfRuleSets();
+    }
+    else if (fRuleSets) {
+        for (NFRuleSet** p = fRuleSets; *p; ++p) {
             if ((**p).isPublic()) {
                 ++result;
             }
             if ((**p).isPublic()) {
                 ++result;
             }
@@ -223,11 +1020,86 @@ RuleBasedNumberFormat::getNumberOfRuleSetNames() const
     return result;
 }
 
     return result;
 }
 
+int32_t 
+RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
+    if (localizations) {
+        return localizations->getNumberOfDisplayLocales();
+    }
+    return 0;
+}
+
+Locale 
+RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return Locale("");
+    }
+    if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) {
+        UnicodeString name(TRUE, localizations->getLocaleName(index), -1);
+        char buffer[64];
+        int32_t cap = name.length() + 1;
+        char* bp = buffer;
+        if (cap > 64) {
+            bp = (char *)uprv_malloc(cap);
+            if (bp == NULL) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                return Locale("");
+            }
+        }
+        name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant);
+        Locale retLocale(bp);
+        if (bp != buffer) {
+            uprv_free(bp);
+        }
+        return retLocale;
+    }
+    status = U_ILLEGAL_ARGUMENT_ERROR;
+    Locale retLocale;
+    return retLocale;
+}
+
+UnicodeString 
+RuleBasedNumberFormat::getRuleSetDisplayName(int32_t index, const Locale& localeParam) {
+    if (localizations && index >= 0 && index < localizations->getNumberOfRuleSets()) {
+        UnicodeString localeName(localeParam.getBaseName(), -1, UnicodeString::kInvariant); 
+        int32_t len = localeName.length();
+        UChar* localeStr = localeName.getBuffer(len + 1);
+        while (len >= 0) {
+            localeStr[len] = 0;
+            int32_t ix = localizations->indexForLocale(localeStr);
+            if (ix >= 0) {
+                UnicodeString name(TRUE, localizations->getDisplayName(ix, index), -1);
+                return name;
+            }
+            
+            // trim trailing portion, skipping over ommitted sections
+            do { --len;} while (len > 0 && localeStr[len] != 0x005f); // underscore
+            while (len > 0 && localeStr[len-1] == 0x005F) --len;
+        }
+        UnicodeString name(TRUE, localizations->getRuleSetName(index), -1);
+        return name;
+    }
+    UnicodeString bogus;
+    bogus.setToBogus();
+    return bogus;
+}
+
+UnicodeString 
+RuleBasedNumberFormat::getRuleSetDisplayName(const UnicodeString& ruleSetName, const Locale& localeParam) {
+    if (localizations) {
+        UnicodeString rsn(ruleSetName);
+        int32_t ix = localizations->indexForRuleSet(rsn.getTerminatedBuffer());
+        return getRuleSetDisplayName(ix, localeParam);
+    }
+    UnicodeString bogus;
+    bogus.setToBogus();
+    return bogus;
+}
+
 NFRuleSet*
 RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const
 {
 NFRuleSet*
 RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status) const
 {
-    if (U_SUCCESS(status) && ruleSets) {
-        for (NFRuleSet** p = ruleSets; *p; ++p) {
+    if (U_SUCCESS(status) && fRuleSets) {
+        for (NFRuleSet** p = fRuleSets; *p; ++p) {
             NFRuleSet* rs = *p;
             if (rs->isNamed(name)) {
                 return rs;
             NFRuleSet* rs = *p;
             if (rs->isNamed(name)) {
                 return rs;
@@ -238,22 +1110,101 @@ RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status
     return NULL;
 }
 
     return NULL;
 }
 
+UnicodeString&
+RuleBasedNumberFormat::format(const DecimalQuantity &number,
+                      UnicodeString &appendTo,
+                      FieldPositionIterator *posIter,
+                      UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    DecimalQuantity copy(number);
+    if (copy.fitsInLong()) {
+        format(number.toLong(), appendTo, posIter, status);
+    }
+    else {
+        copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
+        if (copy.fitsInLong()) {
+            format(number.toDouble(), appendTo, posIter, status);
+        }
+        else {
+            // We're outside of our normal range that this framework can handle.
+            // The DecimalFormat will provide more accurate results.
+
+            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
+            LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status);
+            if (decimalFormat.isNull()) {
+                return appendTo;
+            }
+            Formattable f;
+            LocalPointer<DecimalQuantity> decimalQuantity(new DecimalQuantity(number), status);
+            if (decimalQuantity.isNull()) {
+                return appendTo;
+            }
+            f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity.
+            decimalFormat->format(f, appendTo, posIter, status);
+        }
+    }
+    return appendTo;
+}
+
+
+UnicodeString&
+RuleBasedNumberFormat::format(const DecimalQuantity &number,
+                     UnicodeString& appendTo,
+                     FieldPosition& pos,
+                     UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    DecimalQuantity copy(number);
+    if (copy.fitsInLong()) {
+        format(number.toLong(), appendTo, pos, status);
+    }
+    else {
+        copy.roundToMagnitude(0, number::impl::RoundingMode::UNUM_ROUND_HALFEVEN, status);
+        if (copy.fitsInLong()) {
+            format(number.toDouble(), appendTo, pos, status);
+        }
+        else {
+            // We're outside of our normal range that this framework can handle.
+            // The DecimalFormat will provide more accurate results.
+
+            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
+            LocalPointer<NumberFormat> decimalFormat(NumberFormat::createInstance(locale, UNUM_DECIMAL, status), status);
+            if (decimalFormat.isNull()) {
+                return appendTo;
+            }
+            Formattable f;
+            LocalPointer<DecimalQuantity> decimalQuantity(new DecimalQuantity(number), status);
+            if (decimalQuantity.isNull()) {
+                return appendTo;
+            }
+            f.adoptDecimalQuantity(decimalQuantity.orphan()); // f now owns decimalQuantity.
+            decimalFormat->format(f, appendTo, pos, status);
+        }
+    }
+    return appendTo;
+}
+
 UnicodeString&
 RuleBasedNumberFormat::format(int32_t number,
                               UnicodeString& toAppendTo,
                               FieldPosition& pos) const
 {
 UnicodeString&
 RuleBasedNumberFormat::format(int32_t number,
                               UnicodeString& toAppendTo,
                               FieldPosition& pos) const
 {
-    if (defaultRuleSet) defaultRuleSet->format((int64_t)number, toAppendTo, toAppendTo.length());
-    return toAppendTo;
+    return format((int64_t)number, toAppendTo, pos);
 }
 
 
 UnicodeString&
 RuleBasedNumberFormat::format(int64_t number,
                               UnicodeString& toAppendTo,
 }
 
 
 UnicodeString&
 RuleBasedNumberFormat::format(int64_t number,
                               UnicodeString& toAppendTo,
-                              FieldPosition& pos) const
+                              FieldPosition& /* pos */) const
 {
 {
-    if (defaultRuleSet) defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+    if (defaultRuleSet) {
+        UErrorCode status = U_ZERO_ERROR;
+        format(number, defaultRuleSet, toAppendTo, status);
+    }
     return toAppendTo;
 }
 
     return toAppendTo;
 }
 
@@ -261,9 +1212,12 @@ RuleBasedNumberFormat::format(int64_t number,
 UnicodeString&
 RuleBasedNumberFormat::format(double number,
                               UnicodeString& toAppendTo,
 UnicodeString&
 RuleBasedNumberFormat::format(double number,
                               UnicodeString& toAppendTo,
-                              FieldPosition& pos) const
+                              FieldPosition& /* pos */) const
 {
 {
-    if (defaultRuleSet) defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+    UErrorCode status = U_ZERO_ERROR;
+    if (defaultRuleSet) {
+        format(number, *defaultRuleSet, toAppendTo, status);
+    }
     return toAppendTo;
 }
 
     return toAppendTo;
 }
 
@@ -275,15 +1229,25 @@ RuleBasedNumberFormat::format(int32_t number,
                               FieldPosition& pos,
                               UErrorCode& status) const
 {
                               FieldPosition& pos,
                               UErrorCode& status) const
 {
-    // return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
+    return format((int64_t)number, ruleSetName, toAppendTo, pos, status);
+}
+
+
+UnicodeString&
+RuleBasedNumberFormat::format(int64_t number,
+                              const UnicodeString& ruleSetName,
+                              UnicodeString& toAppendTo,
+                              FieldPosition& /* pos */,
+                              UErrorCode& status) const
+{
     if (U_SUCCESS(status)) {
     if (U_SUCCESS(status)) {
-        if (ruleSetName.indexOf(gPercentPercent) == 0) {
+        if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
             // throw new IllegalArgumentException("Can't use internal rule set");
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet *rs = findRuleSet(ruleSetName, status);
             if (rs) {
             // throw new IllegalArgumentException("Can't use internal rule set");
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet *rs = findRuleSet(ruleSetName, status);
             if (rs) {
-                rs->format((int64_t)number, toAppendTo, toAppendTo.length());
+                format(number, rs, toAppendTo, status);
             }
         }
     }
             }
         }
     }
@@ -292,98 +1256,173 @@ RuleBasedNumberFormat::format(int32_t number,
 
 
 UnicodeString&
 
 
 UnicodeString&
-RuleBasedNumberFormat::format(int64_t number,
+RuleBasedNumberFormat::format(double number,
                               const UnicodeString& ruleSetName,
                               UnicodeString& toAppendTo,
                               const UnicodeString& ruleSetName,
                               UnicodeString& toAppendTo,
-                              FieldPosition& pos,
+                              FieldPosition& /* pos */,
                               UErrorCode& status) const
 {
     if (U_SUCCESS(status)) {
                               UErrorCode& status) const
 {
     if (U_SUCCESS(status)) {
-        if (ruleSetName.indexOf(gPercentPercent) == 0) {
+        if (ruleSetName.indexOf(gPercentPercent, 2, 0) == 0) {
             // throw new IllegalArgumentException("Can't use internal rule set");
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet *rs = findRuleSet(ruleSetName, status);
             if (rs) {
             // throw new IllegalArgumentException("Can't use internal rule set");
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet *rs = findRuleSet(ruleSetName, status);
             if (rs) {
-                rs->format(number, toAppendTo, toAppendTo.length());
+                format(number, *rs, toAppendTo, status);
             }
         }
     }
     return toAppendTo;
 }
 
             }
         }
     }
     return toAppendTo;
 }
 
-
-// make linker happy
-UnicodeString&
-RuleBasedNumberFormat::format(const Formattable& obj,
+void
+RuleBasedNumberFormat::format(double number,
+                              NFRuleSet& rs,
                               UnicodeString& toAppendTo,
                               UnicodeString& toAppendTo,
-                              FieldPosition& pos,
                               UErrorCode& status) const
 {
                               UErrorCode& status) const
 {
-    return NumberFormat::format(obj, toAppendTo, pos, status);
+    int32_t startPos = toAppendTo.length();
+    if (getRoundingMode() != DecimalFormat::ERoundingMode::kRoundUnnecessary && !uprv_isNaN(number) && !uprv_isInfinite(number)) {
+        DecimalQuantity digitList;
+        digitList.setToDouble(number);
+        digitList.roundToMagnitude(
+                -getMaximumFractionDigits(),
+                static_cast<UNumberFormatRoundingMode>(getRoundingMode()),
+                status);
+        number = digitList.toDouble();
+    }
+    rs.format(number, toAppendTo, toAppendTo.length(), 0, status);
+    adjustForCapitalizationContext(startPos, toAppendTo, status);
 }
 
 }
 
+/**
+ * Bottleneck through which all the public format() methods
+ * that take a long pass. By the time we get here, we know
+ * which rule set we're using to do the formatting.
+ * @param number The number to format
+ * @param ruleSet The rule set to use to format the number
+ * @return The text that resulted from formatting the number
+ */
 UnicodeString&
 UnicodeString&
-RuleBasedNumberFormat::format(double number,
-                              const UnicodeString& ruleSetName,
-                              UnicodeString& toAppendTo,
-                              FieldPosition& pos,
-                              UErrorCode& status) const
+RuleBasedNumberFormat::format(int64_t number, NFRuleSet *ruleSet, UnicodeString& toAppendTo, UErrorCode& status) const
 {
 {
+    // all API format() routines that take a double vector through
+    // here.  We have these two identical functions-- one taking a
+    // double and one taking a long-- the couple digits of precision
+    // that long has but double doesn't (both types are 8 bytes long,
+    // but double has to borrow some of the mantissa bits to hold
+    // the exponent).
+    // Create an empty string buffer where the result will
+    // be built, and pass it to the rule set (along with an insertion
+    // position of 0 and the number being formatted) to the rule set
+    // for formatting
+
     if (U_SUCCESS(status)) {
     if (U_SUCCESS(status)) {
-        if (ruleSetName.indexOf(gPercentPercent) == 0) {
-            // throw new IllegalArgumentException("Can't use internal rule set");
-            status = U_ILLEGAL_ARGUMENT_ERROR;
-        } else {
-            NFRuleSet *rs = findRuleSet(ruleSetName, status);
-            if (rs) {
-                rs->format(number, toAppendTo, toAppendTo.length());
+        if (number == U_INT64_MIN) {
+            // We can't handle this value right now. Provide an accurate default value.
+
+            // TODO this section should probably be optimized. The DecimalFormat is shared in ICU4J.
+            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
+            if (decimalFormat == nullptr) {
+                return toAppendTo;
             }
             }
+            Formattable f;
+            FieldPosition pos(FieldPosition::DONT_CARE);
+            DecimalQuantity *decimalQuantity = new DecimalQuantity();
+            if (decimalQuantity == nullptr) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                delete decimalFormat;
+                return toAppendTo;
+            }
+            decimalQuantity->setToLong(number);
+            f.adoptDecimalQuantity(decimalQuantity); // f now owns decimalQuantity.
+            decimalFormat->format(f, toAppendTo, pos, status);
+            delete decimalFormat;
+        }
+        else {
+            int32_t startPos = toAppendTo.length();
+            ruleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
+            adjustForCapitalizationContext(startPos, toAppendTo, status);
         }
     }
     return toAppendTo;
 }
 
         }
     }
     return toAppendTo;
 }
 
+UnicodeString&
+RuleBasedNumberFormat::adjustForCapitalizationContext(int32_t startPos,
+                                                      UnicodeString& currentResult,
+                                                      UErrorCode& status) const
+{
+#if !UCONFIG_NO_BREAK_ITERATION
+    UDisplayContext capitalizationContext = getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
+    if (capitalizationContext != UDISPCTX_CAPITALIZATION_NONE && startPos == 0 && currentResult.length() > 0) {
+        // capitalize currentResult according to context
+        UChar32 ch = currentResult.char32At(0);
+        if (u_islower(ch) && U_SUCCESS(status) && capitalizationBrkIter != NULL &&
+              ( capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+                (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
+                (capitalizationContext == UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) {
+            // titlecase first word of currentResult, here use sentence iterator unlike current implementations
+            // in LocaleDisplayNamesImpl::adjustForUsageAndContext and RelativeDateFormat::format
+            currentResult.toTitle(capitalizationBrkIter, locale, U_TITLECASE_NO_LOWERCASE | U_TITLECASE_NO_BREAK_ADJUSTMENT);
+        }
+    }
+#endif
+    return currentResult;
+}
+
+
 void
 RuleBasedNumberFormat::parse(const UnicodeString& text,
                              Formattable& result,
                              ParsePosition& parsePosition) const
 {
 void
 RuleBasedNumberFormat::parse(const UnicodeString& text,
                              Formattable& result,
                              ParsePosition& parsePosition) const
 {
-    if (!ruleSets) {
+    if (!fRuleSets) {
         parsePosition.setErrorIndex(0);
         return;
     }
 
         parsePosition.setErrorIndex(0);
         return;
     }
 
-    ParsePosition high_pp;
+    UnicodeString workingText(text, parsePosition.getIndex());
+    ParsePosition workingPos(0);
+
+    ParsePosition high_pp(0);
     Formattable high_result;
 
     Formattable high_result;
 
-    for (NFRuleSet** p = ruleSets; *p; ++p) {
+    for (NFRuleSet** p = fRuleSets; *p; ++p) {
         NFRuleSet *rp = *p;
         NFRuleSet *rp = *p;
-        if (rp->isPublic()) {
-            ParsePosition working_pp = parsePosition;
+        if (rp->isPublic() && rp->isParseable()) {
+            ParsePosition working_pp(0);
             Formattable working_result;
 
             Formattable working_result;
 
-            rp->parse(text, working_pp, kMaxDouble, working_result);
+            rp->parse(workingText, working_pp, kMaxDouble, 0, working_result, lenient);
             if (working_pp.getIndex() > high_pp.getIndex()) {
                 high_pp = working_pp;
                 high_result = working_result;
 
             if (working_pp.getIndex() > high_pp.getIndex()) {
                 high_pp = working_pp;
                 high_result = working_result;
 
-                if (high_pp.getIndex() == text.length()) {
+                if (high_pp.getIndex() == workingText.length()) {
                     break;
                 }
             }
         }
     }
 
                     break;
                 }
             }
         }
     }
 
-    if (high_pp.getIndex() > parsePosition.getIndex()) {
-        high_pp.setErrorIndex(-1);
+    int32_t startIndex = parsePosition.getIndex();
+    parsePosition.setIndex(startIndex + high_pp.getIndex());
+    if (high_pp.getIndex() > 0) {
+        parsePosition.setErrorIndex(-1);
+    } else {
+        int32_t errorIndex = (high_pp.getErrorIndex()>0)? high_pp.getErrorIndex(): 0;
+        parsePosition.setErrorIndex(startIndex + errorIndex);
     }
     }
-    parsePosition = high_pp;
     result = high_result;
     if (result.getType() == Formattable::kDouble) {
     result = high_result;
     if (result.getType() == Formattable::kDouble) {
-        int32_t r = (int32_t)result.getDouble();
-        if ((double)r == result.getDouble()) {
-            result.setLong(r);
+        double d = result.getDouble();
+        if (!uprv_isNaN(d) && d == uprv_trunc(d) && INT32_MIN <= d && d <= INT32_MAX) {
+            // Note: casting a double to an int when the double is too large or small
+            //       to fit the destination is undefined behavior. The explicit range checks,
+            //       above, are required. Just casting and checking the result value is undefined.
+            result.setLong(static_cast<int32_t>(d));
         }
     }
 }
         }
     }
 }
@@ -406,8 +1445,13 @@ void
 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) {
     if (U_SUCCESS(status)) {
         if (ruleSetName.isEmpty()) {
 RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErrorCode& status) {
     if (U_SUCCESS(status)) {
         if (ruleSetName.isEmpty()) {
+          if (localizations) {
+              UnicodeString name(TRUE, localizations->getRuleSetName(0), -1);
+              defaultRuleSet = findRuleSet(name, status);
+          } else {
             initDefaultRuleSet();
             initDefaultRuleSet();
-        } else if (ruleSetName.startsWith("%%")) {
+          }
+        } else if (ruleSetName.startsWith(UNICODE_STRING_SIMPLE("%%"))) {
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet* result = findRuleSet(ruleSetName, status);
             status = U_ILLEGAL_ARGUMENT_ERROR;
         } else {
             NFRuleSet* result = findRuleSet(ruleSetName, status);
@@ -418,20 +1462,42 @@ RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErro
     }
 }
 
     }
 }
 
+UnicodeString
+RuleBasedNumberFormat::getDefaultRuleSetName() const {
+    UnicodeString result;
+    if (defaultRuleSet && defaultRuleSet->isPublic()) {
+        defaultRuleSet->getName(result);
+    } else {
+        result.setToBogus();
+    }
+    return result;
+}
+
 void 
 RuleBasedNumberFormat::initDefaultRuleSet()
 {
 void 
 RuleBasedNumberFormat::initDefaultRuleSet()
 {
-    if (!ruleSets) {
-        defaultRuleSet = NULL;
+    defaultRuleSet = NULL;
+    if (!fRuleSets) {
+        return;
     }
     }
-    NFRuleSet**p = &ruleSets[1];
+
+    const UnicodeString spellout(UNICODE_STRING_SIMPLE("%spellout-numbering"));
+    const UnicodeString ordinal(UNICODE_STRING_SIMPLE("%digits-ordinal"));
+    const UnicodeString duration(UNICODE_STRING_SIMPLE("%duration"));
+
+    NFRuleSet**p = &fRuleSets[0];
     while (*p) {
     while (*p) {
-        ++p;
+        if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) {
+            defaultRuleSet = *p;
+            return;
+        } else {
+            ++p;
+        }
     }
 
     defaultRuleSet = *--p;
     if (!defaultRuleSet->isPublic()) {
     }
 
     defaultRuleSet = *--p;
     if (!defaultRuleSet->isPublic()) {
-        while (p != ruleSets) {
+        while (p != fRuleSets) {
             if ((*--p)->isPublic()) {
                 defaultRuleSet = *p;
                 break;
             if ((*--p)->isPublic()) {
                 defaultRuleSet = *p;
                 break;
@@ -442,14 +1508,25 @@ RuleBasedNumberFormat::initDefaultRuleSet()
 
 
 void
 
 
 void
-RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErrorCode& status)
+RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,
+                            UParseError& pErr, UErrorCode& status)
 {
     // TODO: implement UParseError
 {
     // TODO: implement UParseError
+    uprv_memset(&pErr, 0, sizeof(UParseError));
     // Note: this can leave ruleSets == NULL, so remaining code should check
     if (U_FAILURE(status)) {
         return;
     }
 
     // Note: this can leave ruleSets == NULL, so remaining code should check
     if (U_FAILURE(status)) {
         return;
     }
 
+    initializeDecimalFormatSymbols(status);
+    initializeDefaultInfinityRule(status);
+    initializeDefaultNaNRule(status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref();
+
     UnicodeString description(rules);
     if (!description.length()) {
         status = U_MEMORY_ALLOCATION_ERROR;
     UnicodeString description(rules);
     if (!description.length()) {
         status = U_MEMORY_ALLOCATION_ERROR;
@@ -467,7 +1544,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
     // is, pull them out into our temporary holding place for them,
     // and delete them from the description before the real desciption-
     // parsing code sees them
     // is, pull them out into our temporary holding place for them,
     // and delete them from the description before the real desciption-
     // parsing code sees them
-    int32_t lp = description.indexOf(gLenientParse);
+    int32_t lp = description.indexOf(gLenientParse, -1, 0);
     if (lp != -1) {
         // we've got to make sure we're not in the middle of a rule
         // (where "%%lenient-parse" would actually get treated as
     if (lp != -1) {
         // we've got to make sure we're not in the middle of a rule
         // (where "%%lenient-parse" would actually get treated as
@@ -476,13 +1553,13 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
             // locate the beginning and end of the actual collation
             // rules (there may be whitespace between the name and
             // the first token in the description)
             // locate the beginning and end of the actual collation
             // rules (there may be whitespace between the name and
             // the first token in the description)
-            int lpEnd = description.indexOf(gSemiPercent, lp);
+            int lpEnd = description.indexOf(gSemiPercent, 2, lp);
 
             if (lpEnd == -1) {
                 lpEnd = description.length() - 1;
             }
             int lpStart = lp + u_strlen(gLenientParse);
 
             if (lpEnd == -1) {
                 lpEnd = description.length() - 1;
             }
             int lpStart = lp + u_strlen(gLenientParse);
-            while (uprv_isRuleWhiteSpace(description.charAt(lpStart))) {
+            while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
                 ++lpStart;
             }
 
                 ++lpStart;
             }
 
@@ -490,7 +1567,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
             // from the description
             lenientParseRules = new UnicodeString();
             /* test for NULL */
             // from the description
             lenientParseRules = new UnicodeString();
             /* test for NULL */
-            if (lenientParseRules == 0) {
+            if (lenientParseRules == nullptr) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
@@ -503,23 +1580,23 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
     // pre-flight parsing the description and count the number of
     // rule sets (";%" marks the end of one rule set and the beginning
     // of the next)
     // pre-flight parsing the description and count the number of
     // rule sets (";%" marks the end of one rule set and the beginning
     // of the next)
-    int numRuleSets = 0;
-    for (int32_t p = description.indexOf(gSemiPercent); p != -1; p = description.indexOf(gSemiPercent, p)) {
+    numRuleSets = 0;
+    for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, p)) {
         ++numRuleSets;
         ++p;
     }
     ++numRuleSets;
 
     // our rule list is an array of the appropriate size
         ++numRuleSets;
         ++p;
     }
     ++numRuleSets;
 
     // our rule list is an array of the appropriate size
-    ruleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *));
+    fRuleSets = (NFRuleSet **)uprv_malloc((numRuleSets + 1) * sizeof(NFRuleSet *));
     /* test for NULL */
     /* test for NULL */
-    if (ruleSets == 0) {
+    if (fRuleSets == 0) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
     }
 
     for (int i = 0; i <= numRuleSets; ++i) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
     }
 
     for (int i = 0; i <= numRuleSets; ++i) {
-        ruleSets[i] = NULL;
+        fRuleSets[i] = NULL;
     }
 
     // divide up the descriptions into individual rule-set descriptions
     }
 
     // divide up the descriptions into individual rule-set descriptions
@@ -530,12 +1607,12 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
     // because we have to know the names and locations of all the rule
     // sets before we can actually set everything up
     if(!numRuleSets) {
     // because we have to know the names and locations of all the rule
     // sets before we can actually set everything up
     if(!numRuleSets) {
-      status = U_ILLEGAL_ARGUMENT_ERROR;
-      return;
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
     }
     }
-    UnicodeString* ruleSetDescriptions = new UnicodeString[numRuleSets];
-    /* test for NULL */
-    if (ruleSetDescriptions == 0) {
+
+    ruleSetDescriptions = new UnicodeString[numRuleSets];
+    if (ruleSetDescriptions == nullptr) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
     }
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
     }
@@ -543,11 +1620,10 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
     {
         int curRuleSet = 0;
         int32_t start = 0;
     {
         int curRuleSet = 0;
         int32_t start = 0;
-        for (int32_t p = description.indexOf(gSemiPercent); p != -1; p = description.indexOf(gSemiPercent, start)) {
+        for (int32_t p = description.indexOf(gSemiPercent, 2, 0); p != -1; p = description.indexOf(gSemiPercent, 2, start)) {
             ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);
             ruleSetDescriptions[curRuleSet].setTo(description, start, p + 1 - start);
-            ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
-            /* test for NULL */
-            if (ruleSets[curRuleSet] == 0) {
+            fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
+            if (fRuleSets[curRuleSet] == nullptr) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
@@ -555,9 +1631,8 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
             start = p + 1;
         }
         ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
             start = p + 1;
         }
         ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
-        ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
-        /* test for NULL */
-        if (ruleSets[curRuleSet] == 0) {
+        fRuleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
+        if (fRuleSets[curRuleSet] == nullptr) {
             status = U_MEMORY_ALLOCATION_ERROR;
             return;
         }
             status = U_MEMORY_ALLOCATION_ERROR;
             return;
         }
@@ -568,18 +1643,93 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, UParseError& pErr, UErro
     // rather than the first so that a user can create a new formatter
     // from an existing formatter and change its default behavior just
     // by appending more rule sets to the end)
     // rather than the first so that a user can create a new formatter
     // from an existing formatter and change its default behavior just
     // by appending more rule sets to the end)
-       initDefaultRuleSet();
+
+    // {dlf} Initialization of a fraction rule set requires the default rule
+    // set to be known.  For purposes of initialization, this is always the 
+    // last public rule set, no matter what the localization data says.
+    initDefaultRuleSet();
 
     // finally, we can go back through the temporary descriptions
 
     // finally, we can go back through the temporary descriptions
-    // list and finish seting up the substructure (and we throw
+    // list and finish setting up the substructure (and we throw
     // away the temporary descriptions as we go)
     {
         for (int i = 0; i < numRuleSets; i++) {
     // away the temporary descriptions as we go)
     {
         for (int i = 0; i < numRuleSets; i++) {
-            ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status);
+            fRuleSets[i]->parseRules(ruleSetDescriptions[i], status);
+        }
+    }
+
+    // Now that the rules are initialized, the 'real' default rule
+    // set can be adjusted by the localization data.
+
+    // The C code keeps the localization array as is, rather than building
+    // a separate array of the public rule set names, so we have less work
+    // to do here-- but we still need to check the names.
+    
+    if (localizationInfos) {
+        // confirm the names, if any aren't in the rules, that's an error
+        // it is ok if the rules contain public rule sets that are not in this list
+        for (int32_t i = 0; i < localizationInfos->getNumberOfRuleSets(); ++i) {
+            UnicodeString name(TRUE, localizationInfos->getRuleSetName(i), -1);
+            NFRuleSet* rs = findRuleSet(name, status);
+            if (rs == NULL) {
+                break; // error
+            }
+            if (i == 0) {
+                defaultRuleSet = rs;
+            }
         }
         }
+    } else {
+        defaultRuleSet = getDefaultRuleSet();
     }
     }
+    originalDescription = rules;
+}
 
 
-    delete[] ruleSetDescriptions;
+// override the NumberFormat implementation in order to
+// lazily initialize relevant items
+void
+RuleBasedNumberFormat::setContext(UDisplayContext value, UErrorCode& status)
+{
+    NumberFormat::setContext(value, status);
+    if (U_SUCCESS(status)) {
+       if (!capitalizationInfoSet &&
+               (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU || value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE)) {
+           initCapitalizationContextInfo(locale);
+           capitalizationInfoSet = TRUE;
+        }
+#if !UCONFIG_NO_BREAK_ITERATION
+        if ( capitalizationBrkIter == NULL && (value==UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
+                (value==UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForUIListMenu) ||
+                (value==UDISPCTX_CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone)) ) {
+            status = U_ZERO_ERROR;
+            capitalizationBrkIter = BreakIterator::createSentenceInstance(locale, status);
+            if (U_FAILURE(status)) {
+                delete capitalizationBrkIter;
+                capitalizationBrkIter = NULL;
+            }
+        }
+#endif
+    }
+}
+
+void
+RuleBasedNumberFormat::initCapitalizationContextInfo(const Locale& thelocale)
+{
+#if !UCONFIG_NO_BREAK_ITERATION
+    const char * localeID = (thelocale != NULL)? thelocale.getBaseName(): NULL;
+    UErrorCode status = U_ZERO_ERROR;
+    UResourceBundle *rb = ures_open(NULL, localeID, &status);
+    rb = ures_getByKeyWithFallback(rb, "contextTransforms", rb, &status);
+    rb = ures_getByKeyWithFallback(rb, "number-spellout", rb, &status);
+    if (U_SUCCESS(status) && rb != NULL) {
+        int32_t len = 0;
+        const int32_t * intVector = ures_getIntVector(rb, &len, &status);
+        if (U_SUCCESS(status) && intVector != NULL && len >= 2) {
+            capitalizationForUIListMenu = static_cast<UBool>(intVector[0]);
+            capitalizationForStandAlone = static_cast<UBool>(intVector[1]);
+        }
+    }
+    ures_close(rb);
+#endif
 }
 
 void
 }
 
 void
@@ -592,7 +1742,7 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
     while (start != -1 && start < description.length()) {
         // seek to the first non-whitespace character...
         while (start < description.length()
     while (start != -1 && start < description.length()) {
         // seek to the first non-whitespace character...
         while (start < description.length()
-            && uprv_isRuleWhiteSpace(description.charAt(start))) {
+            && PatternProps::isWhiteSpace(description.charAt(start))) {
             ++start;
         }
 
             ++start;
         }
 
@@ -610,7 +1760,7 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
             start = p + 1;
         }
 
             start = p + 1;
         }
 
-        // when we get here, we've seeked off the end of the sring, and
+        // when we get here, we've seeked off the end of the string, and
         // we terminate the loop (we continue until *start* is -1 rather
         // than until *p* is -1, because otherwise we'd miss the last
         // rule in the description)
         // we terminate the loop (we continue until *start* is -1 rather
         // than until *p* is -1, because otherwise we'd miss the last
         // rule in the description)
@@ -626,12 +1776,17 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
 void
 RuleBasedNumberFormat::dispose()
 {
 void
 RuleBasedNumberFormat::dispose()
 {
-    if (ruleSets) {
-        for (NFRuleSet** p = ruleSets; *p; ++p) {
+    if (fRuleSets) {
+        for (NFRuleSet** p = fRuleSets; *p; ++p) {
             delete *p;
         }
             delete *p;
         }
-        uprv_free(ruleSets);
-        ruleSets = NULL;
+        uprv_free(fRuleSets);
+        fRuleSets = NULL;
+    }
+
+    if (ruleSetDescriptions) {
+        delete [] ruleSetDescriptions;
+        ruleSetDescriptions = NULL;
     }
 
 #if !UCONFIG_NO_COLLATION
     }
 
 #if !UCONFIG_NO_COLLATION
@@ -642,8 +1797,23 @@ RuleBasedNumberFormat::dispose()
     delete decimalFormatSymbols;
     decimalFormatSymbols = NULL;
 
     delete decimalFormatSymbols;
     decimalFormatSymbols = NULL;
 
+    delete defaultInfinityRule;
+    defaultInfinityRule = NULL;
+
+    delete defaultNaNRule;
+    defaultNaNRule = NULL;
+
     delete lenientParseRules;
     lenientParseRules = NULL;
     delete lenientParseRules;
     lenientParseRules = NULL;
+
+#if !UCONFIG_NO_BREAK_ITERATION
+    delete capitalizationBrkIter;
+    capitalizationBrkIter = NULL;
+#endif
+
+    if (localizations) {
+        localizations = localizations->unref();
+    }
 }
 
 
 }
 
 
@@ -657,33 +1827,35 @@ RuleBasedNumberFormat::dispose()
  * @return The collator to use for lenient parsing, or null if lenient parsing
  * is turned off.
 */
  * @return The collator to use for lenient parsing, or null if lenient parsing
  * is turned off.
 */
-Collator*
+const RuleBasedCollator*
 RuleBasedNumberFormat::getCollator() const
 {
 #if !UCONFIG_NO_COLLATION
 RuleBasedNumberFormat::getCollator() const
 {
 #if !UCONFIG_NO_COLLATION
-    if (!ruleSets) {
+    if (!fRuleSets) {
         return NULL;
     }
 
         return NULL;
     }
 
-    // lazy-evaulate the collator
+    // lazy-evaluate the collator
     if (collator == NULL && lenient) {
         // create a default collator based on the formatter's locale,
         // then pull out that collator's rules, append any additional
         // rules specified in the description, and create a _new_
     if (collator == NULL && lenient) {
         // create a default collator based on the formatter's locale,
         // then pull out that collator's rules, append any additional
         // rules specified in the description, and create a _new_
-        // collator based on the combinaiton of those rules
+        // collator based on the combination of those rules
 
         UErrorCode status = U_ZERO_ERROR;
 
         Collator* temp = Collator::createInstance(locale, status);
 
         UErrorCode status = U_ZERO_ERROR;
 
         Collator* temp = Collator::createInstance(locale, status);
-        if (U_SUCCESS(status) &&
-            temp->getDynamicClassID() == RuleBasedCollator::getStaticClassID()) {
-
-            RuleBasedCollator* newCollator = (RuleBasedCollator*)temp;
+        RuleBasedCollator* newCollator;
+        if (U_SUCCESS(status) && (newCollator = dynamic_cast<RuleBasedCollator*>(temp)) != NULL) {
             if (lenientParseRules) {
                 UnicodeString rules(newCollator->getRules());
                 rules.append(*lenientParseRules);
 
                 newCollator = new RuleBasedCollator(rules, status);
             if (lenientParseRules) {
                 UnicodeString rules(newCollator->getRules());
                 rules.append(*lenientParseRules);
 
                 newCollator = new RuleBasedCollator(rules, status);
+                // Exit if newCollator could not be created.
+                if (newCollator == NULL) {
+                    return NULL;
+                }
             } else {
                 temp = NULL;
             }
             } else {
                 temp = NULL;
             }
@@ -705,30 +1877,148 @@ RuleBasedNumberFormat::getCollator() const
 }
 
 
 }
 
 
-/**
- * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
- * instances owned by this formatter.  This object is lazily created: this function
- * creates it the first time it's called.
- * @return The DecimalFormatSymbols object that should be used by all DecimalFormat
- * instances owned by this formatter.
-*/
 DecimalFormatSymbols*
 DecimalFormatSymbols*
-RuleBasedNumberFormat::getDecimalFormatSymbols() const
+RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)
 {
     // lazy-evaluate the DecimalFormatSymbols object.  This object
     // is shared by all DecimalFormat instances belonging to this
     // formatter
 {
     // lazy-evaluate the DecimalFormatSymbols object.  This object
     // is shared by all DecimalFormat instances belonging to this
     // formatter
-    if (decimalFormatSymbols == NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        DecimalFormatSymbols* temp = new DecimalFormatSymbols(locale, status);
+    if (decimalFormatSymbols == nullptr) {
+        LocalPointer<DecimalFormatSymbols> temp(new DecimalFormatSymbols(locale, status), status);
         if (U_SUCCESS(status)) {
         if (U_SUCCESS(status)) {
-            ((RuleBasedNumberFormat*)this)->decimalFormatSymbols = temp;
-        } else {
-            delete temp;
+            decimalFormatSymbols = temp.orphan();
         }
     }
     return decimalFormatSymbols;
 }
 
         }
     }
     return decimalFormatSymbols;
 }
 
+/**
+ * Returns the DecimalFormatSymbols object that should be used by all DecimalFormat
+ * instances owned by this formatter.
+*/
+const DecimalFormatSymbols*
+RuleBasedNumberFormat::getDecimalFormatSymbols() const
+{
+    return decimalFormatSymbols;
+}
+
+NFRule*
+RuleBasedNumberFormat::initializeDefaultInfinityRule(UErrorCode &status)
+{
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
+    if (defaultInfinityRule == NULL) {
+        UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: "));
+        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol));
+        LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);
+        if (U_SUCCESS(status)) {
+            defaultInfinityRule = temp.orphan();
+        }
+    }
+    return defaultInfinityRule;
+}
+
+const NFRule*
+RuleBasedNumberFormat::getDefaultInfinityRule() const
+{
+    return defaultInfinityRule;
+}
+
+NFRule*
+RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status)
+{
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
+    if (defaultNaNRule == nullptr) {
+        UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: "));
+        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol));
+        LocalPointer<NFRule> temp(new NFRule(this, rule, status), status);
+        if (U_SUCCESS(status)) {
+            defaultNaNRule = temp.orphan();
+        }
+    }
+    return defaultNaNRule;
+}
+
+const NFRule*
+RuleBasedNumberFormat::getDefaultNaNRule() const
+{
+    return defaultNaNRule;
+}
+
+// De-owning the current localized symbols and adopt the new symbols.
+void
+RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt)
+{
+    if (symbolsToAdopt == NULL) {
+        return; // do not allow caller to set decimalFormatSymbols to NULL
+    }
+
+    if (decimalFormatSymbols != NULL) {
+        delete decimalFormatSymbols;
+    }
+
+    decimalFormatSymbols = symbolsToAdopt;
+
+    {
+        // Apply the new decimalFormatSymbols by reparsing the rulesets
+        UErrorCode status = U_ZERO_ERROR;
+
+        delete defaultInfinityRule;
+        defaultInfinityRule = NULL;
+        initializeDefaultInfinityRule(status); // Reset with the new DecimalFormatSymbols
+
+        delete defaultNaNRule;
+        defaultNaNRule = NULL;
+        initializeDefaultNaNRule(status); // Reset with the new DecimalFormatSymbols
+
+        if (fRuleSets) {
+            for (int32_t i = 0; i < numRuleSets; i++) {
+                fRuleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
+            }
+        }
+    }
+}
+
+// Setting the symbols is equivalent to adopting a newly created localized symbols.
+void
+RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
+{
+    adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
+}
+
+PluralFormat *
+RuleBasedNumberFormat::createPluralFormat(UPluralType pluralType,
+                                          const UnicodeString &pattern,
+                                          UErrorCode& status) const
+{
+    auto *pf = new PluralFormat(locale, pluralType, pattern, status);
+    if (pf == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+    return pf;
+}
+
+/**
+ * Get the rounding mode.
+ * @return A rounding mode
+ */
+DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
+    return fRoundingMode;
+}
+
+/**
+ * Set the rounding mode.  This has no effect unless the rounding
+ * increment is greater than zero.
+ * @param roundingMode A rounding mode
+ */
+void RuleBasedNumberFormat::setRoundingMode(DecimalFormat::ERoundingMode roundingMode) {
+    fRoundingMode = roundingMode;
+}
+
+U_NAMESPACE_END
+
 /* U_HAVE_RBNF */
 #endif
 /* U_HAVE_RBNF */
 #endif