]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/nfrs.cpp
ICU-511.27.tar.gz
[apple/icu.git] / icuSources / i18n / nfrs.cpp
index 814a03aa7291af7734b000916485474b52590f4c..de42d8868e7e2400e9e0a29608984bb9669648a1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 ******************************************************************************
-*   Copyright (C) 1997-2001, International Business Machines
+*   Copyright (C) 1997-2012, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 ******************************************************************************
 *   file name:  nfrs.cpp
 #include "unicode/uchar.h"
 #include "nfrule.h"
 #include "nfrlist.h"
+#include "patternprops.h"
 
 #ifdef RBNF_DEBUG
 #include "cmemory.h"
 #endif
 
-#include "uprops.h"
-
 U_NAMESPACE_BEGIN
 
 #if 0
@@ -114,12 +113,19 @@ static const UChar gPercentPercent[] =
     0x25, 0x25, 0
 }; /* "%%" */
 
+static const UChar gNoparse[] =
+{
+    0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0
+}; /* "@noparse" */
+
 NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& status)
   : name()
   , rules(0)
   , negativeNumberRule(NULL)
   , fIsFractionRuleSet(FALSE)
   , fIsPublic(FALSE)
+  , fIsParseable(TRUE)
+  , fRecursionCount(0)
 {
     for (int i = 0; i < 3; ++i) {
         fractionRules[i] = NULL;
@@ -131,6 +137,12 @@ NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& sta
 
     UnicodeString& description = descriptions[index]; // !!! make sure index is valid
 
+    if (description.length() == 0) {
+        // throw new IllegalArgumentException("Empty rule set description");
+        status = U_PARSE_ERROR;
+        return;
+    }
+
     // if the description begins with a rule set name (the rule set
     // name can be omitted in formatter descriptions that consist
     // of only one rule set), copy it out into our "name" member
@@ -142,12 +154,12 @@ NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& sta
             status = U_PARSE_ERROR;
         } else {
             name.setTo(description, 0, pos);
-            while (pos < description.length() && uprv_isRuleWhiteSpace(description.charAt(++pos))) {
+            while (pos < description.length() && PatternProps::isWhiteSpace(description.charAt(++pos))) {
             }
             description.remove(0, pos);
         }
     } else {
-        name.setTo("%default");
+        name.setTo(UNICODE_STRING_SIMPLE("%default"));
     }
 
     if (description.length() == 0) {
@@ -155,7 +167,12 @@ NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& sta
         status = U_PARSE_ERROR;
     }
 
-    fIsPublic = name.indexOf(gPercentPercent) != 0;
+    fIsPublic = name.indexOf(gPercentPercent, 2, 0) != 0;
+
+    if ( name.endsWith(gNoparse,8) ) {
+        fIsParseable = FALSE;
+        name.truncate(name.length()-8); // remove the @noparse from the name
+    }
 
     // all of the other members of NFRuleSet are initialized
     // by parseRules()
@@ -173,6 +190,9 @@ NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* o
         return;
     }
 
+    // ensure we are starting with an empty rule list
+    rules.deleteAll();
+
     // dlf - the original code kept a separate description array for no reason,
     // so I got rid of it.  The loop was too complex so I simplified it.
 
@@ -208,7 +228,7 @@ NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* o
             // same as the preceding rule's base value in fraction
             // rule sets)
         case NFRule::kNoBase:
-            rule->setBaseValue(defaultBaseValue);
+            rule->setBaseValue(defaultBaseValue, status);
             if (!isFractionRuleSet()) {
                 ++defaultBaseValue;
             }
@@ -218,24 +238,36 @@ NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* o
             // if it's the negative-number rule, copy it into its own
             // data member and delete it from the list
         case NFRule::kNegativeNumberRule:
+            if (negativeNumberRule) {
+                delete negativeNumberRule;
+            }
             negativeNumberRule = rules.remove(i);
             break;
 
             // if it's the improper fraction rule, copy it into the
             // correct element of fractionRules
         case NFRule::kImproperFractionRule:
+            if (fractionRules[0]) {
+                delete fractionRules[0];
+            }
             fractionRules[0] = rules.remove(i);
             break;
 
             // if it's the proper fraction rule, copy it into the
             // correct element of fractionRules
         case NFRule::kProperFractionRule:
+            if (fractionRules[1]) {
+                delete fractionRules[1];
+            }
             fractionRules[1] = rules.remove(i);
             break;
 
             // if it's the master rule, copy it into the
             // correct element of fractionRules
         case NFRule::kMasterRule:
+            if (fractionRules[2]) {
+                delete fractionRules[2];
+            }
             fractionRules[2] = rules.remove(i);
             break;
 
@@ -266,7 +298,7 @@ NFRuleSet::~NFRuleSet()
     delete fractionRules[2];
 }
 
