]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/plurrule.cpp
ICU-400.37.tar.gz
[apple/icu.git] / icuSources / i18n / plurrule.cpp
diff --git a/icuSources/i18n/plurrule.cpp b/icuSources/i18n/plurrule.cpp
new file mode 100644 (file)
index 0000000..3f4bf23
--- /dev/null
@@ -0,0 +1,1164 @@
+/*
+*******************************************************************************
+* Copyright (C) 2007-2008, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File PLURRULE.CPP
+*
+* Modification History:
+*
+*   Date        Name        Description
+*******************************************************************************
+*/
+
+
+#include "unicode/uniset.h"
+#include "unicode/utypes.h"
+#include "unicode/ures.h"
+#include "unicode/plurrule.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "hash.h"
+#include "mutex.h"
+#include "plurrule_impl.h"
+#include "putilimp.h"
+#include "ucln_in.h"
+#include "ustrfmt.h"
+#include "locutil.h"
+
+/*
+// TODO(claireho): remove stdio
+#include "stdio.h"
+*/
+
+#if !UCONFIG_NO_FORMATTING
+
+U_NAMESPACE_BEGIN
+
+
+#define ARRAY_SIZE(array) (int32_t)(sizeof array  / sizeof array[0])
+
+static const UChar PLURAL_KEYWORD_ZERO[] = {LOW_Z,LOW_E,LOW_R,LOW_O, 0};
+static const UChar PLURAL_KEYWORD_ONE[]={LOW_O,LOW_N,LOW_E,0};
+static const UChar PLURAL_KEYWORD_TWO[]={LOW_T,LOW_W,LOW_O,0};
+static const UChar PLURAL_KEYWORD_FEW[]={LOW_F,LOW_E,LOW_W,0};
+static const UChar PLURAL_KEYWORD_MANY[]={LOW_M,LOW_A,LOW_N,LOW_Y,0};
+static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0};
+static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0};
+static const UChar PK_IN[]={LOW_I,LOW_N,0};
+static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0};
+static const UChar PK_IS[]={LOW_I,LOW_S,0};
+static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0};
+static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0};
+static const UChar PK_OR[]={LOW_O,LOW_R,0};
+static const UChar PK_VAR_N[]={LOW_N,0};
+static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules)
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
+
+PluralRules::PluralRules(UErrorCode& status)
+:   UObject(),
+    mRules(NULL),
+    mParser(new RuleParser())
+{
+    if (mParser==NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+}
+
+PluralRules::PluralRules(const PluralRules& other)
+: UObject(other),
+    mRules(NULL),
+    mParser(new RuleParser())
+{
+    *this=other;
+}
+
+PluralRules::~PluralRules() {
+    delete mRules;
+    delete mParser;
+}
+
+PluralRules*
+PluralRules::clone() const {
+    return new PluralRules(*this);
+}
+
+PluralRules&
+PluralRules::operator=(const PluralRules& other) {
+    if (this != &other) {
+        delete mRules;
+        if (other.mRules==NULL) {
+            mRules = NULL;
+        }
+        else {
+            mRules = new RuleChain(*other.mRules);
+        }
+        delete mParser;
+        mParser = new RuleParser();
+    }
+
+    return *this;
+}
+
+PluralRules* U_EXPORT2
+PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
+    RuleChain   rules;
+
+    PluralRules *newRules = new PluralRules(status);
+    if ( (newRules != NULL)&& U_SUCCESS(status) ) {
+        newRules->parseDescription((UnicodeString &)description, rules, status);
+        if (U_SUCCESS(status)) {
+            newRules->addRules(rules);
+        }
+    }
+    if (U_FAILURE(status)) {
+        delete newRules;
+        return NULL;
+    }
+    else {
+        return newRules;
+    }
+}
+
+PluralRules* U_EXPORT2
+PluralRules::createDefaultRules(UErrorCode& status) {
+    return createRules(PLURAL_DEFAULT_RULE, status);
+}
+
+PluralRules* U_EXPORT2
+PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
+    RuleChain   rChain;
+    status = U_ZERO_ERROR;
+    PluralRules *newObj = new PluralRules(status);
+    if (newObj==NULL) {
+        return NULL;
+    }
+    UnicodeString locRule = newObj->getRuleFromResource(locale, status);
+    if ((locRule.length() != 0) && U_SUCCESS(status)) {
+        newObj->parseDescription(locRule, rChain, status);
+        if (U_SUCCESS(status)) {
+            newObj->addRules(rChain);
+        }
+    }
+    if (U_FAILURE(status)||(locRule.length() == 0)) {
+        // use default plural rule
+        status = U_ZERO_ERROR;
+        UnicodeString defRule = UnicodeString(PLURAL_DEFAULT_RULE);
+        newObj->parseDescription(defRule, rChain, status);
+        newObj->addRules(rChain);
+    }
+    
+    return newObj;
+}
+
+UnicodeString
+PluralRules::select(int32_t number) const {
+    if (mRules == NULL) {
+        return PLURAL_DEFAULT_RULE;
+    }
+    else {
+        return mRules->select(number);
+    }
+}
+
+UnicodeString
+PluralRules::select(double number) const {
+    if (mRules == NULL) {
+        return PLURAL_DEFAULT_RULE;
+    }
+    else {
+        return mRules->select(number);
+    }
+}
+
+StringEnumeration*
+PluralRules::getKeywords(UErrorCode& status) const {
+    if (U_FAILURE(status))  return NULL;
+    StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status);
+    return nameEnumerator;
+}
+
+
+UBool
+PluralRules::isKeyword(const UnicodeString& keyword) const {
+    if ( keyword == PLURAL_KEYWORD_OTHER ) {
+        return true;
+    }
+    else {
+        if (mRules==NULL) {
+            return false;
+        }
+        else {
+            return mRules->isKeyword(keyword);
+        }
+    }
+}
+
+UnicodeString
+PluralRules::getKeywordOther() const {
+    return PLURAL_KEYWORD_OTHER;
+}
+
+UBool
+PluralRules::operator==(const PluralRules& other) const  {
+    int32_t limit;
+    UBool sameList = TRUE;
+    const UnicodeString *ptrKeyword;
+    UErrorCode status= U_ZERO_ERROR;
+
+    if ( this == &other ) {
+        return TRUE;
+    }
+    StringEnumeration* myKeywordList = getKeywords(status);
+    StringEnumeration* otherKeywordList =other.getKeywords(status);
+
+    if (myKeywordList->count(status)!=otherKeywordList->count(status)) {
+        sameList = FALSE;
+    }
+    else {
+        myKeywordList->reset(status);
+        while (sameList && (ptrKeyword=myKeywordList->snext(status))!=NULL) {
+            if (!other.isKeyword(*ptrKeyword)) {
+                sameList = FALSE;
+            }
+        }
+        otherKeywordList->reset(status);
+        while (sameList && (ptrKeyword=otherKeywordList->snext(status))!=NULL) {
+            if (!this->isKeyword(*ptrKeyword))  {
+                sameList = FALSE;
+            }
+        }
+        delete myKeywordList;
+        delete otherKeywordList;
+        if (!sameList) {
+            return FALSE;
+        }
+    }
+
+    if ((limit=this->getRepeatLimit()) != other.getRepeatLimit()) {
+        return FALSE;
+    }
+    UnicodeString myKeyword, otherKeyword;
+    for (int32_t i=0; i<limit; ++i) {
+        myKeyword = this->select(i);
+        otherKeyword = other.select(i);
+        if (myKeyword!=otherKeyword) {
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+void
+PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode &status)
+{
+    int32_t ruleIndex=0;
+    UnicodeString token;
+    tokenType type;
+    tokenType prevType=none;
+    RuleChain *ruleChain=NULL;
+    AndConstraint *curAndConstraint=NULL;
+    OrConstraint *orNode=NULL;
+    RuleChain *lastChain=NULL;
+
+    UnicodeString ruleData = data.toLower();
+    while (ruleIndex< ruleData.length()) {
+        mParser->getNextToken(ruleData, &ruleIndex, token, type, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        mParser->checkSyntax(prevType, type, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+        switch (type) {
+        case tAnd:
+            curAndConstraint = curAndConstraint->add();
+            break;
+        case tOr:
+            lastChain = &rules;
+            while (lastChain->next !=NULL) {
+                lastChain = lastChain->next;
+            }
+            orNode=lastChain->ruleHeader;
+            while (orNode->next != NULL) {
+                orNode = orNode->next;
+            }
+            orNode->next= new OrConstraint();
+            orNode=orNode->next;
+            orNode->next=NULL;
+            curAndConstraint = orNode->add();
+            break;
+        case tIs:
+            curAndConstraint->rangeHigh=-1;
+            break;
+        case tNot:
+            curAndConstraint->notIn=TRUE;
+            break;
+        case tIn:
+            curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
+            curAndConstraint->integerOnly = TRUE;
+            break;
+        case tWithin:
+            curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
+            break;
+        case tNumber:
+            if ( (curAndConstraint->op==AndConstraint::MOD)&&
+                 (curAndConstraint->opNum == -1 ) ) {
+                curAndConstraint->opNum=getNumberValue(token);
+            }
+            else {
+                if (curAndConstraint->rangeLow == -1) {
+                    curAndConstraint->rangeLow=getNumberValue(token);
+                }
+                else {
+                    curAndConstraint->rangeHigh=getNumberValue(token);
+                }
+            }
+            break;
+        case tMod:
+            curAndConstraint->op=AndConstraint::MOD;
+            break;
+        case tKeyword:
+            if (ruleChain==NULL) {
+                ruleChain = &rules;
+            }
+            else {
+                while (ruleChain->next!=NULL){
+                    ruleChain=ruleChain->next;
+                }
+                ruleChain=ruleChain->next=new RuleChain();
+            }
+            orNode = ruleChain->ruleHeader = new OrConstraint();
+            curAndConstraint = orNode->add();
+            ruleChain->keyword = token;
+            break;
+        default:
+            break;
+        }
+        prevType=type;
+    }
+}
+
+int32_t
+PluralRules::getNumberValue(const UnicodeString& token) const {
+    int32_t i;
+    char digits[128];
+
+    i = token.extract(0, token.length(), digits, ARRAY_SIZE(digits), US_INV);
+    digits[i]='\0';
+
+    return((int32_t)atoi(digits));
+}
+
+
+void
+PluralRules::getNextLocale(const UnicodeString& localeData, int32_t* curIndex, UnicodeString& localeName) {
+    int32_t i=*curIndex;
+
+    localeName.remove();
+    while (i< localeData.length()) {
+       if ( (localeData.charAt(i)!= SPACE) && (localeData.charAt(i)!= COMMA) ) {
+           break;
+       }
+       i++;
+    }
+
+    while (i< localeData.length()) {
+       if ( (localeData.charAt(i)== SPACE) || (localeData.charAt(i)== COMMA) ) {
+           break;
+       }
+       localeName+=localeData.charAt(i++);
+    }
+    *curIndex=i;
+}
+
+
+int32_t
+PluralRules::getRepeatLimit() const {
+    if (mRules!=NULL) {
+        return mRules->getRepeatLimit();
+    }
+    else {
+        return 0;
+    }
+}
+
+
+void
+PluralRules::addRules(RuleChain& rules) {
+    RuleChain *newRule = new RuleChain(rules);
+    this->mRules=newRule;
+    newRule->setRepeatLimit();
+}
+
+UnicodeString
+PluralRules::getRuleFromResource(const Locale& locale, UErrorCode& errCode) {
+    UnicodeString emptyStr;
+    
+    errCode = U_ZERO_ERROR;
+    UResourceBundle *rb=ures_openDirect(NULL, "plurals", &errCode);
+    if(U_FAILURE(errCode)) {
+        /* total failure, not even root could be opened */
+        return emptyStr;
+    }
+    UResourceBundle *locRes=ures_getByKey(rb, "locales", NULL, &errCode);
+    if(U_FAILURE(errCode)) {
+        ures_close(rb);
+        return emptyStr;
+    }   
+    int32_t resLen=0;
+    const char *curLocaleName=locale.getName();
+    const UChar* s = ures_getStringByKey(locRes, curLocaleName, &resLen, &errCode);
+
+    if (s == NULL) {
+        // Check parent locales.
+        UErrorCode status = U_ZERO_ERROR;
+        char parentLocaleName[ULOC_FULLNAME_CAPACITY];
+        const char *curLocaleName=locale.getName();
+        int32_t localeNameLen=0;
+        uprv_strcpy(parentLocaleName, curLocaleName);
+        
+        while ((localeNameLen=uloc_getParent(parentLocaleName, parentLocaleName, 
+                                       ULOC_FULLNAME_CAPACITY, &status)) > 0) {
+            resLen=0;
+            s = ures_getStringByKey(locRes, parentLocaleName, &resLen, &status);
+            if (s != NULL) {
+                errCode = U_ZERO_ERROR;
+                break;
+            }
+            status = U_ZERO_ERROR;
+        }
+    }
+    if (s==NULL) {
+        ures_close(locRes);
+        ures_close(rb);
+        return emptyStr;
+    }
+    
+    char setKey[256];
+    UChar result[256];
+    u_UCharsToChars(s, setKey, resLen + 1);
+    // printf("\n PluralRule: %s\n", setKey);
+    
+
+    UResourceBundle *ruleRes=ures_getByKey(rb, "rules", NULL, &errCode);
+    if(U_FAILURE(errCode)) {
+        ures_close(locRes);
+        ures_close(rb);
+        return emptyStr;
+    }
+    resLen=0;
+    UResourceBundle *setRes = ures_getByKey(ruleRes, setKey, NULL, &errCode);
+    if (U_FAILURE(errCode)) {
+        ures_close(ruleRes);
+        ures_close(locRes);
+        ures_close(rb);
+        return emptyStr;
+    }
+
+    int32_t numberKeys = ures_getSize(setRes);
+    char *key=NULL;
+    int32_t len=0;
+    for(int32_t i=0; i<numberKeys; ++i) {
+        int32_t keyLen;
+        resLen=0;
+        s=ures_getNextString(setRes, &resLen, (const char**)&key, &errCode);
+        keyLen = uprv_strlen(key);
+        u_charsToUChars(key, result+len, keyLen);
+        len += keyLen;
+        result[len++]=COLON;
+        uprv_memcpy(result+len, s, resLen*sizeof(UChar));
+        len += resLen;
+        result[len++]=SEMI_COLON;
+    }
+    result[len++]=0;
+    u_UCharsToChars(result, setKey, len);
+    // printf(" Rule: %s\n", setKey);
+
+    ures_close(setRes);
+    ures_close(ruleRes);
+    ures_close(locRes);
+    ures_close(rb);
+    return UnicodeString(result);
+    
+}
+
+AndConstraint::AndConstraint() {
+    op = AndConstraint::NONE;
+    opNum=-1;
+    rangeLow=-1;
+    rangeHigh=-1;
+    notIn=FALSE;
+    integerOnly=FALSE;
+    next=NULL;
+}
+
+
+AndConstraint::AndConstraint(const AndConstraint& other) {
+    this->op = other.op;
+    this->opNum=other.opNum;
+    this->rangeLow=other.rangeLow;
+    this->rangeHigh=other.rangeHigh;
+    this->integerOnly=other.integerOnly;
+    this->notIn=other.notIn;
+    if (other.next==NULL) {
+        this->next=NULL;
+    }
+    else {
+        this->next = new AndConstraint(*other.next);
+    }
+}
+
+AndConstraint::~AndConstraint() {
+    if (next!=NULL) {
+        delete next;
+    }
+}
+
+
+UBool
+AndConstraint::isFulfilled(double number) {
+    UBool result=TRUE;
+    double value=number;
+    
+    if ( op == MOD ) {
+        value = (int32_t)value % opNum;
+    }
+    if ( rangeHigh == -1 ) {
+        if ( rangeLow == -1 ) {
+            result = TRUE; // empty rule
+        }
+        else {
+            if ( value == rangeLow ) {
+                result = TRUE;
+            }
+            else {
+                result = FALSE;
+            }
+        }
+    }
+    else {
+        if ((rangeLow <= value) && (value <= rangeHigh)) {
+            if (integerOnly) {
+                if ( value != (int32_t)value) {
+                    result = FALSE;
+                }
+                else {
+                    result = TRUE;
+                }
+            }
+            else {
+                result = TRUE;
+            }
+        }
+        else {
+            result = FALSE;
+        }
+    }
+    if (notIn) {
+        return !result;
+    }
+    else {
+        return result;
+    }
+}
+
+int32_t
+AndConstraint::updateRepeatLimit(int32_t maxLimit) {
+    
+    if ( op == MOD ) {
+        return uprv_max(opNum, maxLimit);
+    }
+    else {
+        if ( rangeHigh == -1 ) {
+            return(rangeLow>maxLimit? rangeLow : maxLimit);
+            return uprv_max(rangeLow, maxLimit);
+        }
+        else{
+            return uprv_max(rangeHigh, maxLimit);
+        }
+    }
+}
+
+
+AndConstraint*
+AndConstraint::add()
+{
+    this->next = new AndConstraint();
+    return this->next;
+}
+
+OrConstraint::OrConstraint() {
+    childNode=NULL;
+    next=NULL;
+}
+
+OrConstraint::OrConstraint(const OrConstraint& other) {
+    if ( other.childNode == NULL ) {
+        this->childNode = NULL;
+    }
+    else {
+        this->childNode = new AndConstraint(*(other.childNode));
+    }
+    if (other.next == NULL ) {
+        this->next = NULL;
+    }
+    else {
+        this->next = new OrConstraint(*(other.next));
+    }
+}
+
+OrConstraint::~OrConstraint() {
+    if (childNode!=NULL) {
+        delete childNode;
+    }
+    if (next!=NULL) {
+        delete next;
+    }
+}
+
+AndConstraint*
+OrConstraint::add()
+{
+    OrConstraint *curOrConstraint=this;
+    {
+        while (curOrConstraint->next!=NULL) {
+            curOrConstraint = curOrConstraint->next;
+        }
+        curOrConstraint->next = NULL;
+        curOrConstraint->childNode = new AndConstraint();
+    }
+    return curOrConstraint->childNode;
+}
+
+UBool
+OrConstraint::isFulfilled(double number) {
+    OrConstraint* orRule=this;
+    UBool result=FALSE;
+    
+    while (orRule!=NULL && !result) {
+        result=TRUE;
+        AndConstraint* andRule = orRule->childNode;
+        while (andRule!=NULL && result) {
+            result = andRule->isFulfilled(number);
+            andRule=andRule->next;
+        }
+        orRule = orRule->next;
+    }
+    
+    return result;
+}
+
+
+RuleChain::RuleChain() {
+    ruleHeader=NULL;
+    next = NULL;
+    repeatLimit=0;
+}
+
+RuleChain::RuleChain(const RuleChain& other) {
+    this->repeatLimit = other.repeatLimit;
+    this->keyword=other.keyword;
+    if (other.ruleHeader != NULL) {
+        this->ruleHeader = new OrConstraint(*(other.ruleHeader));
+    }
+    else {
+        this->ruleHeader = NULL;
+    }
+    if (other.next != NULL ) {
+        this->next = new RuleChain(*other.next);
+    }
+    else
+    {
+        this->next = NULL;
+    }
+}
+
+RuleChain::~RuleChain() {
+    if (next != NULL) {
+        delete next;
+    }
+    if ( ruleHeader != NULL ) {
+        delete ruleHeader;
+    }
+}
+
+UnicodeString
+RuleChain::select(double number) const {
+   
+   if ( ruleHeader != NULL ) {
+       if (ruleHeader->isFulfilled(number)) {
+           return keyword;
+       }
+   }
+   if ( next != NULL ) {
+       return next->select(number);
+   }
+   else {
+       return PLURAL_KEYWORD_OTHER;
+   }
+
+}
+
+void
+RuleChain::dumpRules(UnicodeString& result) {
+    UChar digitString[16];
+    
+    if ( ruleHeader != NULL ) {
+        result +=  keyword;
+        OrConstraint* orRule=ruleHeader;
+        while ( orRule != NULL ) {
+            AndConstraint* andRule=orRule->childNode;
+            while ( andRule != NULL ) {
+                if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeHigh==-1) ) {
+                    result += UNICODE_STRING_SIMPLE(" n is ");
+                    if (andRule->notIn) {
+                        result += UNICODE_STRING_SIMPLE("not ");
+                    }
+                    uprv_itou(digitString,16, andRule->rangeLow,10,0);
+                    result += UnicodeString(digitString);
+                }
+                else {
+                    if (andRule->op==AndConstraint::MOD) {
+                        result += UNICODE_STRING_SIMPLE("  n mod ");
+                        uprv_itou(digitString,16, andRule->opNum,10,0);
+                        result += UnicodeString(digitString);
+                    }
+                    else {
+                        result += UNICODE_STRING_SIMPLE("  n ");
+                    }
+                    if (andRule->rangeHigh==-1) {
+                        if (andRule->notIn) {
+                            result += UNICODE_STRING_SIMPLE(" is not ");
+                            uprv_itou(digitString,16, andRule->rangeLow,10,0);
+                            result += UnicodeString(digitString);
+                        }
+                        else {
+                            result += UNICODE_STRING_SIMPLE(" is ");
+                            uprv_itou(digitString,16, andRule->rangeLow,10,0);
+                            result += UnicodeString(digitString);
+                        }
+                    }
+                    else {
+                        if (andRule->notIn) {
+                            if ( andRule->integerOnly ) {
+                                result += UNICODE_STRING_SIMPLE("  not in ");
+                            }
+                            else {
+                                result += UNICODE_STRING_SIMPLE("  not within ");
+                            }
+                            uprv_itou(digitString,16, andRule->rangeLow,10,0);
+                            result += UnicodeString(digitString);
+                            result += UNICODE_STRING_SIMPLE(" .. ");
+                            uprv_itou(digitString,16, andRule->rangeHigh,10,0);
+                            result += UnicodeString(digitString);
+                        }
+                        else {
+                            if ( andRule->integerOnly ) {
+                                result += UNICODE_STRING_SIMPLE(" in ");
+                            }
+                            else {
+                                result += UNICODE_STRING_SIMPLE(" within ");
+                            }
+                            uprv_itou(digitString,16, andRule->rangeLow,10,0);
+                            result += UnicodeString(digitString);
+                            result += UNICODE_STRING_SIMPLE(" .. ");
+                            uprv_itou(digitString,16, andRule->rangeHigh,10,0);
+                        }
+                    }
+                }
+                if ( (andRule=andRule->next) != NULL) {
+                    result += PK_AND;
+                }
+            }
+            if ( (orRule = orRule->next) != NULL ) {
+                result += PK_OR;
+            }
+        }
+    }
+    if ( next != NULL ) {
+        next->dumpRules(result);
+    }
+}
+
+int32_t
+RuleChain::getRepeatLimit () {
+    return repeatLimit;
+}
+
+void
+RuleChain::setRepeatLimit () {
+    int32_t limit=0;
+
+    if ( next != NULL ) {
+        next->setRepeatLimit();
+        limit = next->repeatLimit;
+    }
+
+    if ( ruleHeader != NULL ) {
+        OrConstraint* orRule=ruleHeader;
+        while ( orRule != NULL ) {
+            AndConstraint* andRule=orRule->childNode;
+            while ( andRule != NULL ) {
+                limit = andRule->updateRepeatLimit(limit);
+                andRule = andRule->next;
+            }
+            orRule = orRule->next;
+        }
+    }
+    repeatLimit = limit;
+}
+
+UErrorCode
+RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const {
+    if ( arraySize < capacityOfKeywords-1 ) {
+        keywords[arraySize++]=keyword;
+    }
+    else {
+        return U_BUFFER_OVERFLOW_ERROR;
+    }
+
+    if ( next != NULL ) {
+        return next->getKeywords(capacityOfKeywords, keywords, arraySize);
+    }
+    else {
+        return U_ZERO_ERROR;
+    }
+}
+
+UBool
+RuleChain::isKeyword(const UnicodeString& keywordParam) const {
+    if ( keyword == keywordParam ) {
+        return TRUE;
+    }
+
+    if ( next != NULL ) {
+        return next->isKeyword(keywordParam);
+    }
+    else {
+        return FALSE;
+    }
+}
+
+
+RuleParser::RuleParser() {
+    UErrorCode err=U_ZERO_ERROR;
+    const UnicodeString idStart=UNICODE_STRING_SIMPLE("[[a-z]]");
+    const UnicodeString idContinue=UNICODE_STRING_SIMPLE("[[a-z][A-Z][_][0-9]]");
+    idStartFilter = new UnicodeSet(idStart, err);
+    idContinueFilter = new UnicodeSet(idContinue, err);
+}
+
+RuleParser::~RuleParser() {
+    delete idStartFilter;
+    delete idContinueFilter;
+}
+
+void
+RuleParser::checkSyntax(tokenType prevType, tokenType curType, UErrorCode &status)
+{
+    if (U_FAILURE(status)) {
+        return;
+    }
+    switch(prevType) {
+    case none:
+    case tSemiColon:
+        if (curType!=tKeyword) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tVariableN :
+        if (curType != tIs && curType != tMod && curType != tIn && 
+            curType != tNot && curType != tWithin) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tZero:
+    case tOne:
+    case tTwo:
+    case tFew:
+    case tMany:
+    case tOther:
+    case tKeyword:
+        if (curType != tColon) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tColon :
+        if (curType != tVariableN) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tIs:
+        if ( curType != tNumber && curType != tNot) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tNot:
+        if (curType != tNumber && curType != tIn && curType != tWithin) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tMod:
+    case tDot:
+    case tIn:
+    case tWithin:
+    case tAnd:
+    case tOr:
+        if (curType != tNumber && curType != tVariableN) {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    case tNumber:
+        if (curType != tDot && curType != tSemiColon && curType != tIs && curType != tNot &&
+            curType != tIn && curType != tWithin && curType != tAnd && curType != tOr)
+        {
+            status = U_UNEXPECTED_TOKEN;
+        }
+        break;
+    default:
+        status = U_UNEXPECTED_TOKEN;
+        break;
+    }
+}
+
+void
+RuleParser::getNextToken(const UnicodeString& ruleData,
+                         int32_t *ruleIndex,
+                         UnicodeString& token,
+                         tokenType& type,
+                         UErrorCode &status)
+{
+    int32_t curIndex= *ruleIndex;
+    UChar ch;
+    tokenType prevType=none;
+
+    while (curIndex<ruleData.length()) {
+        ch = ruleData.charAt(curIndex);
+        if ( !inRange(ch, type) ) {
+            status = U_ILLEGAL_CHARACTER;
+            return;
+        }
+        switch (type) {
+        case tSpace:
+            if ( *ruleIndex != curIndex ) { // letter
+                token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
+                *ruleIndex=curIndex;
+                type=prevType;
+                getKeyType(token, type, status);
+                return;
+            }
+            else {
+                *ruleIndex=*ruleIndex+1;
+            }
+            break; // consective space
+        case tColon:
+        case tSemiColon:
+            if ( *ruleIndex != curIndex ) {
+                token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
+                *ruleIndex=curIndex;
+                type=prevType;
+                getKeyType(token, type, status);
+                return;
+            }
+            else {
+                *ruleIndex=curIndex+1;
+                return;
+            }
+        case tLetter:
+             if ((type==prevType)||(prevType==none)) {
+                prevType=type;
+                break;
+             }
+             break;
+        case tNumber:
+             if ((type==prevType)||(prevType==none)) {
+                prevType=type;
+                break;
+             }
+             else {
+                *ruleIndex=curIndex+1;
+                return;
+             }
+         case tDot:
+             if (prevType==none) {  // first dot
+                prevType=type;
+                continue;
+             }
+             else {
+                 if ( *ruleIndex != curIndex ) {
+                    token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
+                    *ruleIndex=curIndex;  // letter
+                    type=prevType;
+                    getKeyType(token, type, status);
+                    return;
+                 }
+                 else {  // two consective dots
+                    *ruleIndex=curIndex+2;
+                    return;
+                 }
+             }
+             break;
+         default:
+             status = U_UNEXPECTED_TOKEN;
+             return;
+        }
+        curIndex++;
+    }
+    if ( curIndex>=ruleData.length() ) {
+        if ( (type == tLetter)||(type == tNumber) ) {
+            token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
+            getKeyType(token, type, status);
+        }
+        *ruleIndex = ruleData.length();
+    }
+}
+
+UBool
+RuleParser::inRange(UChar ch, tokenType& type) {
+    if ((ch>=CAP_A) && (ch<=CAP_Z)) {
+        // we assume all characters are in lower case already.
+        return FALSE;
+    }
+    if ((ch>=LOW_A) && (ch<=LOW_Z)) {
+        type = tLetter;
+        return TRUE;
+    }
+    if ((ch>=U_ZERO) && (ch<=U_NINE)) {
+        type = tNumber;
+        return TRUE;
+    }
+    switch (ch) {
+    case COLON:
+        type = tColon;
+        return TRUE;
+    case SPACE:
+        type = tSpace;
+        return TRUE;
+    case SEMI_COLON:
+        type = tSemiColon;
+        return TRUE;
+    case DOT:
+        type = tDot;
+        return TRUE;
+    default :
+        type = none;
+        return FALSE;
+    }
+}
+
+
+void
+RuleParser::getKeyType(const UnicodeString& token, tokenType& keyType, UErrorCode &status)
+{
+    if ( keyType==tNumber) {
+    }
+    else if (token==PK_VAR_N) {
+        keyType = tVariableN;
+    }
+    else if (token==PK_IS) {
+        keyType = tIs;
+    }
+    else if (token==PK_AND) {
+        keyType = tAnd;
+    }
+    else if (token==PK_IN) {
+        keyType = tIn;
+    }
+    else if (token==PK_WITHIN) {
+        keyType = tWithin;
+    }
+    else if (token==PK_NOT) {
+        keyType = tNot;
+    }
+    else if (token==PK_MOD) {
+        keyType = tMod;
+    }
+    else if (token==PK_OR) {
+        keyType = tOr;
+    }
+    else if ( isValidKeyword(token) ) {
+        keyType = tKeyword;
+    }
+    else {
+        status = U_UNEXPECTED_TOKEN;
+    }
+}
+
+UBool
+RuleParser::isValidKeyword(const UnicodeString& token) {
+    if ( token.length()==0 ) {
+        return FALSE;
+    }
+    if ( idStartFilter->contains(token.charAt(0) )==TRUE ) {
+        int32_t i;
+        for (i=1; i< token.length(); i++) {
+            if (idContinueFilter->contains(token.charAt(i))== FALSE) {
+                return FALSE;
+            }
+        }
+        return TRUE;
+    }
+    else {
+        return FALSE;
+    }
+}
+
+PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) :
+fKeywordNames(status)
+{
+    RuleChain *node=header;
+    UBool  addKeywordOther=true;
+    
+    pos=0;
+    fKeywordNames.removeAllElements();
+    while(node!=NULL) {
+        fKeywordNames.addElement(new UnicodeString(node->keyword), status);
+        if (node->keyword == PLURAL_KEYWORD_OTHER) {
+            addKeywordOther= false;
+        }
+        node=node->next;
+    }
+    
+    if (addKeywordOther) {
+        fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
+    }
+}
+
+const UnicodeString*
+PluralKeywordEnumeration::snext(UErrorCode& status) {
+    if (U_SUCCESS(status) && pos < fKeywordNames.size()) {
+        return (const UnicodeString*)fKeywordNames.elementAt(pos++);
+    }
+    return NULL;
+}
+
+void
+PluralKeywordEnumeration::reset(UErrorCode& /*status*/) {
+    pos=0;
+}
+
+int32_t
+PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
+       return fKeywordNames.size();
+}
+
+PluralKeywordEnumeration::~PluralKeywordEnumeration() {
+    UnicodeString *s;
+    for (int32_t i=0; i<fKeywordNames.size(); ++i) {
+        if ((s=(UnicodeString *)fKeywordNames.elementAt(i))!=NULL) {
+            delete s;
+        }
+    }
+}
+
+U_NAMESPACE_END
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+//eof