]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/rbnf.cpp
ICU-491.11.2.tar.gz
[apple/icu.git] / icuSources / i18n / rbnf.cpp
index ad2d71b6c8477634d2bc44eb34fa1bb36db8d963..860b92aa8581e2c10c99f74a71fe87cd1b67470d 100644 (file)
@@ -1,9 +1,12 @@
 /*
 *******************************************************************************
-* Copyright (C) 1997-2004, International Business Machines Corporation and others. All Rights Reserved.
+* Copyright (C) 1997-2012, International Business Machines Corporation
+* and others. All Rights Reserved.
 *******************************************************************************
 */
 
+#include <typeinfo>  // for 'typeid' to work
+
 #include "unicode/rbnf.h"
 
 #if U_HAVE_RBNF
@@ -22,7 +25,8 @@
 
 #include "cmemory.h"
 #include "cstring.h"
-#include "util.h"
+#include "patternprops.h"
+#include "uresimp.h"
 
 // debugging
 // #define DEBUG
@@ -58,9 +62,16 @@ U_NAMESPACE_BEGIN
 
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedNumberFormat)
 
-class LocalizationInfo : public UObject {
+/*
+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() {};
+    virtual ~LocalizationInfo();
     uint32_t refcount;
     
 public:
@@ -90,11 +101,13 @@ public:
     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);
+//    virtual UClassID getDynamicClassID() const = 0;
+//    static UClassID getStaticClassID(void);
 };
 
-UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
+LocalizationInfo::~LocalizationInfo() {}
+
+//UOBJECT_DEFINE_ABSTRACT_RTTI_IMPLEMENTATION(LocalizationInfo)
 
 // if both strings are NULL, this returns TRUE
 static UBool 
@@ -254,8 +267,8 @@ public:
     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);
+//    virtual UClassID getDynamicClassID() const;
+//    static UClassID getStaticClassID(void);
     
 private:
     void init(UErrorCode& status) const;
@@ -298,9 +311,9 @@ 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();}
+    void skipWhitespace(void) { while (p < e && PatternProps::isWhiteSpace(ch != 0xffff ? ch : *p)) inc();}
     UBool inList(UChar c, const UChar* list) const {
-        if (*list == SPACE && uprv_isRuleWhiteSpace(c)) return TRUE;
+        if (*list == SPACE && PatternProps::isWhiteSpace(c)) return TRUE;
         while (*list && *list != c) ++list; return *list == c;
     }
     void parseError(const char* msg);
@@ -519,21 +532,24 @@ LocDataParser::parseError(const char* /*str*/) {
     }
 
     const UChar* start = p - U_PARSE_CONTEXT_LEN - 1;
-    if (start < data)
+    if (start < data) {
         start = data;
-    for (UChar* x = p; --x >= start;)
+    }
+    for (UChar* x = p; --x >= start;) {
         if (!*x) {
             start = x+1;
             break;
         }
+    }
     const UChar* limit = p + U_PARSE_CONTEXT_LEN - 1;
-    if (limit > e)
+    if (limit > e) {
         limit = e;
-    u_strncpy(pe.preContext, start, p-start);
+    }
+    u_strncpy(pe.preContext, start, (int32_t)(p-start));
     pe.preContext[p-start] = 0;
-    u_strncpy(pe.postContext, p, limit-p);
+    u_strncpy(pe.postContext, p, (int32_t)(limit-p));
     pe.postContext[limit-p] = 0;
-    pe.offset = p - data;
+    pe.offset = (int32_t)(p - data);
     
 #ifdef DEBUG
     fprintf(stderr, "%s at or near character %d: ", str, p-data);
@@ -565,7 +581,7 @@ LocDataParser::parseError(const char* /*str*/) {
     }
 }
 
-UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
+//UOBJECT_DEFINE_RTTI_IMPLEMENTATION(StringLocalizationInfo)
 
 StringLocalizationInfo* 
 StringLocalizationInfo::create(const UnicodeString& info, UParseError& perror, UErrorCode& status) {
@@ -633,6 +649,8 @@ 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)
@@ -649,6 +667,8 @@ 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)
@@ -665,6 +685,8 @@ 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)
@@ -680,6 +702,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                          UParseError& perror, 
                          UErrorCode& status) 
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(Locale::getDefault())
   , collator(NULL)
@@ -696,6 +720,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
                          UParseError& perror, 
                          UErrorCode& status) 
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(aLocale)
   , collator(NULL)
@@ -709,6 +735,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
 
 RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale& alocale, UErrorCode& status)
   : ruleSets(NULL)
+  , ruleSetDescriptions(NULL)
+  , numRuleSets(0)
   , defaultRuleSet(NULL)
   , locale(alocale)
   , collator(NULL)
@@ -721,26 +749,45 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
         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;
+    case URBNF_NUMBERING_SYSTEM: fmt_tag = "NumberingSystemRules"; break;
     default: status = U_ILLEGAL_ARGUMENT_ERROR; return;
     }
 
     // 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),
                      ures_getLocaleByType(nfrb, ULOC_ACTUAL_LOCALE, &status));
-        const UChar* description = ures_getStringByKey(nfrb, fmt_tag, &len, &status);
-        UnicodeString desc(description, len);
+
+        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;
+
         init (desc, locinfo, perror, status);
+
+        ures_close(ruleSets);
+        ures_close(rbnfRules);
     }
     ures_close(nfrb);
 }
@@ -748,6 +795,8 @@ 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)
@@ -810,7 +859,7 @@ RuleBasedNumberFormat::operator==(const Format& other) const
         return TRUE;
     }
 
