2 *******************************************************************************
3 * Copyright (C) 2007-2011, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 *******************************************************************************
16 #include "unicode/utypes.h"
17 #include "unicode/localpointer.h"
18 #include "unicode/plurrule.h"
19 #include "unicode/ures.h"
24 #include "patternprops.h"
25 #include "plurrule_impl.h"
32 #if !UCONFIG_NO_FORMATTING
36 // shared by all instances when lazy-initializing samples
37 static UMTX pluralMutex
;
39 #define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0])
41 static const UChar PLURAL_KEYWORD_OTHER
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,0};
42 static const UChar PLURAL_DEFAULT_RULE
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,COLON
,SPACE
,LOW_N
,0};
43 static const UChar PK_IN
[]={LOW_I
,LOW_N
,0};
44 static const UChar PK_NOT
[]={LOW_N
,LOW_O
,LOW_T
,0};
45 static const UChar PK_IS
[]={LOW_I
,LOW_S
,0};
46 static const UChar PK_MOD
[]={LOW_M
,LOW_O
,LOW_D
,0};
47 static const UChar PK_AND
[]={LOW_A
,LOW_N
,LOW_D
,0};
48 static const UChar PK_OR
[]={LOW_O
,LOW_R
,0};
49 static const UChar PK_VAR_N
[]={LOW_N
,0};
50 static const UChar PK_WITHIN
[]={LOW_W
,LOW_I
,LOW_T
,LOW_H
,LOW_I
,LOW_N
,0};
52 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules
)
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration
)
55 PluralRules::PluralRules(UErrorCode
& status
)
63 if (U_FAILURE(status
)) {
66 mParser
= new RuleParser();
68 status
= U_MEMORY_ALLOCATION_ERROR
;
72 PluralRules::PluralRules(const PluralRules
& other
)
83 PluralRules::~PluralRules() {
87 uprv_free(mSampleInfo
);
91 PluralRules::clone() const {
92 return new PluralRules(*this);
96 PluralRules::operator=(const PluralRules
& other
) {
99 if (other
.mRules
==NULL
) {
103 mRules
= new RuleChain(*other
.mRules
);
106 mParser
= new RuleParser();
111 uprv_free(mSampleInfo
);
113 mSampleInfoCount
= 0;
119 PluralRules
* U_EXPORT2
120 PluralRules::createRules(const UnicodeString
& description
, UErrorCode
& status
) {
123 if (U_FAILURE(status
)) {
126 PluralRules
*newRules
= new PluralRules(status
);
127 if ( (newRules
!= NULL
)&& U_SUCCESS(status
) ) {
128 newRules
->parseDescription((UnicodeString
&)description
, rules
, status
);
129 if (U_SUCCESS(status
)) {
130 newRules
->addRules(rules
);
133 if (U_FAILURE(status
)) {
142 PluralRules
* U_EXPORT2
143 PluralRules::createDefaultRules(UErrorCode
& status
) {
144 return createRules(UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1), status
);
147 PluralRules
* U_EXPORT2
148 PluralRules::forLocale(const Locale
& locale
, UErrorCode
& status
) {
150 if (U_FAILURE(status
)) {
153 PluralRules
*newObj
= new PluralRules(status
);
154 if (newObj
==NULL
|| U_FAILURE(status
)) {
158 UnicodeString locRule
= newObj
->getRuleFromResource(locale
, status
);
159 if ((locRule
.length() != 0) && U_SUCCESS(status
)) {
160 newObj
->parseDescription(locRule
, rChain
, status
);
161 if (U_SUCCESS(status
)) {
162 newObj
->addRules(rChain
);
165 if (U_FAILURE(status
)||(locRule
.length() == 0)) {
166 // use default plural rule
167 status
= U_ZERO_ERROR
;
168 UnicodeString defRule
= UnicodeString(PLURAL_DEFAULT_RULE
);
169 newObj
->parseDescription(defRule
, rChain
, status
);
170 newObj
->addRules(rChain
);
177 PluralRules::select(int32_t number
) const {
178 if (mRules
== NULL
) {
179 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
182 return mRules
->select(number
);
187 PluralRules::select(double number
) const {
188 if (mRules
== NULL
) {
189 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
192 return mRules
->select(number
);
197 PluralRules::getKeywords(UErrorCode
& status
) const {
198 if (U_FAILURE(status
)) return NULL
;
199 StringEnumeration
* nameEnumerator
= new PluralKeywordEnumeration(mRules
, status
);
200 if (U_FAILURE(status
)) {
201 delete nameEnumerator
;
205 return nameEnumerator
;
209 PluralRules::getUniqueKeywordValue(const UnicodeString
& keyword
) {
211 UErrorCode status
= U_ZERO_ERROR
;
212 int32_t count
= getSamplesInternal(keyword
, &val
, 1, FALSE
, status
);
213 return count
== 1 ? val
: UPLRULES_NO_UNIQUE_VALUE
;
217 PluralRules::getAllKeywordValues(const UnicodeString
&keyword
, double *dest
,
218 int32_t destCapacity
, UErrorCode
& error
) {
219 return getSamplesInternal(keyword
, dest
, destCapacity
, FALSE
, error
);
223 PluralRules::getSamples(const UnicodeString
&keyword
, double *dest
,
224 int32_t destCapacity
, UErrorCode
& status
) {
225 return getSamplesInternal(keyword
, dest
, destCapacity
, TRUE
, status
);
229 PluralRules::getSamplesInternal(const UnicodeString
&keyword
, double *dest
,
230 int32_t destCapacity
, UBool includeUnlimited
,
231 UErrorCode
& status
) {
233 if (U_FAILURE(status
)) {
236 if (destCapacity
< 0 || (dest
== NULL
&& destCapacity
> 0)) {
237 status
= U_ILLEGAL_ARGUMENT_ERROR
;
241 int32_t index
= getKeywordIndex(keyword
, status
);
246 const int32_t LIMIT_MASK
= 0x1 << 31;
248 if (!includeUnlimited
) {
249 if ((mSampleInfo
[index
] & LIMIT_MASK
) == 0) {
254 int32_t start
= index
== 0 ? 0 : mSampleInfo
[index
- 1] & ~LIMIT_MASK
;
255 int32_t limit
= mSampleInfo
[index
] & ~LIMIT_MASK
;
256 int32_t len
= limit
- start
;
257 if (len
<= destCapacity
) {
259 } else if (includeUnlimited
) {
260 len
= destCapacity
; // no overflow, and don't report more than we copy
262 status
= U_BUFFER_OVERFLOW_ERROR
;
265 for (int32_t i
= 0; i
< destCapacity
; ++i
, ++start
) {
266 dest
[i
] = mSamples
[start
];
273 PluralRules::isKeyword(const UnicodeString
& keyword
) const {
274 if (0 == keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
282 return mRules
->isKeyword(keyword
);
288 PluralRules::getKeywordOther() const {
289 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
293 PluralRules::operator==(const PluralRules
& other
) const {
295 const UnicodeString
*ptrKeyword
;
296 UErrorCode status
= U_ZERO_ERROR
;
298 if ( this == &other
) {
301 LocalPointer
<StringEnumeration
> myKeywordList(getKeywords(status
));
302 LocalPointer
<StringEnumeration
> otherKeywordList(other
.getKeywords(status
));
303 if (U_FAILURE(status
)) {
307 if (myKeywordList
->count(status
)!=otherKeywordList
->count(status
)) {
310 myKeywordList
->reset(status
);
311 while ((ptrKeyword
=myKeywordList
->snext(status
))!=NULL
) {
312 if (!other
.isKeyword(*ptrKeyword
)) {
316 otherKeywordList
->reset(status
);
317 while ((ptrKeyword
=otherKeywordList
->snext(status
))!=NULL
) {
318 if (!this->isKeyword(*ptrKeyword
)) {
322 if (U_FAILURE(status
)) {
326 if ((limit
=this->getRepeatLimit()) != other
.getRepeatLimit()) {
329 UnicodeString myKeyword
, otherKeyword
;
330 for (int32_t i
=0; i
<limit
; ++i
) {
331 myKeyword
= this->select(i
);
332 otherKeyword
= other
.select(i
);
333 if (myKeyword
!=otherKeyword
) {
341 PluralRules::parseDescription(UnicodeString
& data
, RuleChain
& rules
, UErrorCode
&status
)
346 tokenType prevType
=none
;
347 RuleChain
*ruleChain
=NULL
;
348 AndConstraint
*curAndConstraint
=NULL
;
349 OrConstraint
*orNode
=NULL
;
350 RuleChain
*lastChain
=NULL
;
352 if (U_FAILURE(status
)) {
355 UnicodeString ruleData
= data
.toLower("");
356 while (ruleIndex
< ruleData
.length()) {
357 mParser
->getNextToken(ruleData
, &ruleIndex
, token
, type
, status
);
358 if (U_FAILURE(status
)) {
361 mParser
->checkSyntax(prevType
, type
, status
);
362 if (U_FAILURE(status
)) {
367 U_ASSERT(curAndConstraint
!= NULL
);
368 curAndConstraint
= curAndConstraint
->add();
372 while (lastChain
->next
!=NULL
) {
373 lastChain
= lastChain
->next
;
375 orNode
=lastChain
->ruleHeader
;
376 while (orNode
->next
!= NULL
) {
377 orNode
= orNode
->next
;
379 orNode
->next
= new OrConstraint();
382 curAndConstraint
= orNode
->add();
385 U_ASSERT(curAndConstraint
!= NULL
);
386 curAndConstraint
->rangeHigh
=-1;
389 U_ASSERT(curAndConstraint
!= NULL
);
390 curAndConstraint
->notIn
=TRUE
;
393 U_ASSERT(curAndConstraint
!= NULL
);
394 curAndConstraint
->rangeHigh
=PLURAL_RANGE_HIGH
;
395 curAndConstraint
->integerOnly
= TRUE
;
398 U_ASSERT(curAndConstraint
!= NULL
);
399 curAndConstraint
->rangeHigh
=PLURAL_RANGE_HIGH
;
402 U_ASSERT(curAndConstraint
!= NULL
);
403 if ( (curAndConstraint
->op
==AndConstraint::MOD
)&&
404 (curAndConstraint
->opNum
== -1 ) ) {
405 curAndConstraint
->opNum
=getNumberValue(token
);
408 if (curAndConstraint
->rangeLow
== -1) {
409 curAndConstraint
->rangeLow
=getNumberValue(token
);
412 curAndConstraint
->rangeHigh
=getNumberValue(token
);
417 U_ASSERT(curAndConstraint
!= NULL
);
418 curAndConstraint
->op
=AndConstraint::MOD
;
421 if (ruleChain
==NULL
) {
425 while (ruleChain
->next
!=NULL
){
426 ruleChain
=ruleChain
->next
;
428 ruleChain
=ruleChain
->next
=new RuleChain();
430 if (ruleChain
->ruleHeader
!= NULL
) {
431 delete ruleChain
->ruleHeader
;
433 orNode
= ruleChain
->ruleHeader
= new OrConstraint();
434 curAndConstraint
= orNode
->add();
435 ruleChain
->keyword
= token
;
445 PluralRules::getNumberValue(const UnicodeString
& token
) const {
449 i
= token
.extract(0, token
.length(), digits
, ARRAY_SIZE(digits
), US_INV
);
452 return((int32_t)atoi(digits
));
457 PluralRules::getNextLocale(const UnicodeString
& localeData
, int32_t* curIndex
, UnicodeString
& localeName
) {
461 while (i
< localeData
.length()) {
462 if ( (localeData
.charAt(i
)!= SPACE
) && (localeData
.charAt(i
)!= COMMA
) ) {
468 while (i
< localeData
.length()) {
469 if ( (localeData
.charAt(i
)== SPACE
) || (localeData
.charAt(i
)== COMMA
) ) {
472 localeName
+=localeData
.charAt(i
++);
479 PluralRules::getRepeatLimit() const {
481 return mRules
->getRepeatLimit();
489 PluralRules::getKeywordIndex(const UnicodeString
& keyword
,
490 UErrorCode
& status
) const {
491 if (U_SUCCESS(status
)) {
493 RuleChain
* rc
= mRules
;
495 if (rc
->ruleHeader
!= NULL
) {
496 if (rc
->keyword
== keyword
) {
503 if (0 == keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
510 typedef struct SampleRecord
{
516 PluralRules::initSamples(UErrorCode
& status
) {
517 if (U_FAILURE(status
)) {
520 Mutex
lock(&pluralMutex
);
526 // Note, the original design let you have multiple rules with the same keyword. But
527 // we don't use that in our data and existing functions in this implementation don't
528 // fully support it (for example, the returned keywords is a list and not a set).
530 // So I don't support this here either. If you ask for samples, or for all values,
531 // you will get information about the first rule with that keyword, not all rules with
534 int32_t maxIndex
= 0;
535 int32_t otherIndex
= -1; // the value -1 will indicate we added 'other' at end
536 RuleChain
* rc
= mRules
;
538 if (rc
->ruleHeader
!= NULL
) {
539 if (otherIndex
== -1 && 0 == rc
->keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
540 otherIndex
= maxIndex
;
546 if (otherIndex
== -1) {
550 LocalMemory
<int32_t> newSampleInfo
;
551 if (NULL
== newSampleInfo
.allocateInsteadAndCopy(maxIndex
)) {
552 status
= U_MEMORY_ALLOCATION_ERROR
;
556 const int32_t LIMIT_MASK
= 0x1 << 31;
561 if (rc
->ruleHeader
!= NULL
) {
562 newSampleInfo
[n
++] = rc
->ruleHeader
->isLimited() ? LIMIT_MASK
: 0;
566 if (otherIndex
== -1) {
567 newSampleInfo
[maxIndex
- 1] = 0; // unlimited
570 MaybeStackArray
<SampleRecord
, 10> newSamples
;
571 int32_t sampleCount
= 0;
573 int32_t limit
= getRepeatLimit() * MAX_SAMPLES
* 2;
578 for (int i
= 0, keywordsRemaining
= maxIndex
;
579 keywordsRemaining
> 0 && i
< limit
;
581 double val
= i
/ 2.0;
587 if (rc
->ruleHeader
!= NULL
) {
588 if (rc
->ruleHeader
->isFulfilled(val
)) {
597 // 'other'. If there is an 'other' rule, the rule set is bad since nothing
598 // should leak through, but we don't bother to report that here.
599 found
= otherIndex
== -1 ? maxIndex
- 1 : otherIndex
;
601 if (newSampleInfo
[found
] == MAX_SAMPLES
) { // limit flag not set
604 newSampleInfo
[found
] += 1; // won't impact limit flag
606 if (sampleCount
== newSamples
.getCapacity()) {
607 int32_t newCapacity
= sampleCount
< 20 ? 128 : sampleCount
* 2;
608 if (NULL
== newSamples
.resize(newCapacity
, sampleCount
)) {
609 status
= U_MEMORY_ALLOCATION_ERROR
;
613 newSamples
[sampleCount
].ruleIndex
= found
;
614 newSamples
[sampleCount
].value
= val
;
617 if (newSampleInfo
[found
] == MAX_SAMPLES
) { // limit flag not set
622 // sort the values by index, leaving order otherwise unchanged
623 // this is just a selection sort for simplicity
624 LocalMemory
<double> values
;
625 if (NULL
== values
.allocateInsteadAndCopy(sampleCount
)) {
626 status
= U_MEMORY_ALLOCATION_ERROR
;
629 for (int i
= 0, j
= 0; i
< maxIndex
; ++i
) {
630 for (int k
= 0; k
< sampleCount
; ++k
) {
631 if (newSamples
[k
].ruleIndex
== i
) {
632 values
[j
++] = newSamples
[k
].value
;
637 // convert array of mask/lengths to array of mask/limits
639 for (int i
= 0; i
< maxIndex
; ++i
) {
640 int32_t info
= newSampleInfo
[i
];
641 int32_t len
= info
& ~LIMIT_MASK
;
643 // if a rule is 'unlimited' but has fewer than MAX_SAMPLES samples,
644 // it's not really unlimited, so mark it as limited
645 int32_t mask
= len
< MAX_SAMPLES
? LIMIT_MASK
: info
& LIMIT_MASK
;
646 newSampleInfo
[i
] = limit
| mask
;
649 // ok, we've got good data
650 mSamples
= values
.orphan();
651 mSampleInfo
= newSampleInfo
.orphan();
652 mSampleInfoCount
= maxIndex
;
656 PluralRules::addRules(RuleChain
& rules
) {
657 RuleChain
*newRule
= new RuleChain(rules
);
658 this->mRules
=newRule
;
659 newRule
->setRepeatLimit();
663 PluralRules::getRuleFromResource(const Locale
& locale
, UErrorCode
& errCode
) {
664 UnicodeString emptyStr
;
666 if (U_FAILURE(errCode
)) {
669 UResourceBundle
*rb
=ures_openDirect(NULL
, "plurals", &errCode
);
670 if(U_FAILURE(errCode
)) {
671 /* total failure, not even root could be opened */
674 UResourceBundle
*locRes
=ures_getByKey(rb
, "locales", NULL
, &errCode
);
675 if(U_FAILURE(errCode
)) {
680 const char *curLocaleName
=locale
.getName();
681 const UChar
* s
= ures_getStringByKey(locRes
, curLocaleName
, &resLen
, &errCode
);
684 // Check parent locales.
685 UErrorCode status
= U_ZERO_ERROR
;
686 char parentLocaleName
[ULOC_FULLNAME_CAPACITY
];
687 const char *curLocaleName
=locale
.getName();
688 int32_t localeNameLen
=0;
689 uprv_strcpy(parentLocaleName
, curLocaleName
);
691 while ((localeNameLen
=uloc_getParent(parentLocaleName
, parentLocaleName
,
692 ULOC_FULLNAME_CAPACITY
, &status
)) > 0) {
694 s
= ures_getStringByKey(locRes
, parentLocaleName
, &resLen
, &status
);
696 errCode
= U_ZERO_ERROR
;
699 status
= U_ZERO_ERROR
;
710 u_UCharsToChars(s
, setKey
, resLen
+ 1);
711 // printf("\n PluralRule: %s\n", setKey);
714 UResourceBundle
*ruleRes
=ures_getByKey(rb
, "rules", NULL
, &errCode
);
715 if(U_FAILURE(errCode
)) {
721 UResourceBundle
*setRes
= ures_getByKey(ruleRes
, setKey
, NULL
, &errCode
);
722 if (U_FAILURE(errCode
)) {
729 int32_t numberKeys
= ures_getSize(setRes
);
732 for(int32_t i
=0; i
<numberKeys
; ++i
) {
735 s
=ures_getNextString(setRes
, &resLen
, (const char**)&key
, &errCode
);
736 keyLen
= (int32_t)uprv_strlen(key
);
737 u_charsToUChars(key
, result
+len
, keyLen
);
740 uprv_memcpy(result
+len
, s
, resLen
*sizeof(UChar
));
742 result
[len
++]=SEMI_COLON
;
745 u_UCharsToChars(result
, setKey
, len
);
746 // printf(" Rule: %s\n", setKey);
752 return UnicodeString(result
);
755 AndConstraint::AndConstraint() {
756 op
= AndConstraint::NONE
;
766 AndConstraint::AndConstraint(const AndConstraint
& other
) {
768 this->opNum
=other
.opNum
;
769 this->rangeLow
=other
.rangeLow
;
770 this->rangeHigh
=other
.rangeHigh
;
771 this->integerOnly
=other
.integerOnly
;
772 this->notIn
=other
.notIn
;
773 if (other
.next
==NULL
) {
777 this->next
= new AndConstraint(*other
.next
);
781 AndConstraint::~AndConstraint() {
789 AndConstraint::isFulfilled(double number
) {
794 if ((rangeHigh
== -1 || integerOnly
) && number
!= uprv_floor(number
)) {
799 value
= (int32_t)value
% opNum
;
801 if ( rangeHigh
== -1 ) {
802 if ( rangeLow
== -1 ) {
803 result
= TRUE
; // empty rule
806 if ( value
== rangeLow
) {
815 if ((rangeLow
<= value
) && (value
<= rangeHigh
)) {
817 if ( value
!= (int32_t)value
) {
841 AndConstraint::isLimited() {
842 return (rangeHigh
== -1 || integerOnly
) && !notIn
&& op
!= MOD
;
846 AndConstraint::updateRepeatLimit(int32_t maxLimit
) {
849 return uprv_max(opNum
, maxLimit
);
852 if ( rangeHigh
== -1 ) {
853 return uprv_max(rangeLow
, maxLimit
);
856 return uprv_max(rangeHigh
, maxLimit
);
865 this->next
= new AndConstraint();
869 OrConstraint::OrConstraint() {
874 OrConstraint::OrConstraint(const OrConstraint
& other
) {
875 if ( other
.childNode
== NULL
) {
876 this->childNode
= NULL
;
879 this->childNode
= new AndConstraint(*(other
.childNode
));
881 if (other
.next
== NULL
) {
885 this->next
= new OrConstraint(*(other
.next
));
889 OrConstraint::~OrConstraint() {
890 if (childNode
!=NULL
) {
901 OrConstraint
*curOrConstraint
=this;
903 while (curOrConstraint
->next
!=NULL
) {
904 curOrConstraint
= curOrConstraint
->next
;
906 curOrConstraint
->next
= NULL
;
907 curOrConstraint
->childNode
= new AndConstraint();
909 return curOrConstraint
->childNode
;
913 OrConstraint::isFulfilled(double number
) {
914 OrConstraint
* orRule
=this;
917 while (orRule
!=NULL
&& !result
) {
919 AndConstraint
* andRule
= orRule
->childNode
;
920 while (andRule
!=NULL
&& result
) {
921 result
= andRule
->isFulfilled(number
);
922 andRule
=andRule
->next
;
924 orRule
= orRule
->next
;
931 OrConstraint::isLimited() {
932 for (OrConstraint
*orc
= this; orc
!= NULL
; orc
= orc
->next
) {
933 UBool result
= FALSE
;
934 for (AndConstraint
*andc
= orc
->childNode
; andc
!= NULL
; andc
= andc
->next
) {
935 if (andc
->isLimited()) {
940 if (result
== FALSE
) {
947 RuleChain::RuleChain() {
953 RuleChain::RuleChain(const RuleChain
& other
) {
954 this->repeatLimit
= other
.repeatLimit
;
955 this->keyword
=other
.keyword
;
956 if (other
.ruleHeader
!= NULL
) {
957 this->ruleHeader
= new OrConstraint(*(other
.ruleHeader
));
960 this->ruleHeader
= NULL
;
962 if (other
.next
!= NULL
) {
963 this->next
= new RuleChain(*other
.next
);
971 RuleChain::~RuleChain() {
975 if ( ruleHeader
!= NULL
) {
981 RuleChain::select(double number
) const {
983 if ( ruleHeader
!= NULL
) {
984 if (ruleHeader
->isFulfilled(number
)) {
988 if ( next
!= NULL
) {
989 return next
->select(number
);
992 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
998 RuleChain::dumpRules(UnicodeString
& result
) {
999 UChar digitString
[16];
1001 if ( ruleHeader
!= NULL
) {
1003 OrConstraint
* orRule
=ruleHeader
;
1004 while ( orRule
!= NULL
) {
1005 AndConstraint
* andRule
=orRule
->childNode
;
1006 while ( andRule
!= NULL
) {
1007 if ( (andRule
->op
==AndConstraint::NONE
) && (andRule
->rangeHigh
==-1) ) {
1008 result
+= UNICODE_STRING_SIMPLE(" n is ");
1009 if (andRule
->notIn
) {
1010 result
+= UNICODE_STRING_SIMPLE("not ");
1012 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1013 result
+= UnicodeString(digitString
);
1016 if (andRule
->op
==AndConstraint::MOD
) {
1017 result
+= UNICODE_STRING_SIMPLE(" n mod ");
1018 uprv_itou(digitString
,16, andRule
->opNum
,10,0);
1019 result
+= UnicodeString(digitString
);
1022 result
+= UNICODE_STRING_SIMPLE(" n ");
1024 if (andRule
->rangeHigh
==-1) {
1025 if (andRule
->notIn
) {
1026 result
+= UNICODE_STRING_SIMPLE(" is not ");
1027 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1028 result
+= UnicodeString(digitString
);
1031 result
+= UNICODE_STRING_SIMPLE(" is ");
1032 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1033 result
+= UnicodeString(digitString
);
1037 if (andRule
->notIn
) {
1038 if ( andRule
->integerOnly
) {
1039 result
+= UNICODE_STRING_SIMPLE(" not in ");
1042 result
+= UNICODE_STRING_SIMPLE(" not within ");
1044 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1045 result
+= UnicodeString(digitString
);
1046 result
+= UNICODE_STRING_SIMPLE(" .. ");
1047 uprv_itou(digitString
,16, andRule
->rangeHigh
,10,0);
1048 result
+= UnicodeString(digitString
);
1051 if ( andRule
->integerOnly
) {
1052 result
+= UNICODE_STRING_SIMPLE(" in ");
1055 result
+= UNICODE_STRING_SIMPLE(" within ");
1057 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1058 result
+= UnicodeString(digitString
);
1059 result
+= UNICODE_STRING_SIMPLE(" .. ");
1060 uprv_itou(digitString
,16, andRule
->rangeHigh
,10,0);
1064 if ( (andRule
=andRule
->next
) != NULL
) {
1065 result
.append(PK_AND
, 3);
1068 if ( (orRule
= orRule
->next
) != NULL
) {
1069 result
.append(PK_OR
, 2);
1073 if ( next
!= NULL
) {
1074 next
->dumpRules(result
);
1079 RuleChain::getRepeatLimit () {
1084 RuleChain::setRepeatLimit () {
1087 if ( next
!= NULL
) {
1088 next
->setRepeatLimit();
1089 limit
= next
->repeatLimit
;
1092 if ( ruleHeader
!= NULL
) {
1093 OrConstraint
* orRule
=ruleHeader
;
1094 while ( orRule
!= NULL
) {
1095 AndConstraint
* andRule
=orRule
->childNode
;
1096 while ( andRule
!= NULL
) {
1097 limit
= andRule
->updateRepeatLimit(limit
);
1098 andRule
= andRule
->next
;
1100 orRule
= orRule
->next
;
1103 repeatLimit
= limit
;
1107 RuleChain::getKeywords(int32_t capacityOfKeywords
, UnicodeString
* keywords
, int32_t& arraySize
) const {
1108 if ( arraySize
< capacityOfKeywords
-1 ) {
1109 keywords
[arraySize
++]=keyword
;
1112 return U_BUFFER_OVERFLOW_ERROR
;
1115 if ( next
!= NULL
) {
1116 return next
->getKeywords(capacityOfKeywords
, keywords
, arraySize
);
1119 return U_ZERO_ERROR
;
1124 RuleChain::isKeyword(const UnicodeString
& keywordParam
) const {
1125 if ( keyword
== keywordParam
) {
1129 if ( next
!= NULL
) {
1130 return next
->isKeyword(keywordParam
);
1138 RuleParser::RuleParser() {
1141 RuleParser::~RuleParser() {
1145 RuleParser::checkSyntax(tokenType prevType
, tokenType curType
, UErrorCode
&status
)
1147 if (U_FAILURE(status
)) {
1153 if (curType
!=tKeyword
) {
1154 status
= U_UNEXPECTED_TOKEN
;
1158 if (curType
!= tIs
&& curType
!= tMod
&& curType
!= tIn
&&
1159 curType
!= tNot
&& curType
!= tWithin
) {
1160 status
= U_UNEXPECTED_TOKEN
;
1170 if (curType
!= tColon
) {
1171 status
= U_UNEXPECTED_TOKEN
;
1175 if (curType
!= tVariableN
) {
1176 status
= U_UNEXPECTED_TOKEN
;
1180 if ( curType
!= tNumber
&& curType
!= tNot
) {
1181 status
= U_UNEXPECTED_TOKEN
;
1185 if (curType
!= tNumber
&& curType
!= tIn
&& curType
!= tWithin
) {
1186 status
= U_UNEXPECTED_TOKEN
;
1195 if (curType
!= tNumber
&& curType
!= tVariableN
) {
1196 status
= U_UNEXPECTED_TOKEN
;
1200 if (curType
!= tDot
&& curType
!= tSemiColon
&& curType
!= tIs
&& curType
!= tNot
&&
1201 curType
!= tIn
&& curType
!= tWithin
&& curType
!= tAnd
&& curType
!= tOr
)
1203 status
= U_UNEXPECTED_TOKEN
;
1207 status
= U_UNEXPECTED_TOKEN
;
1213 RuleParser::getNextToken(const UnicodeString
& ruleData
,
1215 UnicodeString
& token
,
1219 int32_t curIndex
= *ruleIndex
;
1221 tokenType prevType
=none
;
1223 if (U_FAILURE(status
)) {
1226 while (curIndex
<ruleData
.length()) {
1227 ch
= ruleData
.charAt(curIndex
);
1228 if ( !inRange(ch
, type
) ) {
1229 status
= U_ILLEGAL_CHARACTER
;
1234 if ( *ruleIndex
!= curIndex
) { // letter
1235 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1236 *ruleIndex
=curIndex
;
1238 getKeyType(token
, type
, status
);
1242 *ruleIndex
=*ruleIndex
+1;
1244 break; // consective space
1247 if ( *ruleIndex
!= curIndex
) {
1248 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1249 *ruleIndex
=curIndex
;
1251 getKeyType(token
, type
, status
);
1255 *ruleIndex
=curIndex
+1;
1259 if ((type
==prevType
)||(prevType
==none
)) {
1265 if ((type
==prevType
)||(prevType
==none
)) {
1270 *ruleIndex
=curIndex
+1;
1274 if (prevType
==none
) { // first dot
1279 if ( *ruleIndex
!= curIndex
) {
1280 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1281 *ruleIndex
=curIndex
; // letter
1283 getKeyType(token
, type
, status
);
1286 else { // two consective dots
1287 *ruleIndex
=curIndex
+2;
1293 status
= U_UNEXPECTED_TOKEN
;
1298 if ( curIndex
>=ruleData
.length() ) {
1299 if ( (type
== tLetter
)||(type
== tNumber
) ) {
1300 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1301 getKeyType(token
, type
, status
);
1302 if (U_FAILURE(status
)) {
1306 *ruleIndex
= ruleData
.length();
1311 RuleParser::inRange(UChar ch
, tokenType
& type
) {
1312 if ((ch
>=CAP_A
) && (ch
<=CAP_Z
)) {
1313 // we assume all characters are in lower case already.
1316 if ((ch
>=LOW_A
) && (ch
<=LOW_Z
)) {
1320 if ((ch
>=U_ZERO
) && (ch
<=U_NINE
)) {
1345 RuleParser::getKeyType(const UnicodeString
& token
, tokenType
& keyType
, UErrorCode
&status
)
1347 if (U_FAILURE(status
)) {
1350 if ( keyType
==tNumber
) {
1352 else if (0 == token
.compare(PK_VAR_N
, 1)) {
1353 keyType
= tVariableN
;
1355 else if (0 == token
.compare(PK_IS
, 2)) {
1358 else if (0 == token
.compare(PK_AND
, 3)) {
1361 else if (0 == token
.compare(PK_IN
, 2)) {
1364 else if (0 == token
.compare(PK_WITHIN
, 6)) {
1367 else if (0 == token
.compare(PK_NOT
, 3)) {
1370 else if (0 == token
.compare(PK_MOD
, 3)) {
1373 else if (0 == token
.compare(PK_OR
, 2)) {
1376 else if ( isValidKeyword(token
) ) {
1380 status
= U_UNEXPECTED_TOKEN
;
1385 RuleParser::isValidKeyword(const UnicodeString
& token
) {
1386 return PatternProps::isIdentifier(token
.getBuffer(), token
.length());
1389 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain
*header
, UErrorCode
& status
)
1390 : pos(0), fKeywordNames(status
) {
1391 if (U_FAILURE(status
)) {
1394 fKeywordNames
.setDeleter(uprv_deleteUObject
);
1395 UBool addKeywordOther
=TRUE
;
1396 RuleChain
*node
=header
;
1398 fKeywordNames
.addElement(new UnicodeString(node
->keyword
), status
);
1399 if (U_FAILURE(status
)) {
1402 if (0 == node
->keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
1403 addKeywordOther
= FALSE
;
1408 if (addKeywordOther
) {
1409 fKeywordNames
.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER
), status
);
1413 const UnicodeString
*
1414 PluralKeywordEnumeration::snext(UErrorCode
& status
) {
1415 if (U_SUCCESS(status
) && pos
< fKeywordNames
.size()) {
1416 return (const UnicodeString
*)fKeywordNames
.elementAt(pos
++);
1422 PluralKeywordEnumeration::reset(UErrorCode
& /*status*/) {
1427 PluralKeywordEnumeration::count(UErrorCode
& /*status*/) const {
1428 return fKeywordNames
.size();
1431 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
1437 #endif /* #if !UCONFIG_NO_FORMATTING */