2 *******************************************************************************
3 * Copyright (C) 2007-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
10 #include "unicode/utypes.h"
11 #include "unicode/localpointer.h"
12 #include "unicode/plurrule.h"
13 #include "unicode/upluralrules.h"
14 #include "unicode/ures.h"
19 #include "patternprops.h"
20 #include "plurrule_impl.h"
27 #if !UCONFIG_NO_FORMATTING
31 // shared by all instances when lazy-initializing samples
32 static UMutex pluralMutex
= U_MUTEX_INITIALIZER
;
34 #define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0])
36 static const UChar PLURAL_KEYWORD_OTHER
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,0};
37 static const UChar PLURAL_DEFAULT_RULE
[]={LOW_O
,LOW_T
,LOW_H
,LOW_E
,LOW_R
,COLON
,SPACE
,LOW_N
,0};
38 static const UChar PK_IN
[]={LOW_I
,LOW_N
,0};
39 static const UChar PK_NOT
[]={LOW_N
,LOW_O
,LOW_T
,0};
40 static const UChar PK_IS
[]={LOW_I
,LOW_S
,0};
41 static const UChar PK_MOD
[]={LOW_M
,LOW_O
,LOW_D
,0};
42 static const UChar PK_AND
[]={LOW_A
,LOW_N
,LOW_D
,0};
43 static const UChar PK_OR
[]={LOW_O
,LOW_R
,0};
44 static const UChar PK_VAR_N
[]={LOW_N
,0};
45 static const UChar PK_WITHIN
[]={LOW_W
,LOW_I
,LOW_T
,LOW_H
,LOW_I
,LOW_N
,0};
47 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules
)
48 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration
)
50 PluralRules::PluralRules(UErrorCode
& status
)
58 if (U_FAILURE(status
)) {
61 mParser
= new RuleParser();
63 status
= U_MEMORY_ALLOCATION_ERROR
;
67 PluralRules::PluralRules(const PluralRules
& other
)
78 PluralRules::~PluralRules() {
82 uprv_free(mSampleInfo
);
86 PluralRules::clone() const {
87 return new PluralRules(*this);
91 PluralRules::operator=(const PluralRules
& other
) {
94 if (other
.mRules
==NULL
) {
98 mRules
= new RuleChain(*other
.mRules
);
101 mParser
= new RuleParser();
106 uprv_free(mSampleInfo
);
108 mSampleInfoCount
= 0;
114 PluralRules
* U_EXPORT2
115 PluralRules::createRules(const UnicodeString
& description
, UErrorCode
& status
) {
118 if (U_FAILURE(status
)) {
121 PluralRules
*newRules
= new PluralRules(status
);
122 if ( (newRules
!= NULL
)&& U_SUCCESS(status
) ) {
123 newRules
->parseDescription((UnicodeString
&)description
, rules
, status
);
124 if (U_SUCCESS(status
)) {
125 newRules
->addRules(rules
);
128 if (U_FAILURE(status
)) {
137 PluralRules
* U_EXPORT2
138 PluralRules::createDefaultRules(UErrorCode
& status
) {
139 return createRules(UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1), status
);
142 PluralRules
* U_EXPORT2
143 PluralRules::forLocale(const Locale
& locale
, UErrorCode
& status
) {
144 return forLocale(locale
, UPLURAL_TYPE_CARDINAL
, status
);
147 PluralRules
* U_EXPORT2
148 PluralRules::forLocale(const Locale
& locale
, UPluralType type
, UErrorCode
& status
) {
150 if (U_FAILURE(status
)) {
153 if (type
>= UPLURAL_TYPE_COUNT
) {
154 status
= U_ILLEGAL_ARGUMENT_ERROR
;
157 PluralRules
*newObj
= new PluralRules(status
);
158 if (newObj
==NULL
|| U_FAILURE(status
)) {
162 UnicodeString locRule
= newObj
->getRuleFromResource(locale
, type
, status
);
163 if ((locRule
.length() != 0) && U_SUCCESS(status
)) {
164 newObj
->parseDescription(locRule
, rChain
, status
);
165 if (U_SUCCESS(status
)) {
166 newObj
->addRules(rChain
);
169 if (U_FAILURE(status
)||(locRule
.length() == 0)) {
170 // use default plural rule
171 status
= U_ZERO_ERROR
;
172 UnicodeString defRule
= UnicodeString(PLURAL_DEFAULT_RULE
);
173 newObj
->parseDescription(defRule
, rChain
, status
);
174 newObj
->addRules(rChain
);
181 PluralRules::select(int32_t number
) const {
182 if (mRules
== NULL
) {
183 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
186 return mRules
->select(number
);
191 PluralRules::select(double number
) const {
192 if (mRules
== NULL
) {
193 return UnicodeString(TRUE
, PLURAL_DEFAULT_RULE
, -1);
196 return mRules
->select(number
);
201 PluralRules::getKeywords(UErrorCode
& status
) const {
202 if (U_FAILURE(status
)) return NULL
;
203 StringEnumeration
* nameEnumerator
= new PluralKeywordEnumeration(mRules
, status
);
204 if (U_FAILURE(status
)) {
205 delete nameEnumerator
;
209 return nameEnumerator
;
213 PluralRules::getUniqueKeywordValue(const UnicodeString
& keyword
) {
215 UErrorCode status
= U_ZERO_ERROR
;
216 int32_t count
= getSamplesInternal(keyword
, &val
, 1, FALSE
, status
);
217 return count
== 1 ? val
: UPLRULES_NO_UNIQUE_VALUE
;
221 PluralRules::getAllKeywordValues(const UnicodeString
&keyword
, double *dest
,
222 int32_t destCapacity
, UErrorCode
& error
) {
223 return getSamplesInternal(keyword
, dest
, destCapacity
, FALSE
, error
);
227 PluralRules::getSamples(const UnicodeString
&keyword
, double *dest
,
228 int32_t destCapacity
, UErrorCode
& status
) {
229 return getSamplesInternal(keyword
, dest
, destCapacity
, TRUE
, status
);
233 PluralRules::getSamplesInternal(const UnicodeString
&keyword
, double *dest
,
234 int32_t destCapacity
, UBool includeUnlimited
,
235 UErrorCode
& status
) {
237 if (U_FAILURE(status
)) {
240 if (destCapacity
< 0 || (dest
== NULL
&& destCapacity
> 0)) {
241 status
= U_ILLEGAL_ARGUMENT_ERROR
;
245 int32_t index
= getKeywordIndex(keyword
, status
);
250 const int32_t LIMIT_MASK
= 0x1 << 31;
252 if (!includeUnlimited
) {
253 if ((mSampleInfo
[index
] & LIMIT_MASK
) == 0) {
258 int32_t start
= index
== 0 ? 0 : mSampleInfo
[index
- 1] & ~LIMIT_MASK
;
259 int32_t limit
= mSampleInfo
[index
] & ~LIMIT_MASK
;
260 int32_t len
= limit
- start
;
261 if (len
<= destCapacity
) {
263 } else if (includeUnlimited
) {
264 len
= destCapacity
; // no overflow, and don't report more than we copy
266 status
= U_BUFFER_OVERFLOW_ERROR
;
269 for (int32_t i
= 0; i
< destCapacity
; ++i
, ++start
) {
270 dest
[i
] = mSamples
[start
];
277 PluralRules::isKeyword(const UnicodeString
& keyword
) const {
278 if (0 == keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
286 return mRules
->isKeyword(keyword
);
292 PluralRules::getKeywordOther() const {
293 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
297 PluralRules::operator==(const PluralRules
& other
) const {
299 const UnicodeString
*ptrKeyword
;
300 UErrorCode status
= U_ZERO_ERROR
;
302 if ( this == &other
) {
305 LocalPointer
<StringEnumeration
> myKeywordList(getKeywords(status
));
306 LocalPointer
<StringEnumeration
> otherKeywordList(other
.getKeywords(status
));
307 if (U_FAILURE(status
)) {
311 if (myKeywordList
->count(status
)!=otherKeywordList
->count(status
)) {
314 myKeywordList
->reset(status
);
315 while ((ptrKeyword
=myKeywordList
->snext(status
))!=NULL
) {
316 if (!other
.isKeyword(*ptrKeyword
)) {
320 otherKeywordList
->reset(status
);
321 while ((ptrKeyword
=otherKeywordList
->snext(status
))!=NULL
) {
322 if (!this->isKeyword(*ptrKeyword
)) {
326 if (U_FAILURE(status
)) {
330 if ((limit
=this->getRepeatLimit()) != other
.getRepeatLimit()) {
333 UnicodeString myKeyword
, otherKeyword
;
334 for (int32_t i
=0; i
<limit
; ++i
) {
335 myKeyword
= this->select(i
);
336 otherKeyword
= other
.select(i
);
337 if (myKeyword
!=otherKeyword
) {
345 PluralRules::parseDescription(UnicodeString
& data
, RuleChain
& rules
, UErrorCode
&status
)
350 tokenType prevType
=none
;
351 RuleChain
*ruleChain
=NULL
;
352 AndConstraint
*curAndConstraint
=NULL
;
353 OrConstraint
*orNode
=NULL
;
354 RuleChain
*lastChain
=NULL
;
356 if (U_FAILURE(status
)) {
359 UnicodeString ruleData
= data
.toLower("");
360 while (ruleIndex
< ruleData
.length()) {
361 mParser
->getNextToken(ruleData
, &ruleIndex
, token
, type
, status
);
362 if (U_FAILURE(status
)) {
365 mParser
->checkSyntax(prevType
, type
, status
);
366 if (U_FAILURE(status
)) {
371 U_ASSERT(curAndConstraint
!= NULL
);
372 curAndConstraint
= curAndConstraint
->add();
376 while (lastChain
->next
!=NULL
) {
377 lastChain
= lastChain
->next
;
379 orNode
=lastChain
->ruleHeader
;
380 while (orNode
->next
!= NULL
) {
381 orNode
= orNode
->next
;
383 orNode
->next
= new OrConstraint();
386 curAndConstraint
= orNode
->add();
389 U_ASSERT(curAndConstraint
!= NULL
);
390 curAndConstraint
->rangeHigh
=-1;
393 U_ASSERT(curAndConstraint
!= NULL
);
394 curAndConstraint
->notIn
=TRUE
;
397 U_ASSERT(curAndConstraint
!= NULL
);
398 curAndConstraint
->rangeHigh
=PLURAL_RANGE_HIGH
;
399 curAndConstraint
->integerOnly
= TRUE
;
402 U_ASSERT(curAndConstraint
!= NULL
);
403 curAndConstraint
->rangeHigh
=PLURAL_RANGE_HIGH
;
406 U_ASSERT(curAndConstraint
!= NULL
);
407 if ( (curAndConstraint
->op
==AndConstraint::MOD
)&&
408 (curAndConstraint
->opNum
== -1 ) ) {
409 curAndConstraint
->opNum
=getNumberValue(token
);
412 if (curAndConstraint
->rangeLow
== -1) {
413 curAndConstraint
->rangeLow
=getNumberValue(token
);
416 curAndConstraint
->rangeHigh
=getNumberValue(token
);
421 U_ASSERT(curAndConstraint
!= NULL
);
422 curAndConstraint
->op
=AndConstraint::MOD
;
425 if (ruleChain
==NULL
) {
429 while (ruleChain
->next
!=NULL
){
430 ruleChain
=ruleChain
->next
;
432 ruleChain
=ruleChain
->next
=new RuleChain();
434 if (ruleChain
->ruleHeader
!= NULL
) {
435 delete ruleChain
->ruleHeader
;
437 orNode
= ruleChain
->ruleHeader
= new OrConstraint();
438 curAndConstraint
= orNode
->add();
439 ruleChain
->keyword
= token
;
449 PluralRules::getNumberValue(const UnicodeString
& token
) const {
453 i
= token
.extract(0, token
.length(), digits
, ARRAY_SIZE(digits
), US_INV
);
456 return((int32_t)atoi(digits
));
461 PluralRules::getNextLocale(const UnicodeString
& localeData
, int32_t* curIndex
, UnicodeString
& localeName
) {
465 while (i
< localeData
.length()) {
466 if ( (localeData
.charAt(i
)!= SPACE
) && (localeData
.charAt(i
)!= COMMA
) ) {
472 while (i
< localeData
.length()) {
473 if ( (localeData
.charAt(i
)== SPACE
) || (localeData
.charAt(i
)== COMMA
) ) {
476 localeName
+=localeData
.charAt(i
++);
483 PluralRules::getRepeatLimit() const {
485 return mRules
->getRepeatLimit();
493 PluralRules::getKeywordIndex(const UnicodeString
& keyword
,
494 UErrorCode
& status
) const {
495 if (U_SUCCESS(status
)) {
497 RuleChain
* rc
= mRules
;
499 if (rc
->ruleHeader
!= NULL
) {
500 if (rc
->keyword
== keyword
) {
507 if (0 == keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
514 typedef struct SampleRecord
{
520 PluralRules::initSamples(UErrorCode
& status
) {
521 if (U_FAILURE(status
)) {
524 Mutex
lock(&pluralMutex
);
530 // Note, the original design let you have multiple rules with the same keyword. But
531 // we don't use that in our data and existing functions in this implementation don't
532 // fully support it (for example, the returned keywords is a list and not a set).
534 // So I don't support this here either. If you ask for samples, or for all values,
535 // you will get information about the first rule with that keyword, not all rules with
538 int32_t maxIndex
= 0;
539 int32_t otherIndex
= -1; // the value -1 will indicate we added 'other' at end
540 RuleChain
* rc
= mRules
;
542 if (rc
->ruleHeader
!= NULL
) {
543 if (otherIndex
== -1 && 0 == rc
->keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
544 otherIndex
= maxIndex
;
550 if (otherIndex
== -1) {
554 LocalMemory
<int32_t> newSampleInfo
;
555 if (NULL
== newSampleInfo
.allocateInsteadAndCopy(maxIndex
)) {
556 status
= U_MEMORY_ALLOCATION_ERROR
;
560 const int32_t LIMIT_MASK
= 0x1 << 31;
565 if (rc
->ruleHeader
!= NULL
) {
566 newSampleInfo
[n
++] = rc
->ruleHeader
->isLimited() ? LIMIT_MASK
: 0;
570 if (otherIndex
== -1) {
571 newSampleInfo
[maxIndex
- 1] = 0; // unlimited
574 MaybeStackArray
<SampleRecord
, 10> newSamples
;
575 int32_t sampleCount
= 0;
577 int32_t limit
= getRepeatLimit() * MAX_SAMPLES
* 2;
582 for (int i
= 0, keywordsRemaining
= maxIndex
;
583 keywordsRemaining
> 0 && i
< limit
;
585 double val
= i
/ 2.0;
591 if (rc
->ruleHeader
!= NULL
) {
592 if (rc
->ruleHeader
->isFulfilled(val
)) {
601 // 'other'. If there is an 'other' rule, the rule set is bad since nothing
602 // should leak through, but we don't bother to report that here.
603 found
= otherIndex
== -1 ? maxIndex
- 1 : otherIndex
;
605 if (newSampleInfo
[found
] == MAX_SAMPLES
) { // limit flag not set
608 newSampleInfo
[found
] += 1; // won't impact limit flag
610 if (sampleCount
== newSamples
.getCapacity()) {
611 int32_t newCapacity
= sampleCount
< 20 ? 128 : sampleCount
* 2;
612 if (NULL
== newSamples
.resize(newCapacity
, sampleCount
)) {
613 status
= U_MEMORY_ALLOCATION_ERROR
;
617 newSamples
[sampleCount
].ruleIndex
= found
;
618 newSamples
[sampleCount
].value
= val
;
621 if (newSampleInfo
[found
] == MAX_SAMPLES
) { // limit flag not set
626 // sort the values by index, leaving order otherwise unchanged
627 // this is just a selection sort for simplicity
628 LocalMemory
<double> values
;
629 if (NULL
== values
.allocateInsteadAndCopy(sampleCount
)) {
630 status
= U_MEMORY_ALLOCATION_ERROR
;
633 for (int i
= 0, j
= 0; i
< maxIndex
; ++i
) {
634 for (int k
= 0; k
< sampleCount
; ++k
) {
635 if (newSamples
[k
].ruleIndex
== i
) {
636 values
[j
++] = newSamples
[k
].value
;
641 // convert array of mask/lengths to array of mask/limits
643 for (int i
= 0; i
< maxIndex
; ++i
) {
644 int32_t info
= newSampleInfo
[i
];
645 int32_t len
= info
& ~LIMIT_MASK
;
647 // if a rule is 'unlimited' but has fewer than MAX_SAMPLES samples,
648 // it's not really unlimited, so mark it as limited
649 int32_t mask
= len
< MAX_SAMPLES
? LIMIT_MASK
: info
& LIMIT_MASK
;
650 newSampleInfo
[i
] = limit
| mask
;
653 // ok, we've got good data
654 mSamples
= values
.orphan();
655 mSampleInfo
= newSampleInfo
.orphan();
656 mSampleInfoCount
= maxIndex
;
660 PluralRules::addRules(RuleChain
& rules
) {
661 RuleChain
*newRule
= new RuleChain(rules
);
662 this->mRules
=newRule
;
663 newRule
->setRepeatLimit();
667 PluralRules::getRuleFromResource(const Locale
& locale
, UPluralType type
, UErrorCode
& errCode
) {
668 UnicodeString emptyStr
;
670 if (U_FAILURE(errCode
)) {
673 LocalUResourceBundlePointer
rb(ures_openDirect(NULL
, "plurals", &errCode
));
674 if(U_FAILURE(errCode
)) {
679 case UPLURAL_TYPE_CARDINAL
:
682 case UPLURAL_TYPE_ORDINAL
:
683 typeKey
= "locales_ordinals";
686 // Must not occur: The caller should have checked for valid types.
687 errCode
= U_ILLEGAL_ARGUMENT_ERROR
;
690 LocalUResourceBundlePointer
locRes(ures_getByKey(rb
.getAlias(), typeKey
, NULL
, &errCode
));
691 if(U_FAILURE(errCode
)) {
695 const char *curLocaleName
=locale
.getName();
696 const UChar
* s
= ures_getStringByKey(locRes
.getAlias(), curLocaleName
, &resLen
, &errCode
);
699 // Check parent locales.
700 UErrorCode status
= U_ZERO_ERROR
;
701 char parentLocaleName
[ULOC_FULLNAME_CAPACITY
];
702 const char *curLocaleName
=locale
.getName();
703 uprv_strcpy(parentLocaleName
, curLocaleName
);
705 while (uloc_getParent(parentLocaleName
, parentLocaleName
,
706 ULOC_FULLNAME_CAPACITY
, &status
) > 0) {
708 s
= ures_getStringByKey(locRes
.getAlias(), parentLocaleName
, &resLen
, &status
);
710 errCode
= U_ZERO_ERROR
;
713 status
= U_ZERO_ERROR
;
722 u_UCharsToChars(s
, setKey
, resLen
+ 1);
723 // printf("\n PluralRule: %s\n", setKey);
726 LocalUResourceBundlePointer
ruleRes(ures_getByKey(rb
.getAlias(), "rules", NULL
, &errCode
));
727 if(U_FAILURE(errCode
)) {
731 LocalUResourceBundlePointer
setRes(ures_getByKey(ruleRes
.getAlias(), setKey
, NULL
, &errCode
));
732 if (U_FAILURE(errCode
)) {
736 int32_t numberKeys
= ures_getSize(setRes
.getAlias());
739 for(int32_t i
=0; i
<numberKeys
; ++i
) {
742 s
=ures_getNextString(setRes
.getAlias(), &resLen
, (const char**)&key
, &errCode
);
743 keyLen
= (int32_t)uprv_strlen(key
);
744 u_charsToUChars(key
, result
+len
, keyLen
);
747 uprv_memcpy(result
+len
, s
, resLen
*sizeof(UChar
));
749 result
[len
++]=SEMI_COLON
;
752 u_UCharsToChars(result
, setKey
, len
);
753 // printf(" Rule: %s\n", setKey);
755 return UnicodeString(result
);
758 AndConstraint::AndConstraint() {
759 op
= AndConstraint::NONE
;
769 AndConstraint::AndConstraint(const AndConstraint
& other
) {
771 this->opNum
=other
.opNum
;
772 this->rangeLow
=other
.rangeLow
;
773 this->rangeHigh
=other
.rangeHigh
;
774 this->integerOnly
=other
.integerOnly
;
775 this->notIn
=other
.notIn
;
776 if (other
.next
==NULL
) {
780 this->next
= new AndConstraint(*other
.next
);
784 AndConstraint::~AndConstraint() {
792 AndConstraint::isFulfilled(double number
) {
797 if ((rangeHigh
== -1 || integerOnly
) && number
!= uprv_floor(number
)) {
802 value
= (int32_t)value
% opNum
;
804 if ( rangeHigh
== -1 ) {
805 if ( rangeLow
== -1 ) {
806 result
= TRUE
; // empty rule
809 if ( value
== rangeLow
) {
818 if ((rangeLow
<= value
) && (value
<= rangeHigh
)) {
820 if ( value
!= (int32_t)value
) {
844 AndConstraint::isLimited() {
845 return (rangeHigh
== -1 || integerOnly
) && !notIn
&& op
!= MOD
;
849 AndConstraint::updateRepeatLimit(int32_t maxLimit
) {
852 return uprv_max(opNum
, maxLimit
);
855 if ( rangeHigh
== -1 ) {
856 return uprv_max(rangeLow
, maxLimit
);
859 return uprv_max(rangeHigh
, maxLimit
);
868 this->next
= new AndConstraint();
872 OrConstraint::OrConstraint() {
877 OrConstraint::OrConstraint(const OrConstraint
& other
) {
878 if ( other
.childNode
== NULL
) {
879 this->childNode
= NULL
;
882 this->childNode
= new AndConstraint(*(other
.childNode
));
884 if (other
.next
== NULL
) {
888 this->next
= new OrConstraint(*(other
.next
));
892 OrConstraint::~OrConstraint() {
893 if (childNode
!=NULL
) {
904 OrConstraint
*curOrConstraint
=this;
906 while (curOrConstraint
->next
!=NULL
) {
907 curOrConstraint
= curOrConstraint
->next
;
909 curOrConstraint
->next
= NULL
;
910 curOrConstraint
->childNode
= new AndConstraint();
912 return curOrConstraint
->childNode
;
916 OrConstraint::isFulfilled(double number
) {
917 OrConstraint
* orRule
=this;
920 while (orRule
!=NULL
&& !result
) {
922 AndConstraint
* andRule
= orRule
->childNode
;
923 while (andRule
!=NULL
&& result
) {
924 result
= andRule
->isFulfilled(number
);
925 andRule
=andRule
->next
;
927 orRule
= orRule
->next
;
934 OrConstraint::isLimited() {
935 for (OrConstraint
*orc
= this; orc
!= NULL
; orc
= orc
->next
) {
936 UBool result
= FALSE
;
937 for (AndConstraint
*andc
= orc
->childNode
; andc
!= NULL
; andc
= andc
->next
) {
938 if (andc
->isLimited()) {
943 if (result
== FALSE
) {
950 RuleChain::RuleChain() {
956 RuleChain::RuleChain(const RuleChain
& other
) {
957 this->repeatLimit
= other
.repeatLimit
;
958 this->keyword
=other
.keyword
;
959 if (other
.ruleHeader
!= NULL
) {
960 this->ruleHeader
= new OrConstraint(*(other
.ruleHeader
));
963 this->ruleHeader
= NULL
;
965 if (other
.next
!= NULL
) {
966 this->next
= new RuleChain(*other
.next
);
974 RuleChain::~RuleChain() {
978 if ( ruleHeader
!= NULL
) {
984 RuleChain::select(double number
) const {
986 if ( ruleHeader
!= NULL
) {
987 if (ruleHeader
->isFulfilled(number
)) {
991 if ( next
!= NULL
) {
992 return next
->select(number
);
995 return UnicodeString(TRUE
, PLURAL_KEYWORD_OTHER
, 5);
1001 RuleChain::dumpRules(UnicodeString
& result
) {
1002 UChar digitString
[16];
1004 if ( ruleHeader
!= NULL
) {
1006 OrConstraint
* orRule
=ruleHeader
;
1007 while ( orRule
!= NULL
) {
1008 AndConstraint
* andRule
=orRule
->childNode
;
1009 while ( andRule
!= NULL
) {
1010 if ( (andRule
->op
==AndConstraint::NONE
) && (andRule
->rangeHigh
==-1) ) {
1011 result
+= UNICODE_STRING_SIMPLE(" n is ");
1012 if (andRule
->notIn
) {
1013 result
+= UNICODE_STRING_SIMPLE("not ");
1015 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1016 result
+= UnicodeString(digitString
);
1019 if (andRule
->op
==AndConstraint::MOD
) {
1020 result
+= UNICODE_STRING_SIMPLE(" n mod ");
1021 uprv_itou(digitString
,16, andRule
->opNum
,10,0);
1022 result
+= UnicodeString(digitString
);
1025 result
+= UNICODE_STRING_SIMPLE(" n ");
1027 if (andRule
->rangeHigh
==-1) {
1028 if (andRule
->notIn
) {
1029 result
+= UNICODE_STRING_SIMPLE(" is not ");
1030 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1031 result
+= UnicodeString(digitString
);
1034 result
+= UNICODE_STRING_SIMPLE(" is ");
1035 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1036 result
+= UnicodeString(digitString
);
1040 if (andRule
->notIn
) {
1041 if ( andRule
->integerOnly
) {
1042 result
+= UNICODE_STRING_SIMPLE(" not in ");
1045 result
+= UNICODE_STRING_SIMPLE(" not within ");
1047 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1048 result
+= UnicodeString(digitString
);
1049 result
+= UNICODE_STRING_SIMPLE(" .. ");
1050 uprv_itou(digitString
,16, andRule
->rangeHigh
,10,0);
1051 result
+= UnicodeString(digitString
);
1054 if ( andRule
->integerOnly
) {
1055 result
+= UNICODE_STRING_SIMPLE(" in ");
1058 result
+= UNICODE_STRING_SIMPLE(" within ");
1060 uprv_itou(digitString
,16, andRule
->rangeLow
,10,0);
1061 result
+= UnicodeString(digitString
);
1062 result
+= UNICODE_STRING_SIMPLE(" .. ");
1063 uprv_itou(digitString
,16, andRule
->rangeHigh
,10,0);
1067 if ( (andRule
=andRule
->next
) != NULL
) {
1068 result
.append(PK_AND
, 3);
1071 if ( (orRule
= orRule
->next
) != NULL
) {
1072 result
.append(PK_OR
, 2);
1076 if ( next
!= NULL
) {
1077 next
->dumpRules(result
);
1082 RuleChain::getRepeatLimit () {
1087 RuleChain::setRepeatLimit () {
1090 if ( next
!= NULL
) {
1091 next
->setRepeatLimit();
1092 limit
= next
->repeatLimit
;
1095 if ( ruleHeader
!= NULL
) {
1096 OrConstraint
* orRule
=ruleHeader
;
1097 while ( orRule
!= NULL
) {
1098 AndConstraint
* andRule
=orRule
->childNode
;
1099 while ( andRule
!= NULL
) {
1100 limit
= andRule
->updateRepeatLimit(limit
);
1101 andRule
= andRule
->next
;
1103 orRule
= orRule
->next
;
1106 repeatLimit
= limit
;
1110 RuleChain::getKeywords(int32_t capacityOfKeywords
, UnicodeString
* keywords
, int32_t& arraySize
) const {
1111 if ( arraySize
< capacityOfKeywords
-1 ) {
1112 keywords
[arraySize
++]=keyword
;
1115 return U_BUFFER_OVERFLOW_ERROR
;
1118 if ( next
!= NULL
) {
1119 return next
->getKeywords(capacityOfKeywords
, keywords
, arraySize
);
1122 return U_ZERO_ERROR
;
1127 RuleChain::isKeyword(const UnicodeString
& keywordParam
) const {
1128 if ( keyword
== keywordParam
) {
1132 if ( next
!= NULL
) {
1133 return next
->isKeyword(keywordParam
);
1141 RuleParser::RuleParser() {
1144 RuleParser::~RuleParser() {
1148 RuleParser::checkSyntax(tokenType prevType
, tokenType curType
, UErrorCode
&status
)
1150 if (U_FAILURE(status
)) {
1156 if (curType
!=tKeyword
) {
1157 status
= U_UNEXPECTED_TOKEN
;
1161 if (curType
!= tIs
&& curType
!= tMod
&& curType
!= tIn
&&
1162 curType
!= tNot
&& curType
!= tWithin
) {
1163 status
= U_UNEXPECTED_TOKEN
;
1173 if (curType
!= tColon
) {
1174 status
= U_UNEXPECTED_TOKEN
;
1178 if (curType
!= tVariableN
) {
1179 status
= U_UNEXPECTED_TOKEN
;
1183 if ( curType
!= tNumber
&& curType
!= tNot
) {
1184 status
= U_UNEXPECTED_TOKEN
;
1188 if (curType
!= tNumber
&& curType
!= tIn
&& curType
!= tWithin
) {
1189 status
= U_UNEXPECTED_TOKEN
;
1198 if (curType
!= tNumber
&& curType
!= tVariableN
) {
1199 status
= U_UNEXPECTED_TOKEN
;
1203 if (curType
!= tDot
&& curType
!= tSemiColon
&& curType
!= tIs
&& curType
!= tNot
&&
1204 curType
!= tIn
&& curType
!= tWithin
&& curType
!= tAnd
&& curType
!= tOr
)
1206 status
= U_UNEXPECTED_TOKEN
;
1210 status
= U_UNEXPECTED_TOKEN
;
1216 RuleParser::getNextToken(const UnicodeString
& ruleData
,
1218 UnicodeString
& token
,
1222 int32_t curIndex
= *ruleIndex
;
1224 tokenType prevType
=none
;
1226 if (U_FAILURE(status
)) {
1229 while (curIndex
<ruleData
.length()) {
1230 ch
= ruleData
.charAt(curIndex
);
1231 if ( !inRange(ch
, type
) ) {
1232 status
= U_ILLEGAL_CHARACTER
;
1237 if ( *ruleIndex
!= curIndex
) { // letter
1238 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1239 *ruleIndex
=curIndex
;
1241 getKeyType(token
, type
, status
);
1245 *ruleIndex
=*ruleIndex
+1;
1247 break; // consective space
1250 if ( *ruleIndex
!= curIndex
) {
1251 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1252 *ruleIndex
=curIndex
;
1254 getKeyType(token
, type
, status
);
1258 *ruleIndex
=curIndex
+1;
1262 if ((type
==prevType
)||(prevType
==none
)) {
1268 if ((type
==prevType
)||(prevType
==none
)) {
1273 *ruleIndex
=curIndex
+1;
1277 if (prevType
==none
) { // first dot
1282 if ( *ruleIndex
!= curIndex
) {
1283 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1284 *ruleIndex
=curIndex
; // letter
1286 getKeyType(token
, type
, status
);
1289 else { // two consective dots
1290 *ruleIndex
=curIndex
+2;
1295 status
= U_UNEXPECTED_TOKEN
;
1300 if ( curIndex
>=ruleData
.length() ) {
1301 if ( (type
== tLetter
)||(type
== tNumber
) ) {
1302 token
=UnicodeString(ruleData
, *ruleIndex
, curIndex
-*ruleIndex
);
1303 getKeyType(token
, type
, status
);
1304 if (U_FAILURE(status
)) {
1308 *ruleIndex
= ruleData
.length();
1313 RuleParser::inRange(UChar ch
, tokenType
& type
) {
1314 if ((ch
>=CAP_A
) && (ch
<=CAP_Z
)) {
1315 // we assume all characters are in lower case already.
1318 if ((ch
>=LOW_A
) && (ch
<=LOW_Z
)) {
1322 if ((ch
>=U_ZERO
) && (ch
<=U_NINE
)) {
1347 RuleParser::getKeyType(const UnicodeString
& token
, tokenType
& keyType
, UErrorCode
&status
)
1349 if (U_FAILURE(status
)) {
1352 if ( keyType
==tNumber
) {
1354 else if (0 == token
.compare(PK_VAR_N
, 1)) {
1355 keyType
= tVariableN
;
1357 else if (0 == token
.compare(PK_IS
, 2)) {
1360 else if (0 == token
.compare(PK_AND
, 3)) {
1363 else if (0 == token
.compare(PK_IN
, 2)) {
1366 else if (0 == token
.compare(PK_WITHIN
, 6)) {
1369 else if (0 == token
.compare(PK_NOT
, 3)) {
1372 else if (0 == token
.compare(PK_MOD
, 3)) {
1375 else if (0 == token
.compare(PK_OR
, 2)) {
1378 else if ( isValidKeyword(token
) ) {
1382 status
= U_UNEXPECTED_TOKEN
;
1387 RuleParser::isValidKeyword(const UnicodeString
& token
) {
1388 return PatternProps::isIdentifier(token
.getBuffer(), token
.length());
1391 PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain
*header
, UErrorCode
& status
)
1392 : pos(0), fKeywordNames(status
) {
1393 if (U_FAILURE(status
)) {
1396 fKeywordNames
.setDeleter(uprv_deleteUObject
);
1397 UBool addKeywordOther
=TRUE
;
1398 RuleChain
*node
=header
;
1400 fKeywordNames
.addElement(new UnicodeString(node
->keyword
), status
);
1401 if (U_FAILURE(status
)) {
1404 if (0 == node
->keyword
.compare(PLURAL_KEYWORD_OTHER
, 5)) {
1405 addKeywordOther
= FALSE
;
1410 if (addKeywordOther
) {
1411 fKeywordNames
.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER
), status
);
1415 const UnicodeString
*
1416 PluralKeywordEnumeration::snext(UErrorCode
& status
) {
1417 if (U_SUCCESS(status
) && pos
< fKeywordNames
.size()) {
1418 return (const UnicodeString
*)fKeywordNames
.elementAt(pos
++);
1424 PluralKeywordEnumeration::reset(UErrorCode
& /*status*/) {
1429 PluralKeywordEnumeration::count(UErrorCode
& /*status*/) const {
1430 return fKeywordNames
.size();
1433 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
1439 #endif /* #if !UCONFIG_NO_FORMATTING */