-    if (other.getDynamicClassID() == getStaticClassID()) {
+    if (typeid(*this) == typeid(other)) {
         const RuleBasedNumberFormat& rhs = (const RuleBasedNumberFormat&)other;
         if (locale == rhs.locale &&
             lenient == rhs.lenient &&
@@ -899,7 +948,7 @@ RuleBasedNumberFormat::getNumberOfRuleSetDisplayNameLocales(void) const {
 Locale 
 RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& status) const {
     if (U_FAILURE(status)) {
-        return Locale();
+        return Locale("");
     }
     if (localizations && index >= 0 && index < localizations->getNumberOfDisplayLocales()) {
         UnicodeString name(TRUE, localizations->getLocaleName(index), -1);
@@ -910,7 +959,7 @@ RuleBasedNumberFormat::getRuleSetDisplayNameLocale(int32_t index, UErrorCode& st
             bp = (char *)uprv_malloc(cap);
             if (bp == NULL) {
                 status = U_MEMORY_ALLOCATION_ERROR;
-                return Locale();
+                return Locale("");
             }
         }
         name.extract(0, name.length(), bp, cap, UnicodeString::kInvariant);
@@ -1003,7 +1052,15 @@ RuleBasedNumberFormat::format(double number,
                               UnicodeString& toAppendTo,
                               FieldPosition& /* pos */) const
 {
-    if (defaultRuleSet) defaultRuleSet->format(number, toAppendTo, toAppendTo.length());
+    // 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());
+    }
     return toAppendTo;
 }
 
@@ -1017,7 +1074,7 @@ RuleBasedNumberFormat::format(int32_t number,
 {
     // 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 {
@@ -1039,7 +1096,7 @@ RuleBasedNumberFormat::format(int64_t number,
                               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 {
@@ -1071,7 +1128,7 @@ RuleBasedNumberFormat::format(double number,
                               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 {
@@ -1102,11 +1159,11 @@ RuleBasedNumberFormat::parse(const UnicodeString& text,
 
     for (NFRuleSet** p = ruleSets; *p; ++p) {
         NFRuleSet *rp = *p;
-        if (rp->isPublic()) {
+        if (rp->isPublic() && rp->isParseable()) {
             ParsePosition working_pp(0);
             Formattable working_result;
 
-            rp->parse(workingText, working_pp, kMaxDouble, working_result);
+            rp->parse(workingText, working_pp, kMaxDouble, working_result, lenient);
             if (working_pp.getIndex() > high_pp.getIndex()) {
                 high_pp = working_pp;
                 high_result = working_result;
@@ -1118,9 +1175,13 @@ RuleBasedNumberFormat::parse(const UnicodeString& text,
         }
     }
 
-    parsePosition.setIndex(parsePosition.getIndex() + high_pp.getIndex());
+    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);
     }
     result = high_result;
     if (result.getType() == Formattable::kDouble) {
@@ -1185,9 +1246,18 @@ RuleBasedNumberFormat::initDefaultRuleSet()
       return;
     }
 
+    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) {
-        ++p;
+        if ((*p)->isNamed(spellout) || (*p)->isNamed(ordinal) || (*p)->isNamed(duration)) {
+            defaultRuleSet = *p;
+            return;
+        } else {
+            ++p;
+        }
     }
 
     defaultRuleSet = *--p;
@@ -1204,9 +1274,10 @@ RuleBasedNumberFormat::initDefaultRuleSet()
 
 void
 RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* localizationInfos,
-                            UParseError& /* pErr */, UErrorCode& status)
+                            UParseError& pErr, UErrorCode& status)
 {
     // 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;
@@ -1231,7 +1302,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
@@ -1240,13 +1311,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;
             }
 
@@ -1267,8 +1338,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;
     }
@@ -1294,11 +1365,11 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
     // 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 */
+
+    ruleSetDescriptions = new UnicodeString[numRuleSets];
     if (ruleSetDescriptions == 0) {
         status = U_MEMORY_ALLOCATION_ERROR;
         return;
@@ -1307,10 +1378,9 @@ 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);
-            /* test for NULL */
             if (ruleSets[curRuleSet] == 0) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
@@ -1320,7 +1390,6 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
         }
         ruleSetDescriptions[curRuleSet].setTo(description, start, description.length() - start);
         ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet, status);
-        /* test for NULL */
         if (ruleSets[curRuleSet] == 0) {
             status = U_MEMORY_ALLOCATION_ERROR;
             return;
@@ -1347,8 +1416,6 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
         }
     }
 
-    delete[] ruleSetDescriptions;
-
     // Now that the rules are initialized, the 'real' default rule
     // set can be adjusted by the localization data.
 
@@ -1384,7 +1451,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;
         }
 
@@ -1426,6 +1493,10 @@ RuleBasedNumberFormat::dispose()
         ruleSets = NULL;
     }
 
+    if (ruleSetDescriptions) {
+        delete [] ruleSetDescriptions;
+    }
+
 #if !UCONFIG_NO_COLLATION
     delete collator;
 #endif
@@ -1469,15 +1540,17 @@ RuleBasedNumberFormat::getCollator() const
         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);
+                // Exit if newCollator could not be created.
+                if (newCollator == NULL) {
+                       return NULL;
+                }
             } else {
                 temp = NULL;
             }
@@ -1524,6 +1597,37 @@ RuleBasedNumberFormat::getDecimalFormatSymbols() const
     return decimalFormatSymbols;
 }
 
+// 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;
+
+        for (int32_t i = 0; i < numRuleSets; i++) {
+            ruleSets[i]->parseRules(ruleSetDescriptions[i], this, status);
+        }
+    }
+}
+
+// Setting the symbols is equlivalent to adopting a newly created localized symbols.
+void
+RuleBasedNumberFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols)
+{
+    adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols));
+}
+
 U_NAMESPACE_END
 
 /* U_HAVE_RBNF */