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:
parent
056237d6db
commit
e097e1b9ee
@ -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..
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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: << point >>;x,x: << 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>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 >>;0,x: 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>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: << point;x,0: << 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;
|
||||
|
@ -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 #") +
|
||||
|
@ -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 ");
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user