-UBool
+static UBool
 util_equalRules(const NFRule* rule1, const NFRule* rule2)
 {
     if (rule1) {
@@ -300,18 +332,38 @@ NFRuleSet::operator==(const NFRuleSet& rhs) const
     return FALSE;
 }
 
+#define RECURSION_LIMIT 50
+
 void
 NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos) const
 {
     NFRule *rule = findNormalRule(number);
-    rule->doFormat(number, toAppendTo, pos);
+    if (rule) { // else error, but can't report it
+        NFRuleSet* ncThis = (NFRuleSet*)this;
+        if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) {
+            // stop recursion
+            ncThis->fRecursionCount = 0;
+        } else {
+            rule->doFormat(number, toAppendTo, pos);
+            ncThis->fRecursionCount--;
+        }
+    }
 }
 
 void
 NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos) const
 {
     NFRule *rule = findDoubleRule(number);
-    rule->doFormat(number, toAppendTo, pos);
+    if (rule) { // else error, but can't report it
+        NFRuleSet* ncThis = (NFRuleSet*)this;
+        if (ncThis->fRecursionCount++ >= RECURSION_LIMIT) {
+            // stop recursion
+            ncThis->fRecursionCount = 0;
+        } else {
+            rule->doFormat(number, toAppendTo, pos);
+            ncThis->fRecursionCount--;
+        }
+    }
 }
 
 NFRule*
@@ -351,6 +403,14 @@ NFRuleSet::findDoubleRule(double number) const
         return fractionRules[2];
     }
 
+    // always use the last rule for infinity.  It is likely that rule
+    // has a DecimalFormat that will do the right thing with infinity even
+    // if the rule's base value is strange, i.e. something larger than what 
+    // util64_fromDouble produces below.
+    if (uprv_isInfinite(number) && (rules.size() > 0)) {
+        return rules[rules.size() - 1];
+    }
+
     // and if we haven't yet returned a rule, use findNormalRule()
     // to find the applicable rule
     int64_t r = util64_fromDouble(number + 0.5);
@@ -408,6 +468,10 @@ NFRuleSet::findNormalRule(int64_t number) const
                 lo = mid + 1;
             }
         }
+        if (hi == 0) { // bad rule set, minimum base > 0
+            return NULL; // want to throw exception here
+        }
+
         NFRule *result = rules[hi - 1];
 
         // use shouldRollBack() to see whether we need to invoke the
@@ -416,6 +480,9 @@ NFRuleSet::findNormalRule(int64_t number) const
         // one rule and return that one instead of the one we'd normally
         // return
         if (result->shouldRollBack((double)number)) {
+            if (hi == 1) { // bad rule set, no prior rule to rollback to from this base
+                return NULL;
+            }
             result = rules[hi - 2];
         }
         return result;
@@ -538,15 +605,17 @@ NFRuleSet::findFractionRuleSetRule(double number) const
 static void dumpUS(FILE* f, const UnicodeString& us) {
   int len = us.length();
   char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1];
-  us.extract(0, len, buf);
-  buf[len] = 0;
-  fprintf(f, "%s", buf);
-  uprv_free(buf); //delete[] buf;
+  if (buf != NULL) {
+         us.extract(0, len, buf);
+         buf[len] = 0;
+         fprintf(f, "%s", buf);
+         uprv_free(buf); //delete[] buf;
+  }
 }
 #endif
 
 UBool
-NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result) const
+NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, Formattable& result, UBool lenient) const
 {
     // try matching each rule in the rule set against the text being
     // parsed.  Whichever one matches the most characters is the one
@@ -597,7 +666,7 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
         for (int i = 0; i < 3; i++) {
             if (fractionRules[i]) {
                 Formattable tempResult;
-                UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult);
+                UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult, lenient || isDecimalFormatRuleParseable() );
                 if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
                     result = tempResult;
                     highWaterMark = workingPos;
@@ -666,23 +735,23 @@ NFRuleSet::appendRules(UnicodeString& result) const
 
     // followed by the regular rules...
     for (uint32_t i = 0; i < rules.size(); i++) {
-        result.append(gFourSpaces);
-        rules[i]->appendRuleText(result);
+        result.append(gFourSpaces, 4);
+        rules[i]->_appendRuleText(result);
         result.append(gLineFeed);
     }
 
     // followed by the special rules (if they exist)
     if (negativeNumberRule) {
-        result.append(gFourSpaces);
-        negativeNumberRule->appendRuleText(result);
+        result.append(gFourSpaces, 4);
+        negativeNumberRule->_appendRuleText(result);
         result.append(gLineFeed);
     }
 
     {
         for (uint32_t i = 0; i < 3; ++i) {
             if (fractionRules[i]) {
-                result.append(gFourSpaces);
-                fractionRules[i]->appendRuleText(result);
+                result.append(gFourSpaces, 4);
+                fractionRules[i]->_appendRuleText(result);
                 result.append(gLineFeed);
             }
         }
@@ -736,6 +805,7 @@ static const uint8_t asciiDigits[] = {
 
 static const UChar kUMinus = (UChar)0x002d;
 
+#ifdef RBNF_DEBUG
 static const char kMinus = '-';
 
 static const uint8_t digitInfo[] = {
@@ -757,7 +827,6 @@ static const uint8_t digitInfo[] = {
     0xa1u, 0xa2u, 0xa3u,     0,     0,     0,     0,     0,
 };
 
-#ifdef RBNF_DEBUG
 int64_t util64_atoi(const char* str, uint32_t radix)
 {
     if (radix > 36) {
@@ -783,7 +852,6 @@ int64_t util64_atoi(const char* str, uint32_t radix)
     }
     return result;
 }
-#endif
 
 int64_t util64_utoi(const UChar* str, uint32_t radix)
 {
@@ -812,7 +880,6 @@ int64_t util64_utoi(const UChar* str, uint32_t radix)
     return result;
 }
 
-#ifdef RBNF_DEBUG
 uint32_t util64_toa(int64_t w, char* buf, uint32_t len, uint32_t radix, UBool raw)
 {    
     if (radix > 36) {