ICU-11064 Fix a roundtripping problem when the fractional part is non-zero.

X-SVN-Rev: 36203
This commit is contained in:
George Rhoten 2014-08-19 17:13:07 +00:00
parent 5c12dc4923
commit 912521e5cf
4 changed files with 65 additions and 19 deletions

View file

@ -54,6 +54,7 @@ NFRule::~NFRule()
static const UChar gLeftBracket = 0x005b;
static const UChar gRightBracket = 0x005d;
static const UChar gClosedParenthesis = 0x0029;
static const UChar gColon = 0x003a;
static const UChar gZero = 0x0030;
static const UChar gNine = 0x0039;
@ -73,7 +74,6 @@ static const UChar gXDotZero[] = {0x78, 0x2E, 0x30, 0}; /* "x.0"
static const UChar gZeroDotX[] = {0x30, 0x2E, 0x78, 0}; /* "0.x" */
static const UChar gDollarOpenParenthesis[] = {0x24, 0x28, 0}; /* "$(" */
static const UChar gClosedParenthesis[] = {0x29, 0}; /* ")" */
static const UChar gLessLess[] = {0x3C, 0x3C, 0}; /* "<<" */
static const UChar gLessPercent[] = {0x3C, 0x25, 0}; /* "<%" */
@ -393,13 +393,15 @@ NFRule::extractSubstitutions(const NFRuleSet* ruleSet,
else {
sub2 = extractSubstitution(ruleSet, predecessor, status);
}
if (this->ruleText.startsWith(gDollarOpenParenthesis, -1) && this->ruleText.endsWith(gClosedParenthesis, -1)) {
int32_t endType = this->ruleText.indexOf(gComma);
int32_t pluralRuleStart = this->ruleText.indexOf(gDollarOpenParenthesis, -1, 0);
int32_t pluralRuleEnd = (pluralRuleStart >= 0 ? this->ruleText.indexOf(gClosedParenthesis, pluralRuleStart) : -1);
if (pluralRuleEnd >= 0) {
int32_t endType = this->ruleText.indexOf(gComma, pluralRuleStart);
if (endType < 0) {
status = U_PARSE_ERROR;
return;
}
UnicodeString type(this->ruleText.tempSubString(2, endType - 2));
UnicodeString type(this->ruleText.tempSubString(pluralRuleStart + 2, endType - pluralRuleStart - 2));
UPluralType pluralType;
if (type.startsWith(UNICODE_STRING_SIMPLE("cardinal"))) {
pluralType = UPLURAL_TYPE_CARDINAL;
@ -412,7 +414,7 @@ NFRule::extractSubstitutions(const NFRuleSet* ruleSet,
return;
}
rulePatternFormat = formatter->createPluralFormat(pluralType,
this->ruleText.tempSubString(endType + 1, this->ruleText.length() - 2 - endType), status);
this->ruleText.tempSubString(endType + 1, pluralRuleEnd - endType - 1), status);
}
}
@ -687,19 +689,31 @@ NFRule::doFormat(int64_t number, UnicodeString& toInsertInto, int32_t pos, UErro
// into the right places in toInsertInto (notice we do the
// substitutions in reverse order so that the offsets don't get
// messed up)
int32_t pluralRuleStart = ruleText.length();
int32_t lengthOffset = 0;
if (!rulePatternFormat) {
toInsertInto.insert(pos, ruleText);
}
else {
pluralRuleStart = ruleText.indexOf(gDollarOpenParenthesis, -1, 0);
int pluralRuleEnd = ruleText.indexOf(gClosedParenthesis, pluralRuleStart);
int initialLength = toInsertInto.length();
if (pluralRuleEnd < ruleText.length() - 1) {
toInsertInto.insert(pos, ruleText.tempSubString(pluralRuleEnd + 1));
}
toInsertInto.insert(pos,
rulePatternFormat->format((double)(baseValue == 0 ? number : number/baseValue), status));
rulePatternFormat->format((int32_t)(number/uprv_pow(radix, exponent)), status));
if (pluralRuleStart > 0) {
toInsertInto.insert(pos, ruleText.tempSubString(0, pluralRuleStart));
}
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
}
if (!sub2->isNullSubstitution()) {
sub2->doSubstitution(number, toInsertInto, pos, status);
sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), status);
}
if (!sub1->isNullSubstitution()) {
sub1->doSubstitution(number, toInsertInto, pos, status);
sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), status);
}
}
@ -721,18 +735,31 @@ NFRule::doFormat(double number, UnicodeString& toInsertInto, int32_t pos, UError
// [again, we have two copies of this routine that do the same thing
// so that we don't sacrifice precision in a long by casting it
// to a double]
int32_t pluralRuleStart = ruleText.length();
int32_t lengthOffset = 0;
if (!rulePatternFormat) {
toInsertInto.insert(pos, ruleText);
}
else {
pluralRuleStart = ruleText.indexOf(gDollarOpenParenthesis, -1, 0);
int pluralRuleEnd = ruleText.indexOf(gClosedParenthesis, pluralRuleStart);
int initialLength = toInsertInto.length();
if (pluralRuleEnd < ruleText.length() - 1) {
toInsertInto.insert(pos, ruleText.tempSubString(pluralRuleEnd + 1));
}
toInsertInto.insert(pos,
rulePatternFormat->format(baseValue == 0 ? number : number/baseValue, status));
rulePatternFormat->format((int32_t)(number/uprv_pow(radix, exponent)), status));
if (pluralRuleStart > 0) {
toInsertInto.insert(pos, ruleText.tempSubString(0, pluralRuleStart));
}
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
}
if (!sub2->isNullSubstitution()) {
sub2->doSubstitution(number, toInsertInto, pos, status);
sub2->doSubstitution(number, toInsertInto, pos - (sub2->getPos() > pluralRuleStart ? lengthOffset : 0), status);
}
if (!sub1->isNullSubstitution()) {
sub1->doSubstitution(number, toInsertInto, pos, status);
sub1->doSubstitution(number, toInsertInto, pos - (sub1->getPos() > pluralRuleStart ? lengthOffset : 0), status);
}
}
@ -1372,8 +1399,17 @@ NFRule::findText(const UnicodeString& str,
rulePatternFormat->parseType(str, this, result, position);
int start = position.getBeginIndex();
if (start >= 0) {
*length = position.getEndIndex() - start;
return start;
int32_t pluralRuleStart = ruleText.indexOf(gDollarOpenParenthesis, -1, 0);
int32_t pluralRuleSuffix = ruleText.indexOf(gClosedParenthesis, pluralRuleStart) + 1;
int32_t matchLen = position.getEndIndex() - start;
UnicodeString prefix(ruleText.tempSubString(0, pluralRuleStart));
UnicodeString suffix(ruleText.tempSubString(pluralRuleSuffix));
if (str.compare(start - prefix.length(), prefix.length(), prefix, 0, prefix.length()) == 0
&& str.compare(start + matchLen, suffix.length(), suffix, 0, suffix.length()) == 0)
{
*length = matchLen + prefix.length() + suffix.length();
return start - prefix.length();
}
}
*length = 0;
return -1;

View file

@ -535,7 +535,7 @@ void PluralFormat::parseType(const UnicodeString& source, const NFRule *rbnfLeni
else {
currMatchIndex = source.indexOf(currArg);
}
if (currMatchIndex > matchedIndex && currArg.length() > matchedWord.length()) {
if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) {
matchedIndex = currMatchIndex;
matchedWord = currArg;
keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit());

View file

@ -434,15 +434,19 @@ enum URBNFRuleSetTag {
* <td width="37">$(cardinal,<i>plural syntax</i>)</td>
* <td width="23"></td>
* <td width="165" valign="top">in all rule sets</td>
* <td>This provides the ability to choose a word based on the number divided by the base value for the specified locale.
* This uses the cardinal plural rules from PluralFormat. All strings used in the plural format are treated as the same base value for parsing.</td>
* <td>This provides the ability to choose a word based on the number divided by the radix to the power of the
* exponent of the base value for the specified locale, which is normally equivalent to the &lt;&lt; value.
* This uses the cardinal plural rules from PluralFormat. All strings used in the plural format are treated
* as the same base value for parsing.</td>
* </tr>
* <tr>
* <td width="37">$(ordinal,<i>plural syntax</i>)</td>
* <td width="23"></td>
* <td width="165" valign="top">in all rule sets</td>
* <td>This provides the ability to choose a word based on the number divided by the base value for the specified locale.
* This uses the ordinal plural rules from PluralFormat. All strings used in the plural format are treated as the same base value for parsing.</td>
* <td>This provides the ability to choose a word based on the number divided by the radix to the power of the
* exponent of the base value for the specified locale, which is normally equivalent to the &lt;&lt; value.
* This uses the ordinal plural rules from PluralFormat. All strings used in the plural format are treated
* as the same base value for parsing.</td>
* </tr>
* </table>
*

View file

@ -2023,7 +2023,8 @@ void IntlTestRBNF::TestPluralRules() {
"200: << hundred[ >>];"
"300: << hundreds[ >>];"
"500: << hundredss[ >>];"
"1000: <<$(cardinal,one{ thousand}few{ thousands}other{ thousandss})[ >>];");
"1000: << $(cardinal,one{thousand}few{thousands}other{thousandss})[ >>];"
"1000000: << $(cardinal,one{million}few{millions}other{millionss})[ >>];");
RuleBasedNumberFormat ruFormatter(ruRules, Locale("ru"), parseError, status);
const char* const ruTestData[][2] = {
{ "1", "one" },
@ -2031,8 +2032,13 @@ void IntlTestRBNF::TestPluralRules() {
{ "125", "hundred twenty-five" },
{ "399", "three hundreds ninety-nine" },
{ "1,000", "one thousand" },
{ "1,001", "one thousand one" },
{ "2,000", "two thousands" },
{ "2,001", "two thousands one" },
{ "2,002", "two thousands two" },
{ "3,333", "three thousands three hundreds thirty-three" },
{ "5,000", "five thousandss" },
{ "11,000", "eleven thousandss" },
{ "21,000", "twenty-one thousand" },
{ "22,000", "twenty-two thousands" },
{ NULL, NULL }