ICU-8223 Add rule syntax for NaN, infinity and alternate decimal points.

Other issues addressed:
* Some performance enhancements were added for good measure. Creating new RuleBasedNumberFormat objects can take a long time due to all the rule parsing. This was ported from ICU4J.
* I fixed a potential infinite recursion problem when RuleBasedNumberFormat used NumberFormat.createInstance, which could occasionally depend on creating RuleBasedNumberFormat for itself, which was bad. This was ported from ICU4J.
* I fixed a potential memory leak due to lazy initialization of some RBNF data members in a multithreaded environment, which is fine in Java, but it's not okay in C++. We no longer cast away const due to this, which is good.
* There were some compiler warnings and errors found while trying to debug this code on my machine. I fixed those too.

X-SVN-Rev: 37810
This commit is contained in:
George Rhoten 2015-08-25 16:55:52 +00:00
parent 056237d6db
commit e097e1b9ee
14 changed files with 735 additions and 446 deletions

View File

@ -135,8 +135,8 @@ static const UChar kFULLSTOP = 0x002E; // '.'
*/
class SimpleFilteredSentenceBreakData : public UMemory {
public:
LocalPointer<UCharsTrie> fBackwardsTrie; // i.e. ".srM" for Mrs.
LocalPointer<UCharsTrie> fForwardsPartialTrie; // Has ".a" for "a.M."
LocalPointer<UCharsTrie> fBackwardsTrie; // i.e. ".srM" for Mrs.
int32_t refcount;
SimpleFilteredSentenceBreakData(UCharsTrie *forwards, UCharsTrie *backwards )
: fForwardsPartialTrie(forwards), fBackwardsTrie(backwards), refcount(1) { }
@ -229,15 +229,15 @@ private:
};
SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(const SimpleFilteredSentenceBreakIterator& other)
: BreakIterator(other), fDelegate(other.fDelegate->clone()), fData(other.fData->incr())
: BreakIterator(other), fData(other.fData->incr()), fDelegate(other.fDelegate->clone())
{
}
SimpleFilteredSentenceBreakIterator::SimpleFilteredSentenceBreakIterator(BreakIterator *adopt, UCharsTrie *forwards, UCharsTrie *backwards, UErrorCode &status) :
BreakIterator(adopt->getLocale(ULOC_VALID_LOCALE,status),adopt->getLocale(ULOC_ACTUAL_LOCALE,status)),
fDelegate(adopt),
fData(new SimpleFilteredSentenceBreakData(forwards, backwards))
fData(new SimpleFilteredSentenceBreakData(forwards, backwards)),
fDelegate(adopt)
{
// all set..
}

View File

