1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2007-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
15 #include "unicode/utypes.h"
16 #include "unicode/localpointer.h"
17 #include "unicode/plurrule.h"
18 #include "unicode/upluralrules.h"
19 #include "unicode/ures.h"
20 #include "unicode/numfmt.h"
21 #include "unicode/decimfmt.h"
29 #include "patternprops.h"
30 #include "plurrule_impl.h"
36 #include "sharedpluralrules.h"
37 #include "unifiedcache.h"
38 #include "digitinterval.h"
39 #include "visibledigits.h"
41 #if !UCONFIG_NO_FORMATTING
45 static const UChar PLURAL_KEYWORD_OTHER
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,0};
46 static const UChar PLURAL_DEFAULT_RULE
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,COLON
,SPACE
,LOW_N
,0};
47 static const UChar PK_IN
[]={LOW_I
,LOW_N
,0};
48 static const UChar PK_NOT
[]={LOW_N
,LOW_O
,LOW_T
,0};
49 static const UChar PK_IS
[]={LOW_I
,LOW_S
,0};
50 static const UChar PK_MOD
[]={LOW_M
,LOW_O
,LOW_D
,0};
51 static const UChar PK_AND
[]={LOW_A
,LOW_N
,LOW_D
,0};
52 static const UChar PK_OR
[]={LOW_O
,LOW_R
,0};
53 static const UChar PK_VAR_N
[]={LOW_N
,0};
54 static const UChar PK_VAR_I
[]={LOW_I
,0};
55 static const UChar PK_VAR_F
[]={LOW_F
,0};
56 static const UChar PK_VAR_T
[]={LOW_T
,0};
57 static const UChar PK_VAR_V
[]={LOW_V
,0};
58 static const UChar PK_WITHIN
[]={LOW_W
,LOW_I
,LOW_T
,LOW_H
,LOW_I
,LOW_N
,0};
59 static const UChar PK_DECIMAL
[]={LOW_D
,LOW_E
,LOW_C
,LOW_I
,LOW_M
,LOW_A
,LOW_L
,0};
60 static const UChar PK_INTEGER
[]={LOW_I
,LOW_N
,LOW_T
,LOW_E
,LOW_G
,LOW_E
,LOW_R
,0};
62 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules
)
63 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration
)
65 PluralRules::PluralRules(UErrorCode
& /*status*/)
71 PluralRules::PluralRules(const PluralRules
& other
)
78 PluralRules::~PluralRules() {
82 SharedPluralRules::~SharedPluralRules() {
87 PluralRules::clone() const {
88 return new PluralRules(*this);
92 PluralRules::operator=(const PluralRules
& other
) {
95 if (other
.mRules
==NULL
) {
99 mRules
= new RuleChain(*other
.mRules
);
106 StringEnumeration
* PluralRules::getAvailableLocales(UErrorCode
&status
) {
107 StringEnumeration
*result
= new PluralAvailableLocalesEnumeration(status
);
108 if (result
== NULL
&& U_SUCCESS(status
)) {
109 status
= U_MEMORY_ALLOCATION_ERROR
;
111 if (U_FAILURE(status
)) {
119 PluralRules
* U_EXPORT2
120 PluralRules::createRules(const UnicodeString
& description
, UErrorCode
& status
) {
121 if (U_FAILURE(status
)) {
125 PluralRuleParser parser
;
126 PluralRules
*newRules
= new PluralRules(status
);
127 if (U_SUCCESS(status
) && newRules
== NULL
) {
128 status
= U_MEMORY_ALLOCATION_ERROR
;
130 parser
.parse(description
, newRules
, status
);
131 if (U_FAILURE(status
)) {
139 PluralRules
* U_EXPORT2
140 PluralRules::createDefaultRules(UErrorCode
& status
) {
141 return createRules(UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1), status
);
144 /******************************************************************************/
145 /* Create PluralRules cache */
147 template<> U_I18N_API
148 const SharedPluralRules
*LocaleCacheKey
<SharedPluralRules
>::createObject(
149 const void * /*unused*/, UErrorCode
&status
) const {
150 const char *localeId
= fLoc
.getName();
151 PluralRules
*pr
= PluralRules::internalForLocale(
152 localeId
, UPLURAL_TYPE_CARDINAL
, status
);
153 if (U_FAILURE(status
)) {
156 SharedPluralRules
*result
= new SharedPluralRules(pr
);
157 if (result
== NULL
) {
158 status
= U_MEMORY_ALLOCATION_ERROR
;
166 /* end plural rules cache */
167 /******************************************************************************/
169 const SharedPluralRules
* U_EXPORT2
170 PluralRules::createSharedInstance(
171 const Locale
& locale
, UPluralType type
, UErrorCode
& status
) {
172 if (U_FAILURE(status
)) {
175 if (type
!= UPLURAL_TYPE_CARDINAL
) {
176 status
= U_UNSUPPORTED_ERROR
;
179 const SharedPluralRules
*result
= NULL
;
180 UnifiedCache::getByLocale(locale
, result
, status
);
184 PluralRules
* U_EXPORT2
185 PluralRules::forLocale(const Locale
& locale
, UErrorCode
& status
) {
186 return forLocale(locale
, UPLURAL_TYPE_CARDINAL
, status
);
189 PluralRules
* U_EXPORT2
190 PluralRules::forLocale(const Locale
& locale
, UPluralType type
, UErrorCode
& status
) {
191 if (type
!= UPLURAL_TYPE_CARDINAL
) {
192 return internalForLocale(locale
, type
, status
);
194 const SharedPluralRules
*shared
= createSharedInstance(
195 locale
, type
, status
);
196 if (U_FAILURE(status
)) {
199 PluralRules
*result
= (*shared
)->clone();
201 if (result
== NULL
) {
202 status
= U_MEMORY_ALLOCATION_ERROR
;
207 PluralRules
* U_EXPORT2
208 PluralRules::internalForLocale(const Locale
& locale
, UPluralType type
, UErrorCode
& status
) {
209 if (U_FAILURE(status
)) {
212 if (type
>= UPLURAL_TYPE_COUNT
) {
213 status
= U_ILLEGAL_ARGUMENT_ERROR
;
216 PluralRules
*newObj
= new PluralRules(status
);
217 if (newObj
==NULL
|| U_FAILURE(status
)) {
221 UnicodeString locRule
= newObj
->getRuleFromResource(locale
, type
, status
);
222 // TODO: which errors, if any, should be returned?
223 if (locRule
.length() == 0) {
224 // Locales with no specific rules (all numbers have the "other" category
225 // will return a U_MISSING_RESOURCE_ERROR at this point. This is not
227 locRule
= UnicodeString(PLURAL_DEFAULT_RULE
);
228 status
= U_ZERO_ERROR
;
230 PluralRuleParser parser
;
231 parser
.parse(locRule
, newObj
, status
);
232 // TODO: should rule parse errors be returned, or
233 // should we silently use default rules?
234 // Original impl used default rules.
235 // Ask the question to ICU Core.
241 PluralRules::select(int32_t number
) const {
242 return select(FixedDecimal(number
));
246 PluralRules::select(double number
) const {
247 return select(FixedDecimal(number
));
251 PluralRules::select(const Formattable
& obj
, const NumberFormat
& fmt
, UErrorCode
& status
) const {
252 if (U_SUCCESS(status
)) {
253 const DecimalFormat
*decFmt
= dynamic_cast<const DecimalFormat
*>(&fmt
);
254 if (decFmt
!= NULL
) {
255 VisibleDigitsWithExponent digits
;
256 decFmt
->initVisibleDigitsWithExponent(obj
, digits
, status
);
257 if (U_SUCCESS(status
)) {
258 return select(digits
);
261 double number
= obj
.getDouble(status
);
262 if (U_SUCCESS(status
)) {
263 return select(number
);
267 return UnicodeString();
271 PluralRules::select(const IFixedDecimal
&number
) const {
272 if (mRules
== NULL
) {
273 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
276 return mRules
->select(number
);
281 PluralRules::select(const VisibleDigitsWithExponent
&number
) const {
282 if (number
.getExponent() != NULL
) {
283 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
285 return select(FixedDecimal(number
.getMantissa()));
291 PluralRules::getKeywords(UErrorCode
& status
) const {
292 if (U_FAILURE(status
)) return NULL
;
293 StringEnumeration
* nameEnumerator
= new PluralKeywordEnumeration(mRules
, status
);
294 if (U_FAILURE(status
)) {
295 delete nameEnumerator
;
299 return nameEnumerator
;
303 PluralRules::getUniqueKeywordValue(const UnicodeString
& /* keyword */) {
305 return UPLRULES_NO_UNIQUE_VALUE
;
309 PluralRules::getAllKeywordValues(const UnicodeString
& /* keyword */, double * /* dest */,
310 int32_t /* destCapacity */, UErrorCode
& error
) {
311 error
= U_UNSUPPORTED_ERROR
;
316 static double scaleForInt(double d
) {
318 while (d
!= floor(d
)) {
320 scale
= scale
* 10.0;
326 getSamplesFromString(const UnicodeString
&samples
, double *dest
,
327 int32_t destCapacity
, UErrorCode
& status
) {
328 int32_t sampleCount
= 0;
329 int32_t sampleStartIdx
= 0;
330 int32_t sampleEndIdx
= 0;
332 //std::string ss; // TODO: debugging.
333 // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n";
334 for (sampleCount
= 0; sampleCount
< destCapacity
&& sampleStartIdx
< samples
.length(); ) {
335 sampleEndIdx
= samples
.indexOf(COMMA
, sampleStartIdx
);
336 if (sampleEndIdx
== -1) {
337 sampleEndIdx
= samples
.length();
339 const UnicodeString
&sampleRange
= samples
.tempSubStringBetween(sampleStartIdx
, sampleEndIdx
);
341 // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n";
342 int32_t tildeIndex
= sampleRange
.indexOf(TILDE
);
343 if (tildeIndex
< 0) {
344 FixedDecimal
fixed(sampleRange
, status
);
345 double sampleValue
= fixed
.source
;
346 if (fixed
.visibleDecimalDigitCount
== 0 || sampleValue
!= floor(sampleValue
)) {
347 dest
[sampleCount
++] = sampleValue
;
351 FixedDecimal
fixedLo(sampleRange
.tempSubStringBetween(0, tildeIndex
), status
);
352 FixedDecimal
fixedHi(sampleRange
.tempSubStringBetween(tildeIndex
+1), status
);
353 double rangeLo
= fixedLo
.source
;
354 double rangeHi
= fixedHi
.source
;
355 if (U_FAILURE(status
)) {
358 if (rangeHi
< rangeLo
) {
359 status
= U_INVALID_FORMAT_ERROR
;
363 // For ranges of samples with fraction decimal digits, scale the number up so that we
364 // are adding one in the units place. Avoids roundoffs from repetitive adds of tenths.
366 double scale
= scaleForInt(rangeLo
);
367 double t
= scaleForInt(rangeHi
);
373 for (double n
=rangeLo
; n
<=rangeHi
; n
+=1) {
374 // Hack Alert: don't return any decimal samples with integer values that
375 // originated from a format with trailing decimals.
376 // This API is returning doubles, which can't distinguish having displayed
377 // zeros to the right of the decimal.
378 // This results in test failures with values mapping back to a different keyword.
379 double sampleValue
= n
/scale
;
380 if (!(sampleValue
== floor(sampleValue
) && fixedLo
.visibleDecimalDigitCount
> 0)) {
381 dest
[sampleCount
++] = sampleValue
;
383 if (sampleCount
>= destCapacity
) {
388 sampleStartIdx
= sampleEndIdx
+ 1;
395 PluralRules::getSamples(const UnicodeString
&keyword
, double *dest
,
396 int32_t destCapacity
, UErrorCode
& status
) {
397 RuleChain
*rc
= rulesForKeyword(keyword
);
398 if (rc
== NULL
|| destCapacity
== 0 || U_FAILURE(status
)) {
401 int32_t numSamples
= getSamplesFromString(rc
->fIntegerSamples
, dest
, destCapacity
, status
);
402 if (numSamples
== 0) {
403 numSamples
= getSamplesFromString(rc
->fDecimalSamples
, dest
, destCapacity
, status
);
409 RuleChain
*PluralRules::rulesForKeyword(const UnicodeString
&keyword
) const {
411 for (rc
= mRules
; rc
!= NULL
; rc
= rc
->fNext
) {
412 if (rc
->fKeyword
== keyword
) {
421 PluralRules::isKeyword(const UnicodeString
& keyword
) const {
422 if (0 == keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
425 return rulesForKeyword(keyword
) != NULL
;
429 PluralRules::getKeywordOther() const {
430 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
434 PluralRules::operator==(const PluralRules
& other
) const {
435 const UnicodeString
*ptrKeyword
;
436 UErrorCode status
= U_ZERO_ERROR
;
438 if ( this == &other
) {
441 LocalPointer
<StringEnumeration
> myKeywordList(getKeywords(status
));
442 LocalPointer
<StringEnumeration
> otherKeywordList(other
.getKeywords(status
));
443 if (U_FAILURE(status
)) {
447 if (myKeywordList
->count(status
)!=otherKeywordList
->count(status
)) {
450 myKeywordList
->reset(status
);
451 while ((ptrKeyword
=myKeywordList
->snext(status
))!=NULL
) {
452 if (!other
.isKeyword(*ptrKeyword
)) {
456 otherKeywordList
->reset(status
);
457 while ((ptrKeyword
=otherKeywordList
->snext(status
))!=NULL
) {
458 if (!this->isKeyword(*ptrKeyword
)) {
462 if (U_FAILURE(status
)) {
471 PluralRuleParser::parse(const UnicodeString
& ruleData
, PluralRules
*prules
, UErrorCode
&status
)
473 if (U_FAILURE(status
)) {
476 U_ASSERT(ruleIndex
== 0); // Parsers are good for a single use only!
479 while (ruleIndex
< ruleSrc
->length()) {
480 getNextToken(status
);
481 if (U_FAILURE(status
)) {
485 if (U_FAILURE(status
)) {
490 U_ASSERT(curAndConstraint
!= NULL
);
491 curAndConstraint
= curAndConstraint
->add();
495 U_ASSERT(currentChain
!= NULL
);
496 OrConstraint
*orNode
=currentChain
->ruleHeader
;
497 while (orNode
->next
!= NULL
) {
498 orNode
= orNode
->next
;
500 orNode
->next
= new OrConstraint();
503 curAndConstraint
= orNode
->add();
507 U_ASSERT(curAndConstraint
!= NULL
);
508 U_ASSERT(curAndConstraint
->value
== -1);
509 U_ASSERT(curAndConstraint
->rangeList
== NULL
);
512 U_ASSERT(curAndConstraint
!= NULL
);
513 curAndConstraint
->negated
=TRUE
;
517 curAndConstraint
->negated
=TRUE
;
522 U_ASSERT(curAndConstraint
!= NULL
);
523 curAndConstraint
->rangeList
= new UVector32(status
);
524 curAndConstraint
->rangeList
->addElement(-1, status
); // range Low
525 curAndConstraint
->rangeList
->addElement(-1, status
); // range Hi
528 curAndConstraint
->value
=PLURAL_RANGE_HIGH
;
529 curAndConstraint
->integerOnly
= (type
!= tWithin
);
532 U_ASSERT(curAndConstraint
!= NULL
);
533 if ( (curAndConstraint
->op
==AndConstraint::MOD
)&&
534 (curAndConstraint
->opNum
== -1 ) ) {
535 curAndConstraint
->opNum
=getNumberValue(token
);
538 if (curAndConstraint
->rangeList
== NULL
) {
539 // this is for an 'is' rule
540 curAndConstraint
->value
= getNumberValue(token
);
542 // this is for an 'in' or 'within' rule
543 if (curAndConstraint
->rangeList
->elementAti(rangeLowIdx
) == -1) {
544 curAndConstraint
->rangeList
->setElementAt(getNumberValue(token
), rangeLowIdx
);
545 curAndConstraint
->rangeList
->setElementAt(getNumberValue(token
), rangeHiIdx
);
548 curAndConstraint
->rangeList
->setElementAt(getNumberValue(token
), rangeHiIdx
);
549 if (curAndConstraint
->rangeList
->elementAti(rangeLowIdx
) >
550 curAndConstraint
->rangeList
->elementAti(rangeHiIdx
)) {
551 // Range Lower bound > Range Upper bound.
552 // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently
553 // used for all plural rule parse errors.
554 status
= U_UNEXPECTED_TOKEN
;
562 // TODO: rule syntax checking is inadequate, can happen with badly formed rules.
563 // Catch cases like "n mod 10, is 1" here instead.
564 if (curAndConstraint
== NULL
|| curAndConstraint
->rangeList
== NULL
) {
565 status
= U_UNEXPECTED_TOKEN
;
568 U_ASSERT(curAndConstraint
->rangeList
->size() >= 2);
569 rangeLowIdx
= curAndConstraint
->rangeList
->size();
570 curAndConstraint
->rangeList
->addElement(-1, status
); // range Low
571 rangeHiIdx
= curAndConstraint
->rangeList
->size();
572 curAndConstraint
->rangeList
->addElement(-1, status
); // range Hi
575 U_ASSERT(curAndConstraint
!= NULL
);
576 curAndConstraint
->op
=AndConstraint::MOD
;
583 U_ASSERT(curAndConstraint
!= NULL
);
584 curAndConstraint
->digitsType
= type
;
588 RuleChain
*newChain
= new RuleChain
;
589 if (newChain
== NULL
) {
590 status
= U_MEMORY_ALLOCATION_ERROR
;
593 newChain
->fKeyword
= token
;
594 if (prules
->mRules
== NULL
) {
595 prules
->mRules
= newChain
;
597 // The new rule chain goes at the end of the linked list of rule chains,
598 // unless there is an "other" keyword & chain. "other" must remain last.
599 RuleChain
*insertAfter
= prules
->mRules
;
600 while (insertAfter
->fNext
!=NULL
&&
601 insertAfter
->fNext
->fKeyword
.compare(PLURAL_KEYWORD_OTHER
, 5) != 0 ){
602 insertAfter
=insertAfter
->fNext
;
604 newChain
->fNext
= insertAfter
->fNext
;
605 insertAfter
->fNext
= newChain
;
607 OrConstraint
*orNode
= new OrConstraint();
608 newChain
->ruleHeader
= orNode
;
609 curAndConstraint
= orNode
->add();
610 currentChain
= newChain
;
616 getNextToken(status
);
617 if (U_FAILURE(status
) || type
== tSemiColon
|| type
== tEOF
|| type
== tAt
) {
620 if (type
== tEllipsis
) {
621 currentChain
->fIntegerSamplesUnbounded
= TRUE
;
624 currentChain
->fIntegerSamples
.append(token
);
630 getNextToken(status
);
631 if (U_FAILURE(status
) || type
== tSemiColon
|| type
== tEOF
|| type
== tAt
) {
634 if (type
== tEllipsis
) {
635 currentChain
->fDecimalSamplesUnbounded
= TRUE
;
638 currentChain
->fDecimalSamples
.append(token
);
646 if (U_FAILURE(status
)) {
653 PluralRules::getRuleFromResource(const Locale
& locale
, UPluralType type
, UErrorCode
& errCode
) {
654 UnicodeString emptyStr
;
656 if (U_FAILURE(errCode
)) {
659 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "plurals", &errCode
));
660 if(U_FAILURE(errCode
)) {
665 case UPLURAL_TYPE_CARDINAL
:
668 case UPLURAL_TYPE_ORDINAL
:
669 typeKey
= "locales_ordinals";
672 // Must not occur: The caller should have checked for valid types.
673 errCode
= U_ILLEGAL_ARGUMENT_ERROR
;
676 LocalUResourceBundlePointer
locRes(ures_getByKey(rb
.getAlias(), typeKey
, NULL
, &errCode
));
677 if(U_FAILURE(errCode
)) {
681 const char *curLocaleName
=locale
.getName();
682 const UChar
* s
= ures_getStringByKey(locRes
.getAlias(), curLocaleName
, &resLen
, &errCode
);
685 // Check parent locales.
686 UErrorCode status
= U_ZERO_ERROR
;
687 char parentLocaleName
[ULOC_FULLNAME_CAPACITY
];
688 const char *curLocaleName
=locale
.getName();
689 uprv_strcpy(parentLocaleName
, curLocaleName
);
691 while (uloc_getParent(parentLocaleName
, parentLocaleName
,
692 ULOC_FULLNAME_CAPACITY
, &status
) > 0) {
694 s
= ures_getStringByKey(locRes
.getAlias(), parentLocaleName
, &resLen
, &status
);
696 errCode
= U_ZERO_ERROR
;
699 status
= U_ZERO_ERROR
;
707 u_UCharsToChars(s
, setKey
, resLen
+ 1);
708 // printf("\n PluralRule: %s\n", setKey);
710 LocalUResourceBundlePointer
ruleRes(ures_getByKey(rb
.getAlias(), "rules", NULL
, &errCode
));
711 if(U_FAILURE(errCode
)) {
714 LocalUResourceBundlePointer
setRes(ures_getByKey(ruleRes
.getAlias(), setKey
, NULL
, &errCode
));
715 if (U_FAILURE(errCode
)) {
719 int32_t numberKeys
= ures_getSize(setRes
.getAlias());
720 UnicodeString result
;
721 const char *key
=NULL
;
722 for(int32_t i
=0; i
<numberKeys
; ++i
) { // Keys are zero, one, few, ...
723 UnicodeString rules
= ures_getNextUnicodeString(setRes
.getAlias(), &key
, &errCode
);
724 UnicodeString
uKey(key
, -1, US_INV
);
726 result
.append(COLON
);
727 result
.append(rules
);
728 result
.append(SEMI_COLON
);
735 PluralRules::getRules() const {
737 if (mRules
!= NULL
) {
738 mRules
->dumpRules(rules
);
744 AndConstraint::AndConstraint() {
745 op
= AndConstraint::NONE
;
756 AndConstraint::AndConstraint(const AndConstraint
& other
) {
758 this->opNum
=other
.opNum
;
759 this->value
=other
.value
;
760 this->rangeList
=NULL
;
761 if (other
.rangeList
!= NULL
) {
762 UErrorCode status
= U_ZERO_ERROR
;
763 this->rangeList
= new UVector32(status
);
764 this->rangeList
->assign(*other
.rangeList
, status
);
766 this->integerOnly
=other
.integerOnly
;
767 this->negated
=other
.negated
;
768 this->digitsType
= other
.digitsType
;
769 if (other
.next
==NULL
) {
773 this->next
= new AndConstraint(*other
.next
);
777 AndConstraint::~AndConstraint() {
786 AndConstraint::isFulfilled(const IFixedDecimal
&number
) {
788 if (digitsType
== none
) {
789 // An empty AndConstraint, created by a rule with a keyword but no following expression.
793 PluralOperand operand
= tokenTypeToPluralOperand(digitsType
);
794 double n
= number
.getPluralOperand(operand
); // pulls n | i | v | f value for the number.
795 // Will always be positive.
796 // May be non-integer (n option only)
798 if (integerOnly
&& n
!= uprv_floor(n
)) {
806 if (rangeList
== NULL
) {
807 result
= value
== -1 || // empty rule
808 n
== value
; // 'is' rule
811 result
= FALSE
; // 'in' or 'within' rule
812 for (int32_t r
=0; r
<rangeList
->size(); r
+=2) {
813 if (rangeList
->elementAti(r
) <= n
&& n
<= rangeList
->elementAti(r
+1)) {
830 this->next
= new AndConstraint();
834 OrConstraint::OrConstraint() {
839 OrConstraint::OrConstraint(const OrConstraint
& other
) {
840 if ( other
.childNode
== NULL
) {
841 this->childNode
= NULL
;
844 this->childNode
= new AndConstraint(*(other
.childNode
));
846 if (other
.next
== NULL
) {
850 this->next
= new OrConstraint(*(other
.next
));
854 OrConstraint::~OrConstraint() {
855 if (childNode
!=NULL
) {
866 OrConstraint
*curOrConstraint
=this;
868 while (curOrConstraint
->next
!=NULL
) {
869 curOrConstraint
= curOrConstraint
->next
;
871 U_ASSERT(curOrConstraint
->childNode
== NULL
);
872 curOrConstraint
->childNode
= new AndConstraint();
874 return curOrConstraint
->childNode
;
878 OrConstraint::isFulfilled(const IFixedDecimal
&number
) {
879 OrConstraint
* orRule
=this;
882 while (orRule
!=NULL
&& !result
) {
884 AndConstraint
* andRule
= orRule
->childNode
;
885 while (andRule
!=NULL
&& result
) {
886 result
= andRule
->isFulfilled(number
);
887 andRule
=andRule
->next
;
889 orRule
= orRule
->next
;
896 RuleChain::RuleChain(): fKeyword(), fNext(NULL
), ruleHeader(NULL
), fDecimalSamples(), fIntegerSamples(),
897 fDecimalSamplesUnbounded(FALSE
), fIntegerSamplesUnbounded(FALSE
) {
900 RuleChain::RuleChain(const RuleChain
& other
) :
901 fKeyword(other
.fKeyword
), fNext(NULL
), ruleHeader(NULL
), fDecimalSamples(other
.fDecimalSamples
),
902 fIntegerSamples(other
.fIntegerSamples
), fDecimalSamplesUnbounded(other
.fDecimalSamplesUnbounded
),
903 fIntegerSamplesUnbounded(other
.fIntegerSamplesUnbounded
) {
904 if (other
.ruleHeader
!= NULL
) {
905 this->ruleHeader
= new OrConstraint(*(other
.ruleHeader
));
907 if (other
.fNext
!= NULL
) {
908 this->fNext
= new RuleChain(*other
.fNext
);
912 RuleChain::~RuleChain() {
919 RuleChain::select(const IFixedDecimal
&number
) const {
920 if (!number
.isNaN() && !number
.isInfinite()) {
921 for (const RuleChain
*rules
= this; rules
!= NULL
; rules
= rules
->fNext
) {
922 if (rules
->ruleHeader
->isFulfilled(number
)) {
923 return rules
->fKeyword
;
927 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
930 static UnicodeString
tokenString(tokenType tok
) {
934 s
.append(LOW_N
); break;
936 s
.append(LOW_I
); break;
938 s
.append(LOW_F
); break;
940 s
.append(LOW_V
); break;
942 s
.append(LOW_T
); break;
950 RuleChain::dumpRules(UnicodeString
& result
) {
951 UChar digitString
[16];
953 if ( ruleHeader
!= NULL
) {
957 OrConstraint
* orRule
=ruleHeader
;
958 while ( orRule
!= NULL
) {
959 AndConstraint
* andRule
=orRule
->childNode
;
960 while ( andRule
!= NULL
) {
961 if ((andRule
->op
==AndConstraint::NONE
) && (andRule
->rangeList
==NULL
) && (andRule
->value
== -1)) {
963 } else if ( (andRule
->op
==AndConstraint::NONE
) && (andRule
->rangeList
==NULL
) ) {
964 result
+= tokenString(andRule
->digitsType
);
965 result
+= UNICODE_STRING_SIMPLE(" is ");
966 if (andRule
->negated
) {
967 result
+= UNICODE_STRING_SIMPLE("not ");
969 uprv_itou(digitString
,16, andRule
->value
,10,0);
970 result
+= UnicodeString(digitString
);
973 result
+= tokenString(andRule
->digitsType
);
975 if (andRule
->op
==AndConstraint::MOD
) {
976 result
+= UNICODE_STRING_SIMPLE("mod ");
977 uprv_itou(digitString
,16, andRule
->opNum
,10,0);
978 result
+= UnicodeString(digitString
);
980 if (andRule
->rangeList
==NULL
) {
981 if (andRule
->negated
) {
982 result
+= UNICODE_STRING_SIMPLE(" is not ");
983 uprv_itou(digitString
,16, andRule
->value
,10,0);
984 result
+= UnicodeString(digitString
);
987 result
+= UNICODE_STRING_SIMPLE(" is ");
988 uprv_itou(digitString
,16, andRule
->value
,10,0);
989 result
+= UnicodeString(digitString
);
993 if (andRule
->negated
) {
994 if ( andRule
->integerOnly
) {
995 result
+= UNICODE_STRING_SIMPLE(" not in ");
998 result
+= UNICODE_STRING_SIMPLE(" not within ");
1002 if ( andRule
->integerOnly
) {
1003 result
+= UNICODE_STRING_SIMPLE(" in ");
1006 result
+= UNICODE_STRING_SIMPLE(" within ");
1009 for (int32_t r
=0; r
<andRule
->rangeList
->size(); r
+=2) {
1010 int32_t rangeLo
= andRule
->rangeList
->elementAti(r
);
1011 int32_t rangeHi
= andRule
->rangeList
->elementAti(r
+1);
1012 uprv_itou(digitString
,16, rangeLo
, 10, 0);
1013 result
+= UnicodeString(digitString
);
1014 result
+= UNICODE_STRING_SIMPLE("..");
1015 uprv_itou(digitString
,16, rangeHi
, 10,0);
1016 result
+= UnicodeString(digitString
);
1017 if (r
+2 < andRule
->rangeList
->size()) {
1018 result
+= UNICODE_STRING_SIMPLE(", ");
1023 if ( (andRule
=andRule
->next
) != NULL
) {
1024 result
+= UNICODE_STRING_SIMPLE(" and ");
1027 if ( (orRule
= orRule
->next
) != NULL
) {
1028 result
+= UNICODE_STRING_SIMPLE(" or ");
1032 if ( fNext
!= NULL
) {
1033 result
+= UNICODE_STRING_SIMPLE("; ");
1034 fNext
->dumpRules(result
);
1040 RuleChain::getKeywords(int32_t capacityOfKeywords
, UnicodeString
* keywords
, int32_t& arraySize
) const {
1041 if ( arraySize
< capacityOfKeywords
-1 ) {
1042 keywords
[arraySize
++]=fKeyword
;
1045 return U_BUFFER_OVERFLOW_ERROR
;
1048 if ( fNext
!= NULL
) {
1049 return fNext
->getKeywords(capacityOfKeywords
, keywords
, arraySize
);
1052 return U_ZERO_ERROR
;
1057 RuleChain::isKeyword(const UnicodeString
& keywordParam
) const {
1058 if ( fKeyword
== keywordParam
) {
1062 if ( fNext
!= NULL
) {
1063 return fNext
->isKeyword(keywordParam
);
1071 PluralRuleParser::PluralRuleParser() :
1072 ruleIndex(0), token(), type(none
), prevType(none
),
1073 curAndConstraint(NULL
), currentChain(NULL
), rangeLowIdx(-1), rangeHiIdx(-1)
1077 PluralRuleParser::~PluralRuleParser() {
1082 PluralRuleParser::getNumberValue(const UnicodeString
& token
) {
1086 i
= token
.extract(0, token
.length(), digits
, UPRV_LENGTHOF(digits
), US_INV
);
1089 return((int32_t)atoi(digits
));
1094 PluralRuleParser::checkSyntax(UErrorCode
&status
)
1096 if (U_FAILURE(status
)) {
1099 if (!(prevType
==none
|| prevType
==tSemiColon
)) {
1100 type
= getKeyType(token
, type
); // Switch token type from tKeyword if we scanned a reserved word,
1101 // and we are not at the start of a rule, where a
1102 // keyword is expected.
1108 if (type
!=tKeyword
&& type
!= tEOF
) {
1109 status
= U_UNEXPECTED_TOKEN
;
1117 if (type
!= tIs
&& type
!= tMod
&& type
!= tIn
&&
1118 type
!= tNot
&& type
!= tWithin
&& type
!= tEqual
&& type
!= tNotEqual
) {
1119 status
= U_UNEXPECTED_TOKEN
;
1123 if (type
!= tColon
) {
1124 status
= U_UNEXPECTED_TOKEN
;
1128 if (!(type
== tVariableN
||
1129 type
== tVariableI
||
1130 type
== tVariableF
||
1131 type
== tVariableT
||
1132 type
== tVariableV
||
1134 status
= U_UNEXPECTED_TOKEN
;
1138 if ( type
!= tNumber
&& type
!= tNot
) {
1139 status
= U_UNEXPECTED_TOKEN
;
1143 if (type
!= tNumber
&& type
!= tIn
&& type
!= tWithin
) {
1144 status
= U_UNEXPECTED_TOKEN
;
1153 if (type
!= tNumber
) {
1154 status
= U_UNEXPECTED_TOKEN
;
1159 if ( type
!= tVariableN
&&
1160 type
!= tVariableI
&&
1161 type
!= tVariableF
&&
1162 type
!= tVariableT
&&
1163 type
!= tVariableV
) {
1164 status
= U_UNEXPECTED_TOKEN
;
1168 if (type
!= tNumber
) {
1169 status
= U_UNEXPECTED_TOKEN
;
1173 if (type
!= tDot2
&& type
!= tSemiColon
&& type
!= tIs
&& type
!= tNot
&&
1174 type
!= tIn
&& type
!= tEqual
&& type
!= tNotEqual
&& type
!= tWithin
&&
1175 type
!= tAnd
&& type
!= tOr
&& type
!= tComma
&& type
!= tAt
&&
1178 status
= U_UNEXPECTED_TOKEN
;
1180 // TODO: a comma following a number that is not part of a range will be allowed.
1181 // It's not the only case of this sort of thing. Parser needs a re-write.
1184 if (type
!= tDecimal
&& type
!= tInteger
) {
1185 status
= U_UNEXPECTED_TOKEN
;
1189 status
= U_UNEXPECTED_TOKEN
;
1196 * Scan the next token from the input rules.
1197 * rules and returned token type are in the parser state variables.
1200 PluralRuleParser::getNextToken(UErrorCode
&status
)
1202 if (U_FAILURE(status
)) {
1207 while (ruleIndex
< ruleSrc
->length()) {
1208 ch
= ruleSrc
->charAt(ruleIndex
);
1209 type
= charType(ch
);
1210 if (type
!= tSpace
) {
1215 if (ruleIndex
>= ruleSrc
->length()) {
1219 int32_t curIndex
= ruleIndex
;
1226 case tTilde
: // scanned '~'
1227 case tAt
: // scanned '@'
1228 case tEqual
: // scanned '='
1229 case tMod
: // scanned '%'
1230 // Single character tokens.
1234 case tNotEqual
: // scanned '!'
1235 if (ruleSrc
->charAt(curIndex
+1) == EQUALS
) {
1244 while (type
== tKeyword
&& ++curIndex
< ruleSrc
->length()) {
1245 ch
= ruleSrc
->charAt(curIndex
);
1246 type
= charType(ch
);
1252 while (type
== tNumber
&& ++curIndex
< ruleSrc
->length()) {
1253 ch
= ruleSrc
->charAt(curIndex
);
1254 type
= charType(ch
);
1260 // We could be looking at either ".." in a range, or "..." at the end of a sample.
1261 if (curIndex
+1 >= ruleSrc
->length() || ruleSrc
->charAt(curIndex
+1) != DOT
) {
1263 break; // Single dot
1265 if (curIndex
+2 >= ruleSrc
->length() || ruleSrc
->charAt(curIndex
+2) != DOT
) {
1268 break; // double dot
1272 break; // triple dot
1275 status
= U_UNEXPECTED_TOKEN
;
1280 U_ASSERT(ruleIndex
<= ruleSrc
->length());
1281 U_ASSERT(curIndex
<= ruleSrc
->length());
1282 token
=UnicodeString(*ruleSrc
, ruleIndex
, curIndex
-ruleIndex
);
1283 ruleIndex
= curIndex
;
1287 PluralRuleParser::charType(UChar ch
) {
1288 if ((ch
>=U_ZERO
) && (ch
<=U_NINE
)) {
1291 if (ch
>=LOW_A
&& ch
<=LOW_Z
) {
1323 // Set token type for reserved words in the Plural Rule syntax.
1326 PluralRuleParser::getKeyType(const UnicodeString
&token
, tokenType keyType
)
1328 if (keyType
!= tKeyword
) {
1332 if (0 == token
.compare(PK_VAR_N
, 1)) {
1333 keyType
= tVariableN
;
1334 } else if (0 == token
.compare(PK_VAR_I
, 1)) {
1335 keyType
= tVariableI
;
1336 } else if (0 == token
.compare(PK_VAR_F
, 1)) {
1337 keyType
= tVariableF
;
1338 } else if (0 == token
.compare(PK_VAR_T
, 1)) {
1339 keyType
= tVariableT
;
1340 } else if (0 == token
.compare(PK_VAR_V
, 1)) {
1341 keyType
= tVariableV
;
1342 } else if (0 == token
.compare(PK_IS
, 2)) {
1344 } else if (0 == token
.compare(PK_AND
, 3)) {
1346 } else if (0 == token
.compare(PK_IN
, 2)) {
1348 } else if (0 == token
.compare(PK_WITHIN
, 6)) {
1350 } else if (0 == token
.compare(PK_NOT
, 3)) {
1352 } else if (0 == token
.compare(PK_MOD
, 3)) {
1354 } else if (0 == token
.compare(PK_OR
, 2)) {
1356 } else if (0 == token
.compare(PK_DECIMAL
, 7)) {
1358 } else if (0 == token
.compare(PK_INTEGER
, 7)) {
1365 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain
*header
, UErrorCode
& status
)
1366 : pos(0), fKeywordNames(status
) {
1367 if (U_FAILURE(status
)) {
1370 fKeywordNames
.setDeleter(uprv_deleteUObject
);
1371 UBool addKeywordOther
=TRUE
;
1372 RuleChain
*node
=header
;
1374 fKeywordNames
.addElement(new UnicodeString(node
->fKeyword
), status
);
1375 if (U_FAILURE(status
)) {
1378 if (0 == node
->fKeyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
1379 addKeywordOther
= FALSE
;
1384 if (addKeywordOther
) {
1385 fKeywordNames
.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER
), status
);
1389 const UnicodeString
*
1390 PluralKeywordEnumeration::snext(UErrorCode
& status
) {
1391 if (U_SUCCESS(status
) && pos
< fKeywordNames
.size()) {
1392 return (const UnicodeString
*)fKeywordNames
.elementAt(pos
++);
1398 PluralKeywordEnumeration::reset(UErrorCode
& /*status*/) {
1403 PluralKeywordEnumeration::count(UErrorCode
& /*status*/) const {
1404 return fKeywordNames
.size();
1407 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
1410 PluralOperand
tokenTypeToPluralOperand(tokenType tt
) {
1413 return PLURAL_OPERAND_N
;
1415 return PLURAL_OPERAND_I
;
1417 return PLURAL_OPERAND_F
;
1419 return PLURAL_OPERAND_V
;
1421 return PLURAL_OPERAND_T
;
1423 U_ASSERT(FALSE
); // unexpected.
1424 return PLURAL_OPERAND_N
;
1428 //now in number_decimalquantity.cpp
1429 //IFixedDecimal::~IFixedDecimal() = default;
1431 FixedDecimal::FixedDecimal(const VisibleDigits
&digits
) {
1432 digits
.getFixedDecimal(
1433 source
, intValue
, decimalDigits
,
1434 decimalDigitsWithoutTrailingZeros
,
1435 visibleDecimalDigitCount
, _hasIntegerValue
);
1436 isNegative
= digits
.isNegative();
1437 _isNaN
= digits
.isNaN();
1438 _isInfinite
= digits
.isInfinite();
1441 FixedDecimal::FixedDecimal(double n
, int32_t v
, int64_t f
) {
1443 // check values. TODO make into unit test.
1445 // long visiblePower = (int) Math.pow(10, v);
1446 // if (decimalDigits > visiblePower) {
1447 // throw new IllegalArgumentException();
1449 // double fraction = intValue + (decimalDigits / (double) visiblePower);
1450 // if (fraction != source) {
1451 // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source));
1452 // if (diff > 0.00000001d) {
1453 // throw new IllegalArgumentException();
1458 FixedDecimal::FixedDecimal(double n
, int32_t v
) {
1459 // Ugly, but for samples we don't care.
1460 init(n
, v
, getFractionalDigits(n
, v
));
1463 FixedDecimal::FixedDecimal(double n
) {
1467 FixedDecimal::FixedDecimal() {
1472 // Create a FixedDecimal from a UnicodeString containing a number.
1473 // Inefficient, but only used for samples, so simplicity trumps efficiency.
1475 FixedDecimal::FixedDecimal(const UnicodeString
&num
, UErrorCode
&status
) {
1477 cs
.appendInvariantChars(num
, status
);
1479 dl
.set(cs
.toStringPiece(), status
);
1480 if (U_FAILURE(status
)) {
1484 int32_t decimalPoint
= num
.indexOf(DOT
);
1485 double n
= dl
.getDouble();
1486 if (decimalPoint
== -1) {
1489 int32_t v
= num
.length() - decimalPoint
- 1;
1490 init(n
, v
, getFractionalDigits(n
, v
));
1495 FixedDecimal::FixedDecimal(const FixedDecimal
&other
) {
1496 source
= other
.source
;
1497 visibleDecimalDigitCount
= other
.visibleDecimalDigitCount
;
1498 decimalDigits
= other
.decimalDigits
;
1499 decimalDigitsWithoutTrailingZeros
= other
.decimalDigitsWithoutTrailingZeros
;
1500 intValue
= other
.intValue
;
1501 _hasIntegerValue
= other
._hasIntegerValue
;
1502 isNegative
= other
.isNegative
;
1503 _isNaN
= other
._isNaN
;
1504 _isInfinite
= other
._isInfinite
;
1507 FixedDecimal::~FixedDecimal() = default;
1510 void FixedDecimal::init(double n
) {
1511 int32_t numFractionDigits
= decimals(n
);
1512 init(n
, numFractionDigits
, getFractionalDigits(n
, numFractionDigits
));
1516 void FixedDecimal::init(double n
, int32_t v
, int64_t f
) {
1517 isNegative
= n
< 0.0;
1519 _isNaN
= uprv_isNaN(source
);
1520 _isInfinite
= uprv_isInfinite(source
);
1521 if (_isNaN
|| _isInfinite
) {
1525 _hasIntegerValue
= FALSE
;
1527 intValue
= (int64_t)source
;
1528 _hasIntegerValue
= (source
== intValue
);
1531 visibleDecimalDigitCount
= v
;
1534 decimalDigitsWithoutTrailingZeros
= 0;
1537 while ((fdwtz%10
) == 0) {
1540 decimalDigitsWithoutTrailingZeros
= fdwtz
;
1545 // Fast path only exact initialization. Return true if successful.
1546 // Note: Do not multiply by 10 each time through loop, rounding cruft can build
1547 // up that makes the check for an integer result fail.
1548 // A single multiply of the original number works more reliably.
1549 static int32_t p10
[] = {1, 10, 100, 1000, 10000};
1550 UBool
FixedDecimal::quickInit(double n
) {
1551 UBool success
= FALSE
;
1553 int32_t numFractionDigits
;
1554 for (numFractionDigits
= 0; numFractionDigits
<= 3; numFractionDigits
++) {
1555 double scaledN
= n
* p10
[numFractionDigits
];
1556 if (scaledN
== floor(scaledN
)) {
1562 init(n
, numFractionDigits
, getFractionalDigits(n
, numFractionDigits
));
1569 int32_t FixedDecimal::decimals(double n
) {
1570 // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros.
1571 // fastpath the common cases, integers or fractions with 3 or fewer digits
1573 for (int ndigits
=0; ndigits
<=3; ndigits
++) {
1574 double scaledN
= n
* p10
[ndigits
];
1575 if (scaledN
== floor(scaledN
)) {
1580 // Slow path, convert with sprintf, parse converted output.
1582 sprintf(buf
, "%1.15e", n
);
1583 // formatted number looks like this: 1.234567890123457e-01
1584 int exponent
= atoi(buf
+18);
1585 int numFractionDigits
= 15;
1586 for (int i
=16; ; --i
) {
1587 if (buf
[i
] != '0') {
1590 --numFractionDigits
;
1592 numFractionDigits
-= exponent
; // Fraction part of fixed point representation.
1593 return numFractionDigits
;
1597 // Get the fraction digits of a double, represented as an integer.
1598 // v is the number of visible fraction digits in the displayed form of the number.
1599 // Example: n = 1001.234, v = 6, result = 234000
1600 // TODO: need to think through how this is used in the plural rule context.
1601 // This function can easily encounter integer overflow,
1602 // and can easily return noise digits when the precision of a double is exceeded.
1604 int64_t FixedDecimal::getFractionalDigits(double n
, int32_t v
) {
1605 if (v
== 0 || n
== floor(n
) || uprv_isNaN(n
) || uprv_isPositiveInfinity(n
)) {
1609 double fract
= n
- floor(n
);
1611 case 1: return (int64_t)(fract
*10.0 + 0.5);
1612 case 2: return (int64_t)(fract
*100.0 + 0.5);
1613 case 3: return (int64_t)(fract
*1000.0 + 0.5);
1615 double scaled
= floor(fract
* pow(10.0, (double)v
) + 0.5);
1616 if (scaled
> U_INT64_MAX
) {
1619 return (int64_t)scaled
;
1625 void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits
) {
1626 int32_t numTrailingFractionZeros
= minFractionDigits
- visibleDecimalDigitCount
;
1627 if (numTrailingFractionZeros
> 0) {
1628 for (int32_t i
=0; i
<numTrailingFractionZeros
; i
++) {
1629 // Do not let the decimalDigits value overflow if there are many trailing zeros.
1630 // Limit the value to 18 digits, the most that a 64 bit int can fully represent.
1631 if (decimalDigits
>= 100000000000000000LL) {
1634 decimalDigits
*= 10;
1636 visibleDecimalDigitCount
+= numTrailingFractionZeros
;
1641 double FixedDecimal::getPluralOperand(PluralOperand operand
) const {
1643 case PLURAL_OPERAND_N
: return source
;
1644 case PLURAL_OPERAND_I
: return static_cast<double>(intValue
);
1645 case PLURAL_OPERAND_F
: return static_cast<double>(decimalDigits
);
1646 case PLURAL_OPERAND_T
: return static_cast<double>(decimalDigitsWithoutTrailingZeros
);
1647 case PLURAL_OPERAND_V
: return visibleDecimalDigitCount
;
1649 U_ASSERT(FALSE
); // unexpected.
1654 bool FixedDecimal::isNaN() const {
1658 bool FixedDecimal::isInfinite() const {
1662 bool FixedDecimal::hasIntegerValue() const {
1663 return _hasIntegerValue
;
1666 bool FixedDecimal::isNanOrInfinity() const {
1667 return _isNaN
|| _isInfinite
;
1670 int32_t FixedDecimal::getVisibleFractionDigitCount() const {
1671 return visibleDecimalDigitCount
;
1676 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode
&status
) {
1679 fOpenStatus
= status
;
1680 if (U_FAILURE(status
)) {
1683 fOpenStatus
= U_ZERO_ERROR
;
1684 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "plurals", &fOpenStatus
));
1685 fLocales
= ures_getByKey(rb
.getAlias(), "locales", NULL
, &fOpenStatus
);
1688 PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() {
1689 ures_close(fLocales
);
1695 const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength
, UErrorCode
&status
) {
1696 if (U_FAILURE(status
)) {
1699 if (U_FAILURE(fOpenStatus
)) {
1700 status
= fOpenStatus
;
1703 fRes
= ures_getNextResource(fLocales
, fRes
, &status
);
1704 if (fRes
== NULL
|| U_FAILURE(status
)) {
1705 if (status
== U_INDEX_OUTOFBOUNDS_ERROR
) {
1706 status
= U_ZERO_ERROR
;
1710 const char *result
= ures_getKey(fRes
);
1711 if (resultLength
!= NULL
) {
1712 *resultLength
= static_cast<int32_t>(uprv_strlen(result
));
1718 void PluralAvailableLocalesEnumeration::reset(UErrorCode
&status
) {
1719 if (U_FAILURE(status
)) {
1722 if (U_FAILURE(fOpenStatus
)) {
1723 status
= fOpenStatus
;
1726 ures_resetIterator(fLocales
);
1729 int32_t PluralAvailableLocalesEnumeration::count(UErrorCode
&status
) const {
1730 if (U_FAILURE(status
)) {
1733 if (U_FAILURE(fOpenStatus
)) {
1734 status
= fOpenStatus
;
1737 return ures_getSize(fLocales
);
1743 #endif /* #if !UCONFIG_NO_FORMATTING */