]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/rbnf.cpp
ICU-62141.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / rbnf.cpp
index 3922d31215e131ee731fa56694b3e3a83169490b..f7402b597e5f6e8b11f7a5c7769329b13b6c58c7 100644 (file)
@@ -1,17 +1,21 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 *******************************************************************************
-* Copyright (C) 1997-2010, International Business Machines Corporation
+* Copyright (C) 1997-2015, International Business Machines Corporation
 * and others. All Rights Reserved.
 *******************************************************************************
 */
 
-#include <typeinfo>  // for 'typeid' to work
+#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/plurfmt.h"
 #include "unicode/tblcoll.h"
 #include "unicode/uchar.h"
 #include "unicode/ucol.h"
 #include "unicode/ustring.h"
 #include "unicode/utf16.h"
 #include "unicode/udata.h"
-#include "nfrs.h"
+#include "unicode/udisplaycontext.h"
+#include "unicode/brkiter.h"
+#include "unicode/ucasemap.h"
 
 #include "cmemory.h"
 #include "cstring.h"
-#include "util.h"
+#include "patternprops.h"
 #include "uresimp.h"
+#include "nfrs.h"
+#include "digitlst.h"
 
 // debugging
-// #define DEBUG
+// #define RBNF_DEBUG
 
-#ifdef DEBUG
-#include "stdio.h"
+#ifdef RBNF_DEBUG
+#include <stdio.h>
 #endif
 
 #define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
@@ -58,10 +66,6 @@ static const UChar gSemiPercent[] =
 #define kHalfMaxDouble (double)(1 << kSomeNumberOfBitsDiv2)
 #define kMaxDouble (kHalfMaxDouble * kHalfMaxDouble)
 
-// Temporary workaround - when noParse is true, do noting in parse.
-// TODO: We need a real fix - see #6895/#6896
-static const char *NO_SPELLOUT_PARSE_LANGUAGES[] = { "ga", NULL };
-
 U_NAMESPACE_BEGIN
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
@@ -75,7 +79,7 @@ The RTTI code was also removed due to lack of code coverage.
 */
 class LocalizationInfo : public UMemory {
 protected:
-    virtual ~LocalizationInfo() {};
+    virtual ~LocalizationInfo();
     uint32_t refcount;
     
 public:
@@ -109,6 +113,8 @@ public:
 //    static UClassID getStaticClassID(void);
 };
 
+LocalizationInfo::~LocalizationInfo() {}
+
 //UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
 
 // if both strings are NULL, this returns TRUE
@@ -310,13 +316,33 @@ public:
     
 private:
     
-    void inc(void) { ++p; ch = 0xffff; }
-    UBool checkInc(UChar c) { if (p < e && (ch == c || *p == c)) { inc(); return TRUE; } return FALSE; }
-    UBool check(UChar c) { return p < e && (ch == c || *p == c); }
-    void skipWhitespace(void) { while (p < e && uprv_isRuleWhiteSpace(ch != 0xffff ? ch : *p)) inc();}
-    UBool inList(UChar c, const UChar* list) const {
-        if (*list == SPACE && uprv_isRuleWhiteSpace(c)) return TRUE;
-        while (*list && *list != c) ++list; return *list == c;
+    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);
     
@@ -326,10 +352,12 @@ private:
     UChar*  nextString(void);
 };
 
-#ifdef DEBUG
+#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
         
 
@@ -527,8 +555,8 @@ LocDataParser::nextString() {
     return result;
 }
 