@ -26,6 +26,22 @@
#include "cmemory.h"
#endif
enum {
/** -x */
NEGATIVE_RULE_INDEX = 0,
/** x.x */
IMPROPER_FRACTION_RULE_INDEX = 1,
/** 0.x */
PROPER_FRACTION_RULE_INDEX = 2,
/** x.0 */
MASTER_RULE_INDEX = 3,
/** Inf */
INFINITY_RULE_INDEX = 4,
/** NaN */
NAN_RULE_INDEX = 5,
NON_NUMERICAL_RULE_LENGTH = 6
};
U_NAMESPACE_BEGIN
#if 0
@ -104,10 +120,6 @@ static const UChar gColon = 0x003a;
static const UChar gSemicolon = 0x003b;
static const UChar gLineFeed = 0x000a;
static const UChar gFourSpaces[] =
{
0x20, 0x20, 0x20, 0x20, 0
}; /* " " */
static const UChar gPercentPercent[] =
{
0x25, 0x25, 0
@ -118,16 +130,17 @@ static const UChar gNoparse[] =
0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0
}; /* "@noparse" */
NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& status)
NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions, int32_t index, UErrorCode& status)
: name()
, rules(0)
, negativeNumberRule(NULL)
, owner(_owner)
, fractionRules()
, fIsFractionRuleSet(FALSE)
, fIsPublic(FALSE)
, fIsParseable(TRUE)
{
for (int i = 0; i < 3; ++i) {
fractionRules[i] = NULL;
for (int32_t i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) {
nonNumericalRules[i] = NULL;
}
if (U_FAILURE(status)) {
@ -178,7 +191,7 @@ NFRuleSet::NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& sta
}
void
NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* owner, UErrorCode& status)
NFRuleSet::parseRules(UnicodeString& description, UErrorCode& status)
{
// start by creating a Vector whose elements are Strings containing
// the descriptions of the rules (one rule per element). The rules
@ -216,85 +229,103 @@ NFRuleSet::parseRules(UnicodeString& description, const RuleBasedNumberFormat* o
// (this isn't a for loop because we might be deleting items from
// the vector-- we want to make sure we only increment i when
// we _didn't_ delete aything from the vector)
uint32_t i = 0;
while (i < rules.size()) {
int32_t rulesSize = rules.size();
for (int32_t i = 0; i < rulesSize; i++) {
NFRule* rule = rules[i];
int64_t baseValue = rule->getBaseValue();
switch (rule->getType()) {
if (baseValue == 0) {
// if the rule's base value is 0, fill in a default
// base value (this will be 1 plus the preceding
// rule's base value for regular rule sets, and the
// same as the preceding rule's base value in fraction
// rule sets)
case NFRule::kNoBase:
rule->setBaseValue(defaultBaseValue, status);
if (!isFractionRuleSet()) {
++defaultBaseValue;
}
++i;
break;
// 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;
}
else {
// if it's a regular rule that already knows its base value,
// check to make sure the rules are in order, and update
// the default base value for the next rule
default:
if (rule->getBaseValue() < defaultBaseValue) {
if (baseValue < defaultBaseValue) {
// throw new IllegalArgumentException("Rules are not in order");
status = U_PARSE_ERROR;
return;
}
defaultBaseValue = rule->getBaseValue();
if (!isFractionRuleSet()) {
++defaultBaseValue;
}
++i;
break;
defaultBaseValue = baseValue;
}
if (!fIsFractionRuleSet) {
++defaultBaseValue;
}
}
}
/**
* Set one of the non-numerical rules.
* @param rule The rule to set.
*/
void NFRuleSet::setNonNumericalRule(NFRule *rule) {
int64_t baseValue = rule->getBaseValue();
if (baseValue == NFRule::kNegativeNumberRule) {
delete nonNumericalRules[NEGATIVE_RULE_INDEX];
nonNumericalRules[NEGATIVE_RULE_INDEX] = rule;
}
else if (baseValue == NFRule::kImproperFractionRule) {
setBestFractionRule(IMPROPER_FRACTION_RULE_INDEX, rule, TRUE);
}
else if (baseValue == NFRule::kProperFractionRule) {
setBestFractionRule(PROPER_FRACTION_RULE_INDEX, rule, TRUE);
}
else if (baseValue == NFRule::kMasterRule) {
setBestFractionRule(MASTER_RULE_INDEX, rule, TRUE);
}
else if (baseValue == NFRule::kInfinityRule) {
delete nonNumericalRules[INFINITY_RULE_INDEX];
nonNumericalRules[INFINITY_RULE_INDEX] = rule;
}
else if (baseValue == NFRule::kNaNRule) {
delete nonNumericalRules[NAN_RULE_INDEX];
nonNumericalRules[NAN_RULE_INDEX] = rule;
}
}
/**
* Determine the best fraction rule to use. Rules matching the decimal point from
* DecimalFormatSymbols become the main set of rules to use.
* @param originalIndex The index into nonNumericalRules
* @param newRule The new rule to consider
* @param rememberRule Should the new rule be added to fractionRules.
*/
void NFRuleSet::setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule) {
if (rememberRule) {
fractionRules.add(newRule);
}
NFRule *bestResult = nonNumericalRules[originalIndex];
if (bestResult == NULL) {
nonNumericalRules[originalIndex] = newRule;
}
else {
// We have more than one. Which one is better?
const DecimalFormatSymbols *decimalFormatSymbols = owner->getDecimalFormatSymbols();
if (decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol).charAt(0)
== newRule->getDecimalPoint())
{
nonNumericalRules[originalIndex] = newRule;
}
// else leave it alone
}
}
NFRuleSet::~NFRuleSet()
{
delete negativeNumberRule;
delete fractionRules[0];
delete fractionRules[1];
delete fractionRules[2];
for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
if (i != IMPROPER_FRACTION_RULE_INDEX
&& i != PROPER_FRACTION_RULE_INDEX
&& i != MASTER_RULE_INDEX)
{
delete nonNumericalRules[i];
}
// else it will be deleted via NFRuleList fractionRules
}
}
static UBool
@ -315,12 +346,16 @@ NFRuleSet::operator==(const NFRuleSet& rhs) const
{
if (rules.size() == rhs.rules.size() &&
fIsFractionRuleSet == rhs.fIsFractionRuleSet &&
name == rhs.name &&
util_equalRules(negativeNumberRule, rhs.negativeNumberRule) &&
util_equalRules(fractionRules[0], rhs.fractionRules[0]) &&
util_equalRules(fractionRules[1], rhs.fractionRules[1]) &&
util_equalRules(fractionRules[2], rhs.fractionRules[2])) {
name == rhs.name) {
// ...then compare the non-numerical rule lists...
for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
if (!util_equalRules(nonNumericalRules[i], rhs.nonNumericalRules[i])) {
return FALSE;
}
}
// ...then compare the rule lists...
for (uint32_t i = 0; i < rules.size(); ++i) {
if (*rules[i] != *rhs.rules[i]) {
return FALSE;
@ -336,6 +371,24 @@ NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErro
for (uint32_t i = 0; i < rules.size(); ++i) {
rules[i]->setDecimalFormatSymbols(newSymbols, status);
}
// Switch the fraction rules to mirror the DecimalFormatSymbols.
for (int32_t nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= MASTER_RULE_INDEX; nonNumericalIdx++) {
if (nonNumericalRules[nonNumericalIdx]) {
for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) {
NFRule *fractionRule = fractionRules[fIdx];
if (nonNumericalRules[nonNumericalIdx]->getBaseValue() == fractionRule->getBaseValue()) {
setBestFractionRule(nonNumericalIdx, fractionRule, FALSE);
}
}
}
}
for (uint32_t nnrIdx = 0; nnrIdx < NON_NUMERICAL_RULE_LENGTH; nnrIdx++) {
NFRule *rule = nonNumericalRules[nnrIdx];
if (rule) {
rule->setDecimalFormatSymbols(newSymbols, status);
}
}
}
#define RECURSION_LIMIT 64
@ -343,32 +396,32 @@ NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErro
void
NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const
{
NFRule *rule = findNormalRule(number);
if (recursionCount >= RECURSION_LIMIT) {
// stop recursion
status = U_INVALID_STATE_ERROR;
return;
}
const NFRule *rule = findNormalRule(number);
if (rule) { // else error, but can't report it
if (recursionCount >= RECURSION_LIMIT) {
// stop recursion
status = U_INVALID_STATE_ERROR;
} else {
rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
}
rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
}
}
void
NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const
{
NFRule *rule = findDoubleRule(number);
if (recursionCount >= RECURSION_LIMIT) {
// stop recursion
status = U_INVALID_STATE_ERROR;
return;
}
const NFRule *rule = findDoubleRule(number);
if (rule) { // else error, but can't report it
if (recursionCount >= RECURSION_LIMIT) {
// stop recursion
status = U_INVALID_STATE_ERROR;
} else {
rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
}
rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
}
}
NFRule*
const NFRule*
NFRuleSet::findDoubleRule(double number) const
{
// if this is a fraction rule set, use findFractionRuleSetRule()
@ -376,33 +429,49 @@ NFRuleSet::findDoubleRule(double number) const
return findFractionRuleSetRule(number);
}
if (uprv_isNaN(number)) {
const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX];
if (!rule) {
rule = owner->getDefaultNaNRule();
}
return rule;
}
// if the number is negative, return the negative number rule
// (if there isn't a negative-number rule, we pretend it's a
// positive number)
if (number < 0) {
if (negativeNumberRule) {
return negativeNumberRule;
if (nonNumericalRules[NEGATIVE_RULE_INDEX]) {
return nonNumericalRules[NEGATIVE_RULE_INDEX];
} else {
number = -number;
}
}
if (uprv_isInfinite(number)) {
const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX];
if (!rule) {
rule = owner->getDefaultInfinityRule();
}
return rule;
}
// if the number isn't an integer, we use one of the fraction rules...
if (number != uprv_floor(number)) {
// if the number is between 0 and 1, return the proper
// fraction rule
if (number < 1 && fractionRules[1]) {
return fractionRules[1];
if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) {
return nonNumericalRules[PROPER_FRACTION_RULE_INDEX];
}
// otherwise, return the improper fraction rule
else if (fractionRules[0]) {
return fractionRules[0];
else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) {
return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX];
}
}
// if there's a master rule, use it to format the number
if (fractionRules[2]) {
return fractionRules[2];
if (nonNumericalRules[MASTER_RULE_INDEX]) {
return nonNumericalRules[MASTER_RULE_INDEX];
}
// and if we haven't yet returned a rule, use findNormalRule()
@ -411,7 +480,7 @@ NFRuleSet::findDoubleRule(double number) const
return findNormalRule(r);
}
NFRule *
const NFRule *
NFRuleSet::findNormalRule(int64_t number) const
{
// if this is a fraction rule set, use findFractionRuleSetRule()
@ -424,8 +493,8 @@ NFRuleSet::findNormalRule(int64_t number) const
// if the number is negative, return the negative-number rule
// (if there isn't one, pretend the number is positive)
if (number < 0) {
if (negativeNumberRule) {
return negativeNumberRule;
if (nonNumericalRules[NEGATIVE_RULE_INDEX]) {
return nonNumericalRules[NEGATIVE_RULE_INDEX];
} else {
number = -number;
}
@ -482,7 +551,7 @@ NFRuleSet::findNormalRule(int64_t number) const
return result;
}
// else use the master rule
return fractionRules[2];
return nonNumericalRules[MASTER_RULE_INDEX];
}
/**
@ -500,7 +569,7 @@ NFRuleSet::findNormalRule(int64_t number) const
* a number between 0 and 1)
* @return The rule to use to format this number
*/
NFRule*
const NFRule*
NFRuleSet::findFractionRuleSetRule(double number) const
{
// the obvious way to do this (multiply the value being formatted
@ -633,40 +702,16 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
fprintf(stderr, "'\n");
fprintf(stderr, " parse negative: %d\n", this, negativeNumberRule != 0);
#endif
// start by trying the negative number rule (if there is one)
if (negativeNumberRule) {
Formattable tempResult;
#ifdef RBNF_DEBUG
fprintf(stderr, " <nfrs before negative> %x ub: %g\n", negativeNumberRule, upperBound);
#endif
UBool success = negativeNumberRule->doParse(text, workingPos, 0, upperBound, tempResult);
#ifdef RBNF_DEBUG
fprintf(stderr, " <nfrs after negative> success: %d wpi: %d\n", success, workingPos.getIndex());
#endif
if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
result = tempResult;
highWaterMark = workingPos;
}
workingPos = pos;
}
#ifdef RBNF_DEBUG
fprintf(stderr, "<nfrs> continue fractional with text '");
dumpUS(stderr, text);
fprintf(stderr, "' hwm: %d\n", highWaterMark.getIndex());
#endif
// then try each of the fraction rules
{
for (int i = 0; i < 3; i++) {
if (fractionRules[i]) {
Formattable tempResult;
UBool success = fractionRules[i]->doParse(text, workingPos, 0, upperBound, tempResult);
if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
result = tempResult;
highWaterMark = workingPos;
}
workingPos = pos;
// Try each of the negative rules, fraction rules, infinity rules and NaN rules
for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
if (nonNumericalRules[i]) {
Formattable tempResult;
UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, tempResult);
if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
result = tempResult;
highWaterMark = workingPos;
}
workingPos = pos;
}
}
#ifdef RBNF_DEBUG
@ -722,30 +767,37 @@ NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBoun
void
NFRuleSet::appendRules(UnicodeString& result) const
{
uint32_t i;
// the rule set name goes first...
result.append(name);
result.append(gColon);
result.append(gLineFeed);
// followed by the regular rules...
for (uint32_t i = 0; i < rules.size(); i++) {
result.append(gFourSpaces, 4);
for (i = 0; i < rules.size(); i++) {
rules[i]->_appendRuleText(result);
result.append(gLineFeed);
}
// followed by the special rules (if they exist)
if (negativeNumberRule) {
result.append(gFourSpaces, 4);
negativeNumberRule->_appendRuleText(result);
result.append(gLineFeed);
}
{
for (uint32_t i = 0; i < 3; ++i) {
if (fractionRules[i]) {
result.append(gFourSpaces, 4);
fractionRules[i]->_appendRuleText(result);
for (i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) {
NFRule *rule = nonNumericalRules[i];
if (nonNumericalRules[i]) {
if (rule->getBaseValue() == NFRule::kImproperFractionRule
|| rule->getBaseValue() == NFRule::kProperFractionRule
|| rule->getBaseValue() == NFRule::kMasterRule)
{
for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) {
NFRule *fractionRule = fractionRules[fIdx];
if (fractionRule->getBaseValue() == rule->getBaseValue()) {
fractionRule->_appendRuleText(result);
result.append(gLineFeed);
}
}
}
else {
rule->_appendRuleText(result);
result.append(gLineFeed);
}
}

View File

@ -28,10 +28,14 @@
U_NAMESPACE_BEGIN
class NFSubstitution;
class NFRuleSet : public UMemory {
public:
NFRuleSet(UnicodeString* descriptions, int32_t index, UErrorCode& status);
void parseRules(UnicodeString& rules, const RuleBasedNumberFormat* owner, UErrorCode& status);
NFRuleSet(RuleBasedNumberFormat *owner, UnicodeString* descriptions, int32_t index, UErrorCode& status);
void parseRules(UnicodeString& rules, UErrorCode& status);
void setNonNumericalRule(NFRule *rule);
void setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule);
void makeIntoFractionRuleSet() { fIsFractionRuleSet = TRUE; }
~NFRuleSet();
@ -57,16 +61,20 @@ public:
void setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status);
const RuleBasedNumberFormat *getOwner() const { return owner; }
private:
NFRule * findNormalRule(int64_t number) const;
NFRule * findDoubleRule(double number) const;
NFRule * findFractionRuleSetRule(double number) const;
const NFRule * findNormalRule(int64_t number) const;
const NFRule * findDoubleRule(double number) const;
const NFRule * findFractionRuleSetRule(double number) const;
friend NFSubstitution;
private:
UnicodeString name;
NFRuleList rules;
NFRule *negativeNumberRule;
NFRule *fractionRules[3];
NFRule *nonNumericalRules[6];
RuleBasedNumberFormat *owner;
NFRuleList fractionRules;
UBool fIsFractionRuleSet;
UBool fIsPublic;
UBool fIsParseable;

View File

@ -31,25 +31,32 @@
U_NAMESPACE_BEGIN
NFRule::NFRule(const RuleBasedNumberFormat* _rbnf)
NFRule::NFRule(const RuleBasedNumberFormat* _rbnf, const UnicodeString &_ruleText, UErrorCode &status)
: baseValue((int32_t)0)
, radix(0)
, radix(10)
, exponent(0)
, ruleText()
, decimalPoint(0)
, ruleText(_ruleText)
, sub1(NULL)
, sub2(NULL)
, formatter(_rbnf)
, rulePatternFormat(NULL)
{
if (!ruleText.isEmpty()) {
parseRuleDescriptor(ruleText, status);
}
}
NFRule::~NFRule()
{
if (sub1 != sub2) {
delete sub2;
sub2 = NULL;
}
delete sub1;
sub1 = NULL;
delete rulePatternFormat;
rulePatternFormat = NULL;
}
static const UChar gLeftBracket = 0x005b;
@ -66,11 +73,11 @@ static const UChar gDot = 0x002e;
static const UChar gTick = 0x0027;
//static const UChar gMinus = 0x002d;
static const UChar gSemicolon = 0x003b;
static const UChar gX = 0x0078;
static const UChar gMinusX[] = {0x2D, 0x78, 0}; /* "-x" */
static const UChar gXDotX[] = {0x78, 0x2E, 0x78, 0}; /* "x.x" */
static const UChar gXDotZero[] = {0x78, 0x2E, 0x30, 0}; /* "x.0" */
static const UChar gZeroDotX[] = {0x30, 0x2E, 0x78, 0}; /* "0.x" */
static const UChar gInf[] = {0x49, 0x6E, 0x66, 0}; /* "Inf" */
static const UChar gNaN[] = {0x4E, 0x61, 0x4E, 0}; /* "NaN" */
static const UChar gDollarOpenParenthesis[] = {0x24, 0x28, 0}; /* "$(" */
static const UChar gClosedParenthesisDollar[] = {0x29, 0x24, 0}; /* ")$" */
@ -88,7 +95,7 @@ static const UChar gEqualHash[] = {0x3D, 0x23, 0}; /* "=#" */
static const UChar gEqualZero[] = {0x3D, 0x30, 0}; /* "=0" */
static const UChar gGreaterGreaterGreater[] = {0x3E, 0x3E, 0x3E, 0}; /* ">>>" */
static const UChar * const tokenStrings[] = {
static const UChar * const RULE_PREFIXES[] = {
gLessLess, gLessPercent, gLessHash, gLessZero,
gGreaterGreater, gGreaterPercent,gGreaterHash, gGreaterZero,
gEqualPercent, gEqualHash, gEqualZero, NULL
@ -96,7 +103,7 @@ static const UChar * const tokenStrings[] = {
void
NFRule::makeRules(UnicodeString& description,
const NFRuleSet *ruleSet,
NFRuleSet *owner,
const NFRule *predecessor,
const RuleBasedNumberFormat *rbnf,
NFRuleList& rules,
@ -106,29 +113,32 @@ NFRule::makeRules(UnicodeString& description,
// new it up and initialize its basevalue and divisor
// (this also strips the rule descriptor, if any, off the
// descripton string)
NFRule* rule1 = new NFRule(rbnf);
NFRule* rule1 = new NFRule(rbnf, description, status);
/* test for NULL */
if (rule1 == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
rule1->parseRuleDescriptor(description, status);
description = rule1->ruleText;
// check the description to see whether there's text enclosed
// in brackets
int32_t brack1 = description.indexOf(gLeftBracket);
int32_t brack2 = description.indexOf(gRightBracket);
int32_t brack2 = brack1 < 0 ? -1 : description.indexOf(gRightBracket);
// if the description doesn't contain a matched pair of brackets,
// or if it's of a type that doesn't recognize bracketed text,
// then leave the description alone, initialize the rule's
// rule text and substitutions, and return that rule
if (brack1 == -1 || brack2 == -1 || brack1 > brack2
if (brack2 < 0 || brack1 > brack2
|| rule1->getType() == kProperFractionRule
|| rule1->getType() == kNegativeNumberRule) {
rule1->extractSubstitutions(ruleSet, description, predecessor, status);
rules.add(rule1);
} else {
|| rule1->getType() == kNegativeNumberRule
|| rule1->getType() == kInfinityRule
|| rule1->getType() == kNaNRule)
{
rule1->extractSubstitutions(owner, description, predecessor, status);
}
else {
// if the description does contain a matched pair of brackets,
// then it's really shorthand for two rules (with one exception)
NFRule* rule2 = NULL;
@ -147,7 +157,7 @@ NFRule::makeRules(UnicodeString& description,
// set, they both have the same base value; otherwise,
// increment the original rule's base value ("rule1" actually
// goes SECOND in the rule set's rule list)
rule2 = new NFRule(rbnf);
rule2 = new NFRule(rbnf, UnicodeString(), status);
/* test for NULL */
if (rule2 == 0) {
status = U_MEMORY_ALLOCATION_ERROR;
@ -155,7 +165,7 @@ NFRule::makeRules(UnicodeString& description,
}
if (rule1->baseValue >= 0) {
rule2->baseValue = rule1->baseValue;
if (!ruleSet->isFractionRuleSet()) {
if (!owner->isFractionRuleSet()) {
++rule1->baseValue;
}
}
@ -186,7 +196,7 @@ NFRule::makeRules(UnicodeString& description,
if (brack2 + 1 < description.length()) {
sbuf.append(description, brack2 + 1, description.length() - brack2 - 1);
}
rule2->extractSubstitutions(ruleSet, sbuf, predecessor, status);
rule2->extractSubstitutions(owner, sbuf, predecessor, status);
}
// rule1's text includes the text in the brackets but omits
@ -197,7 +207,7 @@ NFRule::makeRules(UnicodeString& description,
if (brack2 + 1 < description.length()) {
sbuf.append(description, brack2 + 1, description.length() - brack2 - 1);
}
rule1->extractSubstitutions(ruleSet, sbuf, predecessor, status);
rule1->extractSubstitutions(owner, sbuf, predecessor, status);
// if we only have one rule, return it; if we have two, return
// a two-element array containing them (notice that rule2 goes
@ -205,10 +215,20 @@ NFRule::makeRules(UnicodeString& description,
// material in the brackets and rule1 INCLUDES the material
// in the brackets)
if (rule2 != NULL) {
rules.add(rule2);
if (rule2->baseValue >= kNoBase) {
rules.add(rule2);
}
else {
owner->setNonNumericalRule(rule2);
}
}
}
if (rule1->baseValue >= kNoBase) {
rules.add(rule1);
}
else {
owner->setNonNumericalRule(rule1);
}
}
/**
@ -230,9 +250,7 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
// separated by a colon. The rule descriptor is optional. If
// it's omitted, just set the base value to 0.
int32_t p = description.indexOf(gColon);
if (p == -1) {
setBaseValue((int32_t)0, status);
} else {
if (p != -1) {
// copy the descriptor out into its own string and strip it,
// along with any trailing whitespace, out of the original
// description
@ -247,25 +265,15 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
// check first to see if the rule descriptor matches the token
// for one of the special rules. If it does, set the base
// value to the correct identfier value
if (0 == descriptor.compare(gMinusX, 2)) {
setType(kNegativeNumberRule);
}
else if (0 == descriptor.compare(gXDotX, 3)) {
setType(kImproperFractionRule);
}
else if (0 == descriptor.compare(gZeroDotX, 3)) {
setType(kProperFractionRule);
}
else if (0 == descriptor.compare(gXDotZero, 3)) {
setType(kMasterRule);
}
// if the rule descriptor begins with a digit, it's a descriptor
// for a normal rule
// since we don't have Long.parseLong, and this isn't much work anyway,
// just build up the value as we encounter the digits.
else if (descriptor.charAt(0) >= gZero && descriptor.charAt(0) <= gNine) {
// value to the correct identifier value
int descriptorLength = descriptor.length();
UChar firstChar = descriptor.charAt(0);
UChar lastChar = descriptor.charAt(descriptorLength - 1);
if (firstChar >= gZero && firstChar <= gNine && lastChar != gX) {
// if the rule descriptor begins with a digit, it's a descriptor
// for a normal rule
// since we don't have Long.parseLong, and this isn't much work anyway,
// just build up the value as we encounter the digits.
int64_t val = 0;
p = 0;
UChar c = gSpace;
@ -275,7 +283,7 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
// stop on a slash or > sign (or at the end of the string),
// and throw an exception on any other character
int64_t ll_10 = 10;
while (p < descriptor.length()) {
while (p < descriptorLength) {
c = descriptor.charAt(p);
if (c >= gZero && c <= gNine) {
val = val * ll_10 + (int32_t)(c - gZero);
@ -304,7 +312,7 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
val = 0;
++p;
int64_t ll_10 = 10;
while (p < descriptor.length()) {
while (p < descriptorLength) {
c = descriptor.charAt(p);
if (c >= gZero && c <= gNine) {
val = val * ll_10 + (int32_t)(c - gZero);
@ -352,7 +360,31 @@ NFRule::parseRuleDescriptor(UnicodeString& description, UErrorCode& status)
}
}
}
else if (0 == descriptor.compare(gMinusX, 2)) {
setType(kNegativeNumberRule);
}
else if (descriptorLength == 3) {
if (firstChar == gZero && lastChar == gX) {
setBaseValue(kProperFractionRule, status);
decimalPoint = descriptor.charAt(1);
}
else if (firstChar == gX && lastChar == gX) {
setBaseValue(kImproperFractionRule, status);
decimalPoint = descriptor.charAt(1);
}
else if (firstChar == gX && lastChar == gZero) {
setBaseValue(kMasterRule, status);
decimalPoint = descriptor.charAt(1);
}
else if (descriptor.compare(gNaN, 3) == 0) {
setBaseValue(kNaNRule, status);
}
else if (descriptor.compare(gInf, 3) == 0) {
setBaseValue(kInfinityRule, status);
}
}
}
// else use the default base value for now.
// finally, if the rule body begins with an apostrophe, strip it off
// (this is generally used to put whitespace at the beginning of
@ -384,11 +416,10 @@ NFRule::extractSubstitutions(const NFRuleSet* ruleSet,
return;
}
this->ruleText = ruleText;
this->rulePatternFormat = NULL;
sub1 = extractSubstitution(ruleSet, predecessor, status);
if (sub1 == NULL || sub1->isNullSubstitution()) {
if (sub1 == NULL) {
// Small optimization. There is no need to create a redundant NullSubstitution.
sub2 = sub1;
sub2 = NULL;
}
else {
sub2 = extractSubstitution(ruleSet, predecessor, status);
@ -439,14 +470,13 @@ NFRule::extractSubstitution(const NFRuleSet* ruleSet,
// search the rule's rule text for the first two characters of
// a substitution token
int32_t subStart = indexOfAny(tokenStrings);
int32_t subStart = indexOfAnyRulePrefix();
int32_t subEnd = subStart;
// if we didn't find one, create a null substitution positioned
// at the end of the rule text
if (subStart == -1) {
return NFSubstitution::makeSubstitution(ruleText.length(), this, predecessor,
ruleSet, this->formatter, UnicodeString(), status);
return NULL;
}
// special-case the ">>>" token, since searching for the > at the
@ -473,8 +503,7 @@ NFRule::extractSubstitution(const NFRuleSet* ruleSet,
// unmatched token character), create a null substitution positioned
// at the end of the rule
if (subEnd == -1) {
return NFSubstitution::makeSubstitution(ruleText.length(), this, predecessor,
ruleSet, this->formatter, UnicodeString(), status);
return NULL;
}
// if we get here, we have a real substitution token (or at least
@ -503,6 +532,7 @@ NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status)
{
// set the base value
baseValue = newBaseValue;
radix = 10;
// if this isn't a special rule, recalculate the radix and exponent
// (the radix always defaults to 10; if it's supposed to be something
@ -510,7 +540,6 @@ NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status)
// recalculated again-- the only function that does this is
// NFRule.parseRuleDescriptor() )
if (baseValue >= 1) {
radix = 10;
exponent = expectedExponent();
// this function gets called on a fully-constructed rule whose
@ -527,7 +556,6 @@ NFRule::setBaseValue(int64_t newBaseValue, UErrorCode& status)
// if this is a special rule, its radix and exponent are basically
// ignored. Set them to "safe" default values
} else {
radix = 10;
exponent = 0;
}
}
@ -560,19 +588,17 @@ NFRule::expectedExponent() const
/**
* Searches the rule's rule text for any of the specified strings.
* @param strings An array of strings to search the rule's rule
* text for
* @return The index of the first match in the rule's rule text
* (i.e., the first substring in the rule's rule text that matches
* _any_ of the strings in "strings"). If none of the strings in
* "strings" is found in the rule's rule text, returns -1.
*/
int32_t
NFRule::indexOfAny(const UChar* const strings[]) const
NFRule::indexOfAnyRulePrefix() const
{
int result = -1;
for (int i = 0; strings[i]; i++) {
int32_t pos = ruleText.indexOf(*strings[i]);
for (int i = 0; RULE_PREFIXES[i]; i++) {
int32_t pos = ruleText.indexOf(*RULE_PREFIXES[i]);
if (pos != -1 && (result == -1 || pos < result)) {
result = pos;
}
@ -584,6 +610,19 @@ NFRule::indexOfAny(const UChar* const strings[]) const
// boilerplate
//-----------------------------------------------------------------------
static UBool
util_equalSubstitutions(const NFSubstitution* sub1, const NFSubstitution* sub2)
{
if (sub1) {
if (sub2) {
return *sub1 == *sub2;
}
} else if (!sub2) {
return TRUE;
}
return FALSE;
}
/**
* Tests two rules for equality.
* @param that The rule to compare this one against
@ -596,8 +635,8 @@ NFRule::operator==(const NFRule& rhs) const
&& radix == rhs.radix
&& exponent == rhs.exponent
&& ruleText == rhs.ruleText
&& *sub1 == *rhs.sub1
&& *sub2 == *rhs.sub2;
&& util_equalSubstitutions(sub1, rhs.sub1)
&& util_equalSubstitutions(sub2, rhs.sub2);
}
/**
@ -619,9 +658,11 @@ NFRule::_appendRuleText(UnicodeString& result) const
{
switch (getType()) {
case kNegativeNumberRule: result.append(gMinusX, 2); break;
case kImproperFractionRule: result.append(gXDotX, 3); break;
case kProperFractionRule: result.append(gZeroDotX, 3); break;
case kMasterRule: result.append(gXDotZero, 3); break;
case kImproperFractionRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break;
case kProperFractionRule: result.append(gZero).append(decimalPoint == 0 ? gDot : decimalPoint).append(gX); break;
case kMasterRule: result.append(gX).append(decimalPoint == 0 ? gDot : decimalPoint).append(gZero); break;
case kInfinityRule: result.append(gInf, 3); break;
case kNaNRule: result.append(gNaN, 3); break;
default:
// for a normal rule, write out its base value, and if the radix is
// something other than 10, write out the radix (with the preceding
@ -646,7 +687,7 @@ NFRule::_appendRuleText(UnicodeString& result) const
// if the rule text begins with a space, write an apostrophe
// (whitespace after the rule descriptor is ignored; the
// apostrophe is used to make the whitespace significant)
if (ruleText.charAt(0) == gSpace && sub1->getPos() != 0) {
if (ruleText.charAt(0) == gSpace && (sub1 == NULL || sub1->getPos() != 0)) {
result.append(gTick);
}
@ -656,10 +697,14 @@ NFRule::_appendRuleText(UnicodeString& result) const
ruleTextCopy.setTo(ruleText);
UnicodeString temp;
sub2->toString(temp);
ruleTextCopy.insert(sub2->getPos(), temp);
sub1->toString(temp);
ruleTextCopy.insert(sub1->getPos(), temp);
if (sub2 != NULL) {
sub2->toString(temp);
ruleTextCopy.insert(sub2->getPos(), temp);
}
if (sub1 != NULL) {
sub1->toString(temp);
ruleTextCopy.insert(sub1->getPos(), temp);
}
result.append(ruleTextCopy);
@ -709,10 +754,10 @@ NFRule::doFormat(int64_t number, UnicodeString& toInsertInto, int32_t pos, int32
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
}
if (!sub2->isNullSubstitution()) {
if (sub2 != NULL) {
sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status);
}
if (!sub1->isNullSubstitution()) {
if (sub1 != NULL) {
sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status);
}
}
@ -755,10 +800,10 @@ NFRule::doFormat(double number, UnicodeString& toInsertInto, int32_t pos, int32_
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
}
if (!sub2->isNullSubstitution()) {
if (sub2 != NULL) {
sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status);
}
if (!sub1->isNullSubstitution()) {
if (sub1 != NULL) {
sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount, status);
}
}
@ -790,7 +835,7 @@ NFRule::shouldRollBack(double number) const
// a modulus substitution, its base value isn't an even multiple
// of 100, and the value we're trying to format _is_ an even
// multiple of 100. This is called the "rollback rule."
if ((sub1->isModulusSubstitution()) || (sub2->isModulusSubstitution())) {
if ((sub1 != NULL && sub1->isModulusSubstitution()) || (sub2 != NULL && sub2->isModulusSubstitution())) {
int64_t re = util64_pow(radix, exponent);
return uprv_fmod(number, (double)re) == 0 && (baseValue % re) != 0;
}
@ -833,7 +878,6 @@ static void dumpUS(FILE* f, const UnicodeString& us) {
}
}
#endif
UBool
NFRule::doParse(const UnicodeString& text,
ParsePosition& parsePosition,
@ -846,22 +890,25 @@ NFRule::doParse(const UnicodeString& text,
ParsePosition pp;
UnicodeString workText(text);
int32_t sub1Pos = sub1 != NULL ? sub1->getPos() : ruleText.length();
int32_t sub2Pos = sub2 != NULL ? sub2->getPos() : ruleText.length();
// check to see whether the text before the first substitution
// matches the text at the beginning of the string being
// parsed. If it does, strip that off the front of workText;
// otherwise, dump out with a mismatch
UnicodeString prefix;
prefix.setTo(ruleText, 0, sub1->getPos());
prefix.setTo(ruleText, 0, sub1Pos);
#ifdef RBNF_DEBUG
fprintf(stderr, "doParse %x ", this);
fprintf(stderr, "doParse %p ", this);
{
UnicodeString rt;
_appendRuleText(rt);
dumpUS(stderr, rt);
}
fprintf(stderr, " text: '", this);
fprintf(stderr, " text: '");
dumpUS(stderr, text);
fprintf(stderr, "' prefix: '");
dumpUS(stderr, prefix);
@ -870,16 +917,28 @@ NFRule::doParse(const UnicodeString& text,
int32_t prefixLength = text.length() - workText.length();
#ifdef RBNF_DEBUG
fprintf(stderr, "' pl: %d ppi: %d s1p: %d\n", prefixLength, pp.getIndex(), sub1->getPos());
fprintf(stderr, "' pl: %d ppi: %d s1p: %d\n", prefixLength, pp.getIndex(), sub1Pos);
#endif
if (pp.getIndex() == 0 && sub1->getPos() != 0) {
if (pp.getIndex() == 0 && sub1Pos != 0) {
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
parsePosition.setErrorIndex(pp.getErrorIndex());
resVal.setLong(0);
return TRUE;
}
if (baseValue == kInfinityRule) {
// If you match this, don't try to perform any calculations on it.
parsePosition.setIndex(pp.getIndex());
resVal.setDouble(uprv_getInfinity());
return TRUE;
}
if (baseValue == kNaNRule) {
// If you match this, don't try to perform any calculations on it.
parsePosition.setIndex(pp.getIndex());
resVal.setDouble(uprv_getNaN());
return TRUE;
}
// this is the fun part. The basic guts of the rule-matching
// logic is matchToDelimiter(), which is called twice. The first
@ -923,7 +982,7 @@ NFRule::doParse(const UnicodeString& text,
// the substitution, giving us a new partial parse result
pp.setIndex(0);
temp.setTo(ruleText, sub1->getPos(), sub2->getPos() - sub1->getPos());
temp.setTo(ruleText, sub1Pos, sub2Pos - sub1Pos);
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
temp, pp, sub1,
upperBound);
@ -932,7 +991,7 @@ NFRule::doParse(const UnicodeString& text,
// null substitution), pp is now pointing at the first unmatched
// character. Take note of that, and try matchToDelimiter()
// on the input text again
if (pp.getIndex() != 0 || sub1->isNullSubstitution()) {
if (pp.getIndex() != 0 || sub1 == NULL) {
start = pp.getIndex();
UnicodeString workText2;
@ -943,7 +1002,7 @@ NFRule::doParse(const UnicodeString& text,
// partial result with whatever it gets back from its
// substitution if there's a successful match, giving us
// a real result
temp.setTo(ruleText, sub2->getPos(), ruleText.length() - sub2->getPos());
temp.setTo(ruleText, sub2Pos, ruleText.length() - sub2Pos);
partialResult = matchToDelimiter(workText2, 0, partialResult,
temp, pp2, sub2,
upperBound);
@ -951,25 +1010,25 @@ NFRule::doParse(const UnicodeString& text,
// if we got a successful match on this second
// matchToDelimiter() call, update the high-water mark
// and result (if necessary)
if (pp2.getIndex() != 0 || sub2->isNullSubstitution()) {
if (pp2.getIndex() != 0 || sub2 == NULL) {
if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) {
highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex();
result = partialResult;
}
}
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
else {
int32_t temp = pp2.getErrorIndex() + sub1->getPos() + pp.getIndex();
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
int32_t temp = pp2.getErrorIndex() + sub1Pos + pp.getIndex();
if (temp> parsePosition.getErrorIndex()) {
parsePosition.setErrorIndex(temp);
}
}
}
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
else {
int32_t temp = sub1->getPos() + pp.getErrorIndex();
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
int32_t temp = sub1Pos + pp.getErrorIndex();
if (temp > parsePosition.getErrorIndex()) {
parsePosition.setErrorIndex(temp);
}
@ -977,7 +1036,7 @@ NFRule::doParse(const UnicodeString& text,
// keep trying to match things until the outer matchToDelimiter()
// call fails to make a match (each time, it picks up where it
// left off the previous time)
} while (sub1->getPos() != sub2->getPos()
} while (sub1Pos != sub2Pos
&& pp.getIndex() > 0
&& pp.getIndex() < workText.length()
&& pp.getIndex() != start);
@ -999,9 +1058,7 @@ NFRule::doParse(const UnicodeString& text,
// we have to account for it here. By definition, if the matching
// rule in a fraction rule set has no substitutions, its numerator
// is 1, and so the result is the reciprocal of its base value.
if (isFractionRule &&
highWaterMark > 0 &&
sub1->isNullSubstitution()) {
if (isFractionRule && highWaterMark > 0 && sub1 == NULL) {
result = 1 / result;
}
@ -1129,9 +1186,9 @@ NFRule::matchToDelimiter(const UnicodeString& text,
pp.setIndex(dPos + dLen);
return result.getDouble();
}
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
else {
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
if (tempPP.getErrorIndex() > 0) {
pp.setErrorIndex(tempPP.getErrorIndex());
} else {
@ -1155,7 +1212,11 @@ NFRule::matchToDelimiter(const UnicodeString& text,
// (i.e., is semantically empty), thwe we obviously can't search
// for "delimiter". Instead, just use "sub" to parse as much of
// "text" as possible.
} else {
}
else if (sub == NULL) {
return _baseValue;
}
else {
ParsePosition tempPP;
Formattable result;
@ -1167,7 +1228,7 @@ NFRule::matchToDelimiter(const UnicodeString& text,
formatter->isLenient(),
#endif
result);
if (success && (tempPP.getIndex() != 0 || sub->isNullSubstitution())) {
if (success && (tempPP.getIndex() != 0)) {
// if there's a successful match (or it's a null
// substitution), update pp to point to the first
// character we didn't match, and pass the result from
@ -1175,9 +1236,9 @@ NFRule::matchToDelimiter(const UnicodeString& text,
pp.setIndex(tempPP.getIndex());
return result.getDouble();
}
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
else {
// commented out because ParsePosition doesn't have error index in 1.1.x
// restored for ICU4C port
pp.setErrorIndex(tempPP.getErrorIndex());
}

View File

@ -38,17 +38,19 @@ public:
kImproperFractionRule = -2,
kProperFractionRule = -3,
kMasterRule = -4,
kOtherRule = -5
kInfinityRule = -5,
kNaNRule = -6,
kOtherRule = -7
};
static void makeRules(UnicodeString& definition,
const NFRuleSet* ruleSet,
NFRuleSet* ruleSet,
const NFRule* predecessor,
const RuleBasedNumberFormat* rbnf,
NFRuleList& ruleList,
UErrorCode& status);
NFRule(const RuleBasedNumberFormat* rbnf);
NFRule(const RuleBasedNumberFormat* rbnf, const UnicodeString &ruleText, UErrorCode &status);
~NFRule();
UBool operator==(const NFRule& rhs) const;
@ -60,6 +62,8 @@ public:
int64_t getBaseValue() const { return baseValue; }
void setBaseValue(int64_t value, UErrorCode& status);
UChar getDecimalPoint() const { return decimalPoint; }
double getDivisor() const { return uprv_pow(radix, exponent); }
void doFormat(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const;
@ -86,7 +90,7 @@ private:
NFSubstitution* extractSubstitution(const NFRuleSet* ruleSet, const NFRule* predecessor, UErrorCode& status);
int16_t expectedExponent() const;
int32_t indexOfAny(const UChar* const strings[]) const;
int32_t indexOfAnyRulePrefix() const;
double matchToDelimiter(const UnicodeString& text, int32_t startPos, double baseValue,
const UnicodeString& delimiter, ParsePosition& pp, const NFSubstitution* sub,
double upperBound) const;
@ -101,6 +105,7 @@ private:
int64_t baseValue;
int32_t radix;
int16_t exponent;
UChar decimalPoint;
UnicodeString ruleText;
NFSubstitution* sub1;
NFSubstitution* sub2;

View File

@ -48,7 +48,6 @@ class SameValueSubstitution : public NFSubstitution {
public:
SameValueSubstitution(int32_t pos,
const NFRuleSet* ruleset,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status);
virtual ~SameValueSubstitution();
@ -74,10 +73,9 @@ public:
MultiplierSubstitution(int32_t _pos,
double _divisor,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status), divisor(_divisor)
: NFSubstitution(_pos, _ruleSet, description, status), divisor(_divisor)
{
ldivisor = util64_fromDouble(divisor);
if (divisor == 0) {
@ -133,7 +131,6 @@ public:
double _divisor,
const NFRule* rulePredecessor,
const NFRuleSet* ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status);
virtual ~ModulusSubstitution();
@ -185,10 +182,9 @@ class IntegralPartSubstitution : public NFSubstitution {
public:
IntegralPartSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
: NFSubstitution(_pos, _ruleSet, description, status) {}
virtual ~IntegralPartSubstitution();
virtual int64_t transformNumber(int64_t number) const { return number; }
@ -211,7 +207,6 @@ class FractionalPartSubstitution : public NFSubstitution {
public:
FractionalPartSubstitution(int32_t pos,
const NFRuleSet* ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status);
virtual ~FractionalPartSubstitution();
@ -245,10 +240,9 @@ class AbsoluteValueSubstitution : public NFSubstitution {
public:
AbsoluteValueSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
: NFSubstitution(_pos, _ruleSet, description, status) {}
virtual ~AbsoluteValueSubstitution();
virtual int64_t transformNumber(int64_t number) const { return number >= 0 ? number : -number; }
@ -278,11 +272,10 @@ public:
}
NumeratorSubstitution(int32_t _pos,
double _denominator,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
NFRuleSet* _ruleSet,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, fixdesc(description), status), denominator(_denominator)
: NFSubstitution(_pos, _ruleSet, fixdesc(description), status), denominator(_denominator)
{
ldenominator = util64_fromDouble(denominator);
withZeros = description.endsWith(LTLT, 2);
@ -316,40 +309,6 @@ public:
NumeratorSubstitution::~NumeratorSubstitution() {}
class NullSubstitution : public NFSubstitution {
public:
NullSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status) {}
virtual ~NullSubstitution();
virtual void toString(UnicodeString& /*result*/) const {}
virtual void doSubstitution(double /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {}
virtual void doSubstitution(int64_t /*number*/, UnicodeString& /*toInsertInto*/, int32_t /*_pos*/, int32_t /*recursionCount*/, UErrorCode& /*status*/) const {}
virtual int64_t transformNumber(int64_t /*number*/) const { return 0; }
virtual double transformNumber(double /*number*/) const { return 0; }
virtual UBool doParse(const UnicodeString& /*text*/,
ParsePosition& /*parsePosition*/,
double baseValue,
double /*upperBound*/,
UBool /*lenientParse*/,
Formattable& result) const
{ result.setDouble(baseValue); return TRUE; }
virtual double composeRuleValue(double /*newRuleValue*/, double /*oldRuleValue*/) const { return 0.0; } // never called
virtual double calcUpperBound(double /*oldUpperBound*/) const { return 0; } // never called
virtual UBool isNullSubstitution() const { return TRUE; }
virtual UChar tokenChar() const { return (UChar)0x0020; } // ' ' never called
public:
static UClassID getStaticClassID(void);
virtual UClassID getDynamicClassID(void) const;
};
NullSubstitution::~NullSubstitution() {}
NFSubstitution*
NFSubstitution::makeSubstitution(int32_t pos,
const NFRule* rule,
@ -361,7 +320,7 @@ NFSubstitution::makeSubstitution(int32_t pos,
{
// if the description is empty, return a NullSubstitution
if (description.length() == 0) {
return new NullSubstitution(pos, ruleSet, formatter, description, status);
return NULL;
}
switch (description.charAt(0)) {
@ -380,20 +339,20 @@ NFSubstitution::makeSubstitution(int32_t pos,
else if (rule->getBaseValue() == NFRule::kImproperFractionRule
|| rule->getBaseValue() == NFRule::kProperFractionRule
|| rule->getBaseValue() == NFRule::kMasterRule) {
return new IntegralPartSubstitution(pos, ruleSet, formatter, description, status);
return new IntegralPartSubstitution(pos, ruleSet, description, status);
}
// if the rule set containing the rule is a fraction
// rule set, return a NumeratorSubstitution
else if (ruleSet->isFractionRuleSet()) {
return new NumeratorSubstitution(pos, (double)rule->getBaseValue(),
formatter->getDefaultRuleSet(), formatter, description, status);
formatter->getDefaultRuleSet(), description, status);
}
// otherwise, return a MultiplierSubstitution
else {
return new MultiplierSubstitution(pos, rule->getDivisor(), ruleSet,
formatter, description, status);
description, status);
}
// if the description begins with '>'...
@ -401,7 +360,7 @@ NFSubstitution::makeSubstitution(int32_t pos,
// if the rule is a negative-number rule, return
// an AbsoluteValueSubstitution
if (rule->getBaseValue() == NFRule::kNegativeNumberRule) {
return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description, status);
return new AbsoluteValueSubstitution(pos, ruleSet, description, status);
}
// if the rule is a fraction rule, return a
@ -409,7 +368,7 @@ NFSubstitution::makeSubstitution(int32_t pos,
else if (rule->getBaseValue() == NFRule::kImproperFractionRule
|| rule->getBaseValue() == NFRule::kProperFractionRule
|| rule->getBaseValue() == NFRule::kMasterRule) {
return new FractionalPartSubstitution(pos, ruleSet, formatter, description, status);
return new FractionalPartSubstitution(pos, ruleSet, description, status);
}
// if the rule set owning the rule is a fraction rule set,
@ -423,13 +382,13 @@ NFSubstitution::makeSubstitution(int32_t pos,
// otherwise, return a ModulusSubstitution
else {
return new ModulusSubstitution(pos, rule->getDivisor(), predecessor,
ruleSet, formatter, description, status);
ruleSet, description, status);
}
// if the description begins with '=', always return a
// SameValueSubstitution
case gEquals:
return new SameValueSubstitution(pos, ruleSet, formatter, description, status);
return new SameValueSubstitution(pos, ruleSet, description, status);
// and if it's anything else, throw an exception
default:
@ -441,7 +400,6 @@ NFSubstitution::makeSubstitution(int32_t pos,
NFSubstitution::NFSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: pos(_pos), ruleSet(NULL), numberFormat(NULL)
@ -463,54 +421,55 @@ NFSubstitution::NFSubstitution(int32_t _pos,
return;
}
// if the description was just two paired token characters
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
// format its result
if (workingDescription.length() == 0) {
// if the description was just two paired token characters
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
// format its result
this->ruleSet = _ruleSet;
}
// if the description contains a rule set name, that's the rule
// set we use to format the result: get a reference to the
// names rule set
else if (workingDescription.charAt(0) == gPercent) {
this->ruleSet = formatter->findRuleSet(workingDescription, status);
// if the description contains a rule set name, that's the rule
// set we use to format the result: get a reference to the
// names rule set
this->ruleSet = _ruleSet->getOwner()->findRuleSet(workingDescription, status);
}
// if the description begins with 0 or #, treat it as a
// DecimalFormat pattern, and initialize a DecimalFormat with
// that pattern (then set it to use the DecimalFormatSymbols
// belonging to our formatter)
else if (workingDescription.charAt(0) == gPound || workingDescription.charAt(0) ==gZero) {
DecimalFormatSymbols* sym = formatter->getDecimalFormatSymbols();
// if the description begins with 0 or #, treat it as a
// DecimalFormat pattern, and initialize a DecimalFormat with
// that pattern (then set it to use the DecimalFormatSymbols
// belonging to our formatter)
const DecimalFormatSymbols* sym = _ruleSet->getOwner()->getDecimalFormatSymbols();
if (!sym) {
status = U_MISSING_RESOURCE_ERROR;
return;
}
this->numberFormat = new DecimalFormat(workingDescription, *sym, status);
DecimalFormat *tempNumberFormat = new DecimalFormat(workingDescription, *sym, status);
/* test for NULL */
if (this->numberFormat == 0) {
if (!tempNumberFormat) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
if (U_FAILURE(status)) {
delete (DecimalFormat*)this->numberFormat;
this->numberFormat = NULL;
delete tempNumberFormat;
return;
}
// this->numberFormat->setDecimalFormatSymbols(formatter->getDecimalFormatSymbols());
this->numberFormat = tempNumberFormat;
}
// if the description is ">>>", this substitution bypasses the
// usual rule-search process and always uses the rule that precedes
// it in its own rule set's rule list (this is used for place-value
// notations: formats where you want to see a particular part of
// a number even when it's 0)
else if (workingDescription.charAt(0) == gGreaterThan) {
// if the description is ">>>", this substitution bypasses the
// usual rule-search process and always uses the rule that precedes
// it in its own rule set's rule list (this is used for place-value
// notations: formats where you want to see a particular part of
// a number even when it's 0)
// this causes problems when >>> is used in a frationalPartSubstitution
// this->ruleSet = NULL;
this->ruleSet = _ruleSet;
this->numberFormat = NULL;
}
// and of the description is none of these things, it's a syntax error
else {
// and of the description is none of these things, it's a syntax error
// throw new IllegalArgumentException("Illegal substitution syntax");
status = U_PARSE_ERROR;
}
@ -647,6 +606,13 @@ NFSubstitution::doSubstitution(double number, UnicodeString& toInsertInto, int32
// is dependent on the type of substitution this is
double numberToFormat = transformNumber(number);
if (uprv_isInfinite(numberToFormat)) {
// This is probably a minus rule. Combine it with an infinite rule.
const NFRule *infiniteRule = ruleSet->findDoubleRule(uprv_getInfinity());
infiniteRule->doFormat(numberToFormat, toInsertInto, _pos + this->pos, recursionCount, status);
return;
}
// if the result is an integer, from here on out we work in integer
// space (saving time and memory and preserving accuracy)
if (numberToFormat == uprv_floor(numberToFormat) && ruleSet != NULL) {
@ -785,11 +751,6 @@ NFSubstitution::doParse(const UnicodeString& text,
}
}
UBool
NFSubstitution::isNullSubstitution() const {
return FALSE;
}
/**
* Returns true if this is a modulus substitution. (We didn't do this
* with instanceof partially because it causes source files to
@ -811,10 +772,9 @@ NFSubstitution::isModulusSubstitution() const {
*/
SameValueSubstitution::SameValueSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status)
: NFSubstitution(_pos, _ruleSet, description, status)
{
if (0 == description.compare(gEqualsEquals, 2)) {
// throw new IllegalArgumentException("== is not a legal token");
@ -850,10 +810,9 @@ ModulusSubstitution::ModulusSubstitution(int32_t _pos,
double _divisor,
const NFRule* predecessor,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status)
: NFSubstitution(_pos, _ruleSet, description, status)
, divisor(_divisor)
, ruleToUse(NULL)
{
@ -1026,10 +985,9 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(IntegralPartSubstitution)
*/
FractionalPartSubstitution::FractionalPartSubstitution(int32_t _pos,
const NFRuleSet* _ruleSet,
const RuleBasedNumberFormat* formatter,
const UnicodeString& description,
UErrorCode& status)
: NFSubstitution(_pos, _ruleSet, formatter, description, status)
: NFSubstitution(_pos, _ruleSet, description, status)
, byDigits(FALSE)
, useSpaces(TRUE)
@ -1357,12 +1315,6 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NumeratorSubstitution)
const UChar NumeratorSubstitution::LTLT[] = { 0x003c, 0x003c };
//===================================================================
// NullSubstitution
//===================================================================
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NullSubstitution)
U_NAMESPACE_END
/* U_HAVE_RBNF */

View File

@ -37,7 +37,6 @@ class NFSubstitution : public UObject {
protected:
NFSubstitution(int32_t pos,
const NFRuleSet* ruleSet,
const RuleBasedNumberFormat* rbnf,
const UnicodeString& description,
UErrorCode& status);
@ -234,14 +233,6 @@ public:
*/
virtual UChar tokenChar() const = 0;
/**
* Returns true if this is a null substitution. (We didn't do this
* with instanceof partially because it causes source files to
* proliferate and partially because we have to port this to C++.)
* @return true if this object is an instance of NullSubstitution
*/
virtual UBool isNullSubstitution() const;
/**
* Returns true if this is a modulus substitution. (We didn't do this
* with instanceof partially because it causes source files to

View File

@ -36,7 +36,7 @@
// #define RBNF_DEBUG
#ifdef RBNF_DEBUG
#include "stdio.h"
#include <stdio.h>
#endif
#define U_ICUDATA_RBNF U_ICUDATA_NAME U_TREE_SEPARATOR_STRING "rbnf"
@ -661,6 +661,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, locale(alocale)
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -683,6 +685,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, locale(Locale::getDefault())
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -705,6 +709,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, locale(alocale)
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -726,6 +732,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, locale(Locale::getDefault())
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -748,6 +756,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const UnicodeString& description,
, locale(aLocale)
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -767,6 +777,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
, locale(alocale)
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -814,7 +826,7 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(URBNFRuleSetTag tag, const Locale&
}
UParseError perror;
init (desc, locinfo, perror, status);
init(desc, locinfo, perror, status);
ures_close(ruleSets);
ures_close(rbnfRules);
@ -831,6 +843,8 @@ RuleBasedNumberFormat::RuleBasedNumberFormat(const RuleBasedNumberFormat& rhs)
, locale(rhs.locale)
, collator(NULL)
, decimalFormatSymbols(NULL)
, defaultInfinityRule(NULL)
, defaultNaNRule(NULL)
, lenient(FALSE)
, lenientParseRules(NULL)
, localizations(NULL)
@ -857,8 +871,8 @@ RuleBasedNumberFormat::operator=(const RuleBasedNumberFormat& rhs)
lenient = rhs.lenient;
UParseError perror;
init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
setDecimalFormatSymbols(*rhs.getDecimalFormatSymbols());
init(rhs.originalDescription, rhs.localizations ? rhs.localizations->ref() : NULL, perror, status);
setDefaultRuleSet(rhs.getDefaultRuleSetName(), status);
capitalizationInfoSet = rhs.capitalizationInfoSet;
@ -936,9 +950,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;
@ -959,8 +974,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;
@ -1096,13 +1112,7 @@ RuleBasedNumberFormat::format(double number,
FieldPosition& /* pos */) const
{
int32_t startPos = 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) {
if (defaultRuleSet) {
UErrorCode status = U_ZERO_ERROR;
defaultRuleSet->format(number, toAppendTo, toAppendTo.length(), 0, status);
}
@ -1294,13 +1304,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
@ -1308,12 +1318,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) {
@ -1348,6 +1358,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);
@ -1445,7 +1462,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
int32_t start = 0;
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;
return;
@ -1454,7 +1471,7 @@ RuleBasedNumberFormat::init(const UnicodeString& rules, LocalizationInfo* locali
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;
return;
@ -1477,7 +1494,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);
}
}
@ -1609,6 +1626,7 @@ RuleBasedNumberFormat::dispose()
if (ruleSetDescriptions) {
delete [] ruleSetDescriptions;
ruleSetDescriptions = NULL;
}
#if !UCONFIG_NO_COLLATION
@ -1619,15 +1637,23 @@ RuleBasedNumberFormat::dispose()
delete decimalFormatSymbols;
decimalFormatSymbols = NULL;
delete defaultInfinityRule;
defaultInfinityRule = NULL;
delete defaultNaNRule;
defaultNaNRule = NULL;
delete lenientParseRules;
lenientParseRules = NULL;
#if !UCONFIG_NO_BREAK_ITERATION
delete capitalizationBrkIter;
capitalizationBrkIter = NULL;
delete capitalizationBrkIter;
capitalizationBrkIter = NULL;
#endif
if (localizations) localizations = localizations->unref();
if (localizations) {
localizations = localizations->unref();
}
}
@ -1691,31 +1717,80 @@ 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 (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 (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)
@ -1734,8 +1809,18 @@ RuleBasedNumberFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsTo
// Apply the new decimalFormatSymbols by reparsing the rulesets
UErrorCode status = U_ZERO_ERROR;
for (int32_t i = 0; i < numRuleSets; i++) {
ruleSets[i]->setDecimalFormatSymbols(*symbolsToAdopt, status);
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);
}
}
}
}

View File

@ -38,6 +38,7 @@
U_NAMESPACE_BEGIN
class NFRule;
class NFRuleSet;
class LocalizationInfo;
class PluralFormat;
@ -264,17 +265,47 @@ enum URBNFRuleSetTag {
* </tr>
* <tr>
* <td>x.x:</td>
* <td>The rule is an <em>improper fraction rule.</em></td>
* <td>The rule is an <em>improper fraction rule</em>. If the full stop in
* the middle of the rule name is replaced with the decimal point
* that is used in the language or DecimalFormatSymbols, then that rule will
* have precedence when formatting and parsing this rule. For example, some
* languages use the comma, and can thus be written as x,x instead. For example,
* you can use "x.x: &lt;&lt; point &gt;&gt;;x,x: &lt;&lt; comma &gt;&gt;;" to
* handle the decimal point that matches the language's natural spelling of
* the punctuation of either the full stop or comma.</td>
* </tr>
* <tr>
* <td>0.x:</td>
* <td>The rule is a <em>proper fraction rule.</em></td>
* <td>The rule is a <em>proper fraction rule</em>. If the full stop in
* the middle of the rule name is replaced with the decimal point
* that is used in the language or DecimalFormatSymbols, then that rule will
* have precedence when formatting and parsing this rule. For example, some
* languages use the comma, and can thus be written as 0,x instead. For example,
* you can use "0.x: point &gt;&gt;;0,x: comma &gt;&gt;;" to
* handle the decimal point that matches the language's natural spelling of
* the punctuation of either the full stop or comma.</td>
* </tr>
* <tr>
* <td>x.0:</td>
* <td>The rule is a <em>master rule.</em></td>
* <td>The rule is a <em>master rule</em>. If the full stop in
* the middle of the rule name is replaced with the decimal point
* that is used in the language or DecimalFormatSymbols, then that rule will
* have precedence when formatting and parsing this rule. For example, some
* languages use the comma, and can thus be written as x,0 instead. For example,
* you can use "x.0: &lt;&lt; point;x,0: &lt;&lt; comma;" to
* handle the decimal point that matches the language's natural spelling of
* the punctuation of either the full stop or comma.</td>
* </tr>
* <tr>
* <td>Inf:</td>
* <td>The rule for infinity.</td>
* </tr>
* <tr>
* <td>NaN:</td>
* <td>The rule for an IEEE 754 NaN (not a number).</td>
* </tr>
* <tr>
* <tr>
* <td><em>nothing</em></td>
* <td>If the rule's rule descriptor is left out, the base value is one plus the
* preceding rule's base value (or zero if this is the first rule in the list) in a normal
@ -980,11 +1011,17 @@ private:
/* friend access */
friend class NFSubstitution;
friend class NFRule;
friend class NFRuleSet;
friend class FractionalPartSubstitution;
inline NFRuleSet * getDefaultRuleSet() const;
const RuleBasedCollator * getCollator() const;
DecimalFormatSymbols * getDecimalFormatSymbols() const;
DecimalFormatSymbols * initializeDecimalFormatSymbols(UErrorCode &status);
const DecimalFormatSymbols * getDecimalFormatSymbols() const;
NFRule * initializeDefaultInfinityRule(UErrorCode &status);
const NFRule * getDefaultInfinityRule() const;
NFRule * initializeDefaultNaNRule(UErrorCode &status);
const NFRule * getDefaultNaNRule() const;
PluralFormat *createPluralFormat(UPluralType pluralType, const UnicodeString &pattern, UErrorCode& status) const;
UnicodeString& adjustForCapitalizationContext(int32_t startPos, UnicodeString& currentResult) const;
@ -996,6 +1033,8 @@ private:
Locale locale;
RuleBasedCollator* collator;
DecimalFormatSymbols* decimalFormatSymbols;
NFRule *defaultInfinityRule;
NFRule *defaultNaNRule;
UBool lenient;
UnicodeString* lenientParseRules;
LocalizationInfo* localizations;

View File

@ -974,8 +974,8 @@ UBool IntlTest::logKnownIssue(const char *ticket, const UnicodeString &msg) {
char fullpath[2048];
strcpy(fullpath, basePath);
strcat(fullpath, currName);
UnicodeString msg2 =msg;
UBool firstForTicket, firstForWhere;
UnicodeString msg2 = msg;
UBool firstForTicket = TRUE, firstForWhere = TRUE;
knownList = udbg_knownIssue_openU(knownList, ticket, fullpath, msg2.getTerminatedBuffer(), &firstForTicket, &firstForWhere);
msg2 = UNICODE_STRING_SIMPLE("(Known issue #") +

View File

@ -19,6 +19,7 @@
#include "unicode/ustring.h"
#include "unicode/decimfmt.h"
#include "unicode/udata.h"
#include "putilimp.h"
#include "testutil.h"
#include <string.h>
@ -67,6 +68,8 @@ void IntlTestRBNF::runIndexedTest(int32_t index, UBool exec, const char* &name,
TESTCASE(19, TestSetDecimalFormatSymbols);
TESTCASE(20, TestPluralRules);
TESTCASE(21, TestMultiplePluralRules);
TESTCASE(22, TestInfinityNaN);
TESTCASE(23, TestVariableDecimalPoint);
#else
TESTCASE(0, TestRBNFDisabled);
#endif
@ -2106,6 +2109,93 @@ void IntlTestRBNF::TestPluralRules() {
}
void IntlTestRBNF::TestInfinityNaN() {
UErrorCode status = U_ZERO_ERROR;
UParseError parseError;
UnicodeString enRules("%default:"
"-x: minus >>;"
"Inf: infinite;"
"NaN: not a number;"
"0: =#,##0=;");
RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
const char * const enTestData[][2] = {
{"1", "1"},
{"\\u221E", "infinite"},
{"-\\u221E", "minus infinite"},
{"NaN", "not a number"},
{ NULL, NULL }
};
if (U_FAILURE(status)) {
errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
return;
}
doTest(&enFormatter, enTestData, true);
// Test the default behavior when the rules are undefined.
UnicodeString enRules2("%default:"
"-x: ->>;"
"0: =#,##0=;");
RuleBasedNumberFormat enFormatter2(enRules2, Locale::getEnglish(), parseError, status);
if (U_FAILURE(status)) {
errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
return;
}
const char * const enDefaultTestData[][2] = {
{"1", "1"},
{"\\u221E", "\\u221E"},
{"-\\u221E", "-\\u221E"},
{"NaN", "NaN"},
{ NULL, NULL }
};
doTest(&enFormatter2, enDefaultTestData, true);
}
void IntlTestRBNF::TestVariableDecimalPoint() {
UErrorCode status = U_ZERO_ERROR;
UParseError parseError;
UnicodeString enRules("%spellout-numbering:"
"-x: minus >>;"
"x.x: << point >>;"
"x,x: << comma >>;"
"0.x: xpoint >>;"
"0,x: xcomma >>;"
"0: zero;"
"1: one;"
"2: two;"
"3: three;"
"4: four;"
"5: five;"
"6: six;"
"7: seven;"
"8: eight;"
"9: nine;");
RuleBasedNumberFormat enFormatter(enRules, Locale::getEnglish(), parseError, status);
const char * const enTestPointData[][2] = {
{"1.1", "one point one"},
{"1.23", "one point two three"},
{"0.4", "xpoint four"},
{ NULL, NULL }
};
if (U_FAILURE(status)) {
errln("Unable to create RuleBasedNumberFormat - " + UnicodeString(u_errorName(status)));
return;
}
doTest(&enFormatter, enTestPointData, true);
DecimalFormatSymbols decimalFormatSymbols(Locale::getEnglish(), status);
decimalFormatSymbols.setSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol, UNICODE_STRING_SIMPLE(","));
enFormatter.setDecimalFormatSymbols(decimalFormatSymbols);
const char * const enTestCommaData[][2] = {
{"1.1", "one comma one"},
{"1.23", "one comma two three"},
{"0.4", "xcomma four"},
{ NULL, NULL }
};
doTest(&enFormatter, enTestCommaData, true);
}
void
IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing)
{
@ -2124,7 +2214,8 @@ IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
log("[%i] %s = ", i, numString);
Formattable expectedNumber;
decFmt.parse(numString, expectedNumber, status);
UnicodeString escapedNumString = UnicodeString(numString, -1, US_INV).unescape();
decFmt.parse(escapedNumString, expectedNumber, status);
if (U_FAILURE(status)) {
errln("FAIL: decFmt could not parse %s", numString);
break;
@ -2161,7 +2252,9 @@ IntlTestRBNF::doTest(RuleBasedNumberFormat* formatter, const char* const testDat
errln(msg);
break;
} else {
if (parsedNumber != expectedNumber) {
if (parsedNumber != expectedNumber
&& (!uprv_isNaN(parsedNumber.getDouble()) || !uprv_isNaN(expectedNumber.getDouble())))
{
UnicodeString msg = "FAIL: parse failed for ";
msg.append(actualString);
msg.append(", expected ");

View File

@ -47,100 +47,103 @@ class IntlTestRBNF : public IntlTest {
/**
* Perform a simple spot check on the English spellout rules
*/
virtual void TestEnglishSpellout();
void TestEnglishSpellout();
/**
* Perform a simple spot check on the English ordinal-abbreviation rules
*/
virtual void TestOrdinalAbbreviations();
void TestOrdinalAbbreviations();
/**
* Perform a simple spot check on the duration-formatting rules
*/
virtual void TestDurations();
void TestDurations();
/**
* Perform a simple spot check on the Spanish spellout rules
*/
virtual void TestSpanishSpellout();
void TestSpanishSpellout();
/**
* Perform a simple spot check on the French spellout rules
*/
virtual void TestFrenchSpellout();
void TestFrenchSpellout();
/**
* Perform a simple spot check on the Swiss French spellout rules
*/
virtual void TestSwissFrenchSpellout();
void TestSwissFrenchSpellout();
/**
* Check that Belgian French matches Swiss French spellout rules
*/
virtual void TestBelgianFrenchSpellout();
void TestBelgianFrenchSpellout();
/**
* Perform a simple spot check on the Italian spellout rules
*/
virtual void TestItalianSpellout();
void TestItalianSpellout();
/**
* Perform a simple spot check on the Portuguese spellout rules
*/
virtual void TestPortugueseSpellout();
void TestPortugueseSpellout();
/**
* Perform a simple spot check on the German spellout rules
*/
virtual void TestGermanSpellout();
void TestGermanSpellout();
/**
* Perform a simple spot check on the Thai spellout rules
*/
virtual void TestThaiSpellout();
void TestThaiSpellout();
/**
* Perform a simple spot check on the Swedish spellout rules
*/
virtual void TestSwedishSpellout();
void TestSwedishSpellout();
/**
* Perform a simple spot check on small values
*/
virtual void TestSmallValues();
void TestSmallValues();
/**
* Test localizations using string data.
*/
virtual void TestLocalizations();
void TestLocalizations();
/**
* Test that all locales construct ok.
*/
virtual void TestAllLocales();
void TestAllLocales();
/**
* Test that hebrew fractions format without trailing '<'
*/
virtual void TestHebrewFraction();
void TestHebrewFraction();
/**
* Regression test, don't truncate
* when doing multiplier substitution to a number format rule.
*/
virtual void TestMultiplierSubstitution();
void TestMultiplierSubstitution();
/**
* Test the setDecimalFormatSymbols in RBNF
*/
virtual void TestSetDecimalFormatSymbols();
void TestSetDecimalFormatSymbols();
/**
* Test the plural rules in RBNF
*/
virtual void TestPluralRules();
void TestPluralRules();
protected:
void TestInfinityNaN();
void TestVariableDecimalPoint();
protected:
virtual void doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing);
virtual void doLenientParseTest(RuleBasedNumberFormat* formatter, const char* testData[][2]);

View File

@ -1,6 +1,6 @@
/***********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2011, International Business Machines Corporation
* Copyright (c) 1997-2015, International Business Machines Corporation
* and others. All Rights Reserved.
***********************************************************************/
@ -26,7 +26,7 @@
UBool NumberFormatRoundTripTest::verbose = FALSE;
UBool NumberFormatRoundTripTest::STRING_COMPARE = TRUE;
UBool NumberFormatRoundTripTest::EXACT_NUMERIC_COMPARE = FALSE;
UBool NumberFormatRoundTripTest::DEBUG = FALSE;
UBool NumberFormatRoundTripTest::DEBUG_VAR = FALSE;
double NumberFormatRoundTripTest::MAX_ERROR = 1e-14;
double NumberFormatRoundTripTest::max_numeric_error = 0.0;
double NumberFormatRoundTripTest::min_numeric_error = 1.0;
@ -243,12 +243,12 @@ NumberFormatRoundTripTest::test(NumberFormat *fmt, const Formattable& value)
Formattable n;
UBool show = verbose;
if(DEBUG)
if(DEBUG_VAR)
logln(/*value.getString(temp) +*/ " F> " + escape(s));
fmt->parse(s, n, status);
failure(status, "fmt->parse");
if(DEBUG)
if(DEBUG_VAR)
logln(escape(s) + " P> " /*+ n.getString(temp)*/);
if(isDouble(n))
@ -256,7 +256,7 @@ NumberFormatRoundTripTest::test(NumberFormat *fmt, const Formattable& value)
else
s2 = fmt->format(n.getLong(), s2);
if(DEBUG)
if(DEBUG_VAR)
logln(/*n.getString(temp) +*/ " F> " + escape(s2));
if(STRING_COMPARE) {

View File

@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
* Copyright (c) 1997-2009, International Business Machines Corporation and
* Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@ -27,7 +27,7 @@ public:
static UBool verbose;
static UBool STRING_COMPARE;
static UBool EXACT_NUMERIC_COMPARE;
static UBool DEBUG;
static UBool DEBUG_VAR;
static double MAX_ERROR;
static double max_numeric_error;
static double min_numeric_error;