-void
-LocDataParser::parseError(const char* /*str*/) {
+void LocDataParser::parseError(const char* EXPLANATION_ARG)
+{
     if (!data) {
         return;
     }
@@ -553,14 +581,14 @@ LocDataParser::parseError(const char* /*str*/) {
     pe.postContext[limit-p] = 0;
     pe.offset = (int32_t)(p - data);
     
-#ifdef DEBUG
-    fprintf(stderr, "%s at or near character %d: ", str, 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("'");
+    msg.append(UNICODE_STRING_SIMPLE("'"));
     
     char buf[128];
     int32_t len = msg.extract(0, msg.length(), buf, 128);
@@ -651,14 +679,22 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                                              const UnicodeString& locs,
                                              const Locale& alocale, UParseError& perror, UErrorCode& status)
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
-  , noParse(FALSE) //TODO: to be removed after #6895
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
   LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
   init(description, locinfo, perror, status);
@@ -668,14 +704,22 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                                              const UnicodeString& locs,
                                              UParseError& perror, UErrorCode& status)
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(Locale::getDefault())
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
-  , noParse(FALSE) //TODO: to be removed after #6895
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
   LocalizationInfo* locinfo = StringLocalizationInfo::create(locs, perror, status);
   init(description, locinfo, perror, status);
@@ -685,14 +729,22 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                                              LocalizationInfo* info,
                                              const Locale& alocale, UParseError& perror, UErrorCode& status)
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
-  , noParse(FALSE) //TODO: to be removed after #6895
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
   init(description, info, perror, status);
 }
@@ -701,14 +753,22 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                          UParseError& perror, 
                          UErrorCode& status) 
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(Locale::getDefault())
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
-  , noParse(FALSE) //TODO: to be removed after #6895
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     init(description, NULL, perror, status);
 }
@@ -718,27 +778,44 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                          UParseError& perror, 
                          UErrorCode& status) 
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(aLocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
-  , noParse(FALSE) //TODO: to be removed after #6895
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     init(description, NULL, perror, status);
 }
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     if (U_FAILURE(status)) {
         return;
@@ -757,7 +834,6 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
     // TODO: read localization info from resource
     LocalizationInfo* locinfo = NULL;
 
-    int32_t len = 0;
     UResourceBundle* nfrb = ures_open(U_ICUDATA_RBNF, locale.getName(), &status);
     if (U_SUCCESS(status)) {
         setLocaleIDs(ures_getLocaleByType(nfrb, ULOC_VALID_LOCALE, &status),
@@ -773,29 +849,14 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
             ures_close(nfrb);
             return;
         }
-        
+
         UnicodeString desc;
         while (ures_hasNext(ruleSets)) {
-           const UChar* currentString = ures_getNextString(ruleSets,&len,NULL,&status);
-           desc.append(currentString);
+           desc.append(ures_getNextUnicodeString(ruleSets,NULL,&status));
         }
         UParseError perror;
-        
 
-        init (desc, locinfo, perror, status);
-
-        //TODO: we need a real fix - see #6895 / #6896
-        noParse = FALSE;
-        if (tag == URBNF_SPELLOUT) {
-            const char *lang = alocale.getLanguage();
-            for (int32_t i = 0; NO_SPELLOUT_PARSE_LANGUAGES[i] != NULL; i++) {
-                if (uprv_strcmp(lang, NO_SPELLOUT_PARSE_LANGUAGES[i]) == 0) {
-                    noParse = TRUE;
-                    break;
-                }
-            }
-        }
-        //TODO: end
+        init(desc, locinfo, perror, status);
 
         ures_close(ruleSets);
         ures_close(rbnfRules);
@@ -806,13 +867,22 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
 RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
   : NumberFormat(rhs)
   , ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(rhs.locale)
   , collator(NULL)
   , decimalFormatSymbols(NULL)
+  , defaultInfinityRule(NULL)
+  , defaultNaNRule(NULL)
+  , roundingMode(DecimalFormat::ERoundingMode::kRoundUnnecessary)
   , lenient(FALSE)
   , lenientParseRules(NULL)
   , localizations(NULL)
+  , capitalizationInfoSet(FALSE)
+  , capitalizationForUIListMenu(FALSE)
+  , capitalizationForStandAlone(FALSE)
+  , capitalizationBrkIter(NULL)
 {
     this->operator=(rhs);
 }
@@ -822,17 +892,27 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(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;
     lenient = rhs.lenient;
 
-    UnicodeString rules = rhs.getRules();
     UParseError perror;
-    init(rules, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
-
-    //TODO: remove below when we fix the parse bug - See #6895 / #6896
-    noParse = rhs.noParse;
+    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;
 }
@@ -845,26 +925,7 @@ RuleBasedNumberFormat::~RuleBasedNumberFormat()
 Format*
 RuleBasedNumberFormat::clone(void) const
 {
-    RuleBasedNumberFormat * result = NULL;
-    UnicodeString rules = getRules();
-    UErrorCode status = U_ZERO_ERROR;
-    UParseError perror;
-    result = new RuleBasedNumberFormat(rules, localizations, 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;
-
-        //TODO: remove below when we fix the parse bug - See #6895 / #6896
-        result->noParse = noParse;
-    }
-    return result;
+    return new RuleBasedNumberFormat(*this);
 }
 
 UBool
@@ -876,6 +937,9 @@ RuleBasedNumberFormat::operator==(const Format& other) const
 
     if (typeid(*this) == typeid(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 &&
             lenient == rhs.lenient &&
             (localizations == NULL 
@@ -918,9 +982,10 @@ UnicodeString
 RuleBasedNumberFormat::getRuleSetName(int32_t index) const
 {
     if (localizations) {
-      UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);
-      return string;
-    } else if (ruleSets) {
+        UnicodeString string(TRUE, localizations->getRuleSetName(index), (int32_t)-1);
+        return string;
+    }
+    else if (ruleSets) {
         UnicodeString result;
         for (NFRuleSet** p = ruleSets; *p; ++p) {
             NFRuleSet* rs = *p;
@@ -941,8 +1006,9 @@ RuleBasedNumberFormat::getNumberOfRuleSetNames() const
 {
     int32_t result = 0;
     if (localizations) {
-      result = localizations->getNumberOfRuleSets();
-    } else if (ruleSets) {
+        result = localizations->getNumberOfRuleSets();
+    }
+    else if (ruleSets) {
         for (NFRuleSet** p = ruleSets; *p; ++p) {
             if ((**p).isPublic()) {
                 ++result;
@@ -1042,13 +1108,77 @@ RuleBasedNumberFormat::findRuleSet(const UnicodeString& name, UErrorCode& status
     return NULL;
 }
 
+UnicodeString&
+RuleBasedNumberFormat::format(const DigitList &number,
+                      UnicodeString &appendTo,
+                      FieldPositionIterator *posIter,
+                      UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    DigitList copy(number);
+    if (copy.fitsIntoInt64(false)) {
+        format(((DigitList &)number).getInt64(), appendTo, posIter, status);
+    }
+    else {
+        copy.roundAtExponent(0);
+        if (copy.fitsIntoInt64(false)) {
+            format(number.getDouble(), 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.
+            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
+            Formattable f;
+            f.adoptDigitList(new DigitList(number));
+            decimalFormat->format(f, appendTo, posIter, status);
+            delete decimalFormat;
+        }
+    }
+    return appendTo;
+}
+
+
+UnicodeString&
+RuleBasedNumberFormat::format(const DigitList &number,
+                     UnicodeString& appendTo,
+                     FieldPosition& pos,
+                     UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    DigitList copy(number);
+    if (copy.fitsIntoInt64(false)) {
+        format(((DigitList &)number).getInt64(), appendTo, pos, status);
+    }
+    else {
+        copy.roundAtExponent(0);
+        if (copy.fitsIntoInt64(false)) {
+            format(number.getDouble(), 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.
+            NumberFormat *decimalFormat = NumberFormat::createInstance(locale, UNUM_DECIMAL, status);
+            Formattable f;
+            f.adoptDigitList(new DigitList(number));
+            decimalFormat->format(f, appendTo, pos, status);
+            delete decimalFormat;
+        }
+    }
+    return appendTo;
+}
+
 UnicodeString&
 RuleBasedNumberFormat::format(int32_t number,
                               UnicodeString& toAppendTo,
-                              FieldPosition& /* pos */) const
+                              FieldPosition& pos) const
 {
-    if (defaultRuleSet) defaultRuleSet->format((int64_t)number, toAppendTo, toAppendTo.length());
-    return toAppendTo;
+    return format((int64_t)number, toAppendTo, pos);
 }
 
 
@@ -1057,7 +1187,10 @@ RuleBasedNumberFormat::format(int64_t number,
                               UnicodeString& toAppendTo,
                               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;
 }
 
@@ -1067,14 +1200,9 @@ RuleBasedNumberFormat::format(double number,
                               UnicodeString& toAppendTo,
                               FieldPosition& /* pos */) const
 {
-    // Special case for NaN; adapted from what DecimalFormat::_format( double number,...) does.
-    if (uprv_isNaN(number)) {
-        DecimalFormatSymbols* decFmtSyms = getDecimalFormatSymbols(); // RuleBasedNumberFormat internal
-        if (decFmtSyms) {
-            toAppendTo += decFmtSyms->getConstSymbol(DecimalFormatSymbols::kNaNSymbol);
-        }
-    } else if (defaultRuleSet) {
-        defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+    UErrorCode status = U_ZERO_ERROR;
+    if (defaultRuleSet) {
+        format(number, *defaultRuleSet, toAppendTo, status);
     }
     return toAppendTo;
 }
@@ -1082,20 +1210,30 @@ RuleBasedNumberFormat::format(double number,
 
 UnicodeString&
 RuleBasedNumberFormat::format(int32_t number,
+                              const UnicodeString& ruleSetName,
+                              UnicodeString& toAppendTo,
+                              FieldPosition& pos,
+                              UErrorCode& status) const
+{
+    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
 {
-    // return format((int64_t)number, ruleSetName, toAppendTo, pos, 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) {
-                rs->format((int64_t)number, toAppendTo, toAppendTo.length());
+                format(number, rs, toAppendTo, status);
             }
         }
     }
@@ -1104,70 +1242,118 @@ RuleBasedNumberFormat::format(int32_t number,
 
 
 UnicodeString&
-RuleBasedNumberFormat::format(int64_t number,
+RuleBasedNumberFormat::format(double number,
                               const UnicodeString& ruleSetName,
                               UnicodeString& toAppendTo,
                               FieldPosition& /* pos */,
                               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) {
-                rs->format(number, toAppendTo, toAppendTo.length());
+                format(number, *rs, toAppendTo, status);
             }
         }
     }
     return toAppendTo;
 }
 
-
-// make linker happy
-UnicodeString&
-RuleBasedNumberFormat::format(const Formattable& obj,
+void
+RuleBasedNumberFormat::format(double number,
+                              NFRuleSet& rs,
                               UnicodeString& toAppendTo,
-                              FieldPosition& pos,
                               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)) {
+        DigitList digitList;
+        digitList.set(number);
+        digitList.setRoundingMode(getRoundingMode());
+        digitList.roundFixedPoint(getMaximumFractionDigits());
+        number = digitList.getDouble();
+    }
+    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&
-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 (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);
+            Formattable f;
+            FieldPosition pos(FieldPosition::DONT_CARE);
+            DigitList *digitList = new DigitList();
+            digitList->set(number);
+            f.adoptDigitList(digitList);
+            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;
 }
 
+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
 {
-    //TODO: We need a real fix.  See #6895 / #6896
-    if (noParse) {
-        // skip parsing
-        parsePosition.setErrorIndex(0);
-        return;
-    }
-
     if (!ruleSets) {
         parsePosition.setErrorIndex(0);
         return;
@@ -1185,7 +1371,7 @@ RuleBasedNumberFormat::parse(const UnicodeString& text,
             ParsePosition working_pp(0);
             Formattable working_result;
 
-            rp->parse(workingText, working_pp, kMaxDouble, working_result, lenient);
+            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;
@@ -1207,9 +1393,12 @@ RuleBasedNumberFormat::parse(const UnicodeString& text,
     }
     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));
         }
     }
 }
@@ -1251,13 +1440,13 @@ RuleBasedNumberFormat::setDefaultRuleSet(const UnicodeString& ruleSetName, UErro
 
 UnicodeString
 RuleBasedNumberFormat::getDefaultRuleSetName() const {
-  UnicodeString result;
-  if (defaultRuleSet && defaultRuleSet->isPublic()) {
-    defaultRuleSet->getName(result);
-  } else {
-    result.setToBogus();
-  }
-  return result;
+    UnicodeString result;
+    if (defaultRuleSet && defaultRuleSet->isPublic()) {
+        defaultRuleSet->getName(result);
+    } else {
+        result.setToBogus();
+    }
+    return result;
 }
 
 void 
@@ -1265,12 +1454,12 @@ RuleBasedNumberFormat::initDefaultRuleSet()
 {
     defaultRuleSet = NULL;
     if (!ruleSets) {
-      return;
+        return;
     }
 
-    const UnicodeString spellout = UNICODE_STRING_SIMPLE("%spellout-numbering");
-    const UnicodeString ordinal = UNICODE_STRING_SIMPLE("%digits-ordinal");
-    const UnicodeString duration = UNICODE_STRING_SIMPLE("%duration");
+    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 = &ruleSets[0];
     while (*p) {
@@ -1305,6 +1494,13 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
         return;
     }
 
+    initializeDecimalFormatSymbols(status);
+    initializeDefaultInfinityRule(status);
+    initializeDefaultNaNRule(status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
     this->localizations = localizationInfos == NULL ? NULL : localizationInfos->ref();
 
     UnicodeString description(rules);
@@ -1324,7 +1520,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     // 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
@@ -1333,13 +1529,13 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
             // 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);
-            while (uprv_isRuleWhiteSpace(description.charAt(lpStart))) {
+            while (PatternProps::isWhiteSpace(description.charAt(lpStart))) {
                 ++lpStart;
             }
 
@@ -1360,8 +1556,8 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     // 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;
     }
@@ -1390,7 +1586,8 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return;
     }
-    UnicodeString* ruleSetDescriptions = new UnicodeString[numRuleSets];
+
+    ruleSetDescriptions = new UnicodeString[numRuleSets];
     if (ruleSetDescriptions == 0) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
@@ -1399,21 +1596,21 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     {
         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);
-            ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
+            ruleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
             if (ruleSets[curRuleSet] == 0) {
                 status = U_MEMORY_ALLOCATION_ERROR;
-                goto cleanup;
+                return;
             }
             ++curRuleSet;
             start = p + 1;
         }
         ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
-        ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
+        ruleSets[curRuleSet] = new NFRuleSet(this, ruleSetDescriptions, curRuleSet, status);
         if (ruleSets[curRuleSet] == 0) {
             status = U_MEMORY_ALLOCATION_ERROR;
-            goto cleanup;
+            return;
         }
     }
 
@@ -1433,7 +1630,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     // away the temporary descriptions as we go)
     {
         for (int i = 0; i < numRuleSets; i++) {
-            ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status);
+            ruleSets[i]->parseRules(ruleSetDescriptions[i], status);
         }
     }
 
@@ -1460,9 +1657,55 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     } else {
         defaultRuleSet = getDefaultRuleSet();
     }
+    originalDescription = rules;
+}
 
-cleanup:
-    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)) ) {
+            UErrorCode 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 = intVector[0];
+            capitalizationForStandAlone = intVector[1];
+        }
+    }
+    ures_close(rb);
+#endif
 }
 
 void
@@ -1475,7 +1718,7 @@ RuleBasedNumberFormat::stripWhitespace(UnicodeString& description)
     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;
         }
 
@@ -1517,6 +1760,11 @@ RuleBasedNumberFormat::dispose()
         ruleSets = NULL;
     }
 
+    if (ruleSetDescriptions) {
+        delete [] ruleSetDescriptions;
+        ruleSetDescriptions = NULL;
+    }
+
 #if !UCONFIG_NO_COLLATION
     delete collator;
 #endif
@@ -1525,10 +1773,23 @@ RuleBasedNumberFormat::dispose()
     delete decimalFormatSymbols;
     decimalFormatSymbols = NULL;
 
+    delete defaultInfinityRule;
+    defaultInfinityRule = NULL;
+
+    delete defaultNaNRule;
+    defaultNaNRule = NULL;
+
     delete lenientParseRules;
     lenientParseRules = NULL;
 
-    if (localizations) localizations = localizations->unref();
+#if !UCONFIG_NO_BREAK_ITERATION
+    delete capitalizationBrkIter;
+    capitalizationBrkIter = NULL;
+#endif
+
+    if (localizations) {
+        localizations = localizations->unref();
+    }
 }
 
 
@@ -1542,7 +1803,7 @@ RuleBasedNumberFormat::dispose()
  * @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
@@ -1550,7 +1811,7 @@ RuleBasedNumberFormat::getCollator() const
         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
@@ -1569,7 +1830,7 @@ RuleBasedNumberFormat::getCollator() const
                 newCollator = new RuleBasedCollator(rules, status);
                 // Exit if newCollator could not be created.
                 if (newCollator == NULL) {
-                       return NULL;
+                    return NULL;
                 }
             } else {
                 temp = NULL;
@@ -1592,31 +1853,152 @@ 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*
-RuleBasedNumberFormat::getDecimalFormatSymbols() const
+RuleBasedNumberFormat::initializeDecimalFormatSymbols(UErrorCode &status)
 {
     // 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 (U_SUCCESS(status)) {
-            ((RuleBasedNumberFormat*)this)->decimalFormatSymbols = temp;
-        } else {
+            decimalFormatSymbols = temp;
+        }
+        else {
             delete temp;
         }
     }
     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 NULL;
+    }
+    if (defaultInfinityRule == NULL) {
+        UnicodeString rule(UNICODE_STRING_SIMPLE("Inf: "));
+        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kInfinitySymbol));
+        NFRule* temp = new NFRule(this, rule, status);
+        if (U_SUCCESS(status)) {
+            defaultInfinityRule = temp;
+        }
+        else {
+            delete temp;
+        }
+    }
+    return defaultInfinityRule;
+}
+
+const NFRule*
+RuleBasedNumberFormat::getDefaultInfinityRule() const
+{
+    return defaultInfinityRule;
+}
+
+NFRule*
+RuleBasedNumberFormat::initializeDefaultNaNRule(UErrorCode &status)
+{
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    if (defaultNaNRule == NULL) {
+        UnicodeString rule(UNICODE_STRING_SIMPLE("NaN: "));
+        rule.append(getDecimalFormatSymbols()->getSymbol(DecimalFormatSymbols::kNaNSymbol));
+        NFRule* temp = new NFRule(this, rule, status);
+        if (U_SUCCESS(status)) {
+            defaultNaNRule = temp;
+        }
+        else {
+            delete temp;
+        }
+    }
+    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 (ruleSets) {
+            for (int32_t i = 0; i < numRuleSets; i++) {
+                ruleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
+            }
+        }
+    }
+}
+
+// Setting the symbols is equlivalent 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
+{
+    return new PluralFormat(locale, pluralType, pattern, status);
+}
+
+/**
+ * Get the rounding mode.
+ * @return A rounding mode
+ */
+DecimalFormat::ERoundingMode RuleBasedNumberFormat::getRoundingMode() const {
+    return roundingMode;
+}
+
+/**
+ * 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) {
+    this->roundingMode = roundingMode;
+}
+
 U_NAMESPACE_END
 
 /* U_HAVE_RBNF */