Index: icu46/source/i18n/plurrule.cpp |
=================================================================== |
--- icu46/source/i18n/plurrule.cpp (revision 0) |
+++ icu46/source/i18n/plurrule.cpp (revision 0) |
@@ -0,0 +1,1212 @@ |
+/* |
+******************************************************************************* |
+* Copyright (C) 2007-2010, 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) |
+{ |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ 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; |
+ |
+ if (U_FAILURE(status)) { |
+ return NULL; |
+ } |
+ 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; |
+ if (U_FAILURE(status)) { |
+ return NULL; |
+ } |
+ PluralRules *newObj = new PluralRules(status); |
+ if (newObj==NULL || U_FAILURE(status)) { |
+ 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); |
+ if (U_FAILURE(status)) return NULL; |
+ |
+ 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); |
+ if (U_FAILURE(status)) { |
+ return FALSE; |
+ } |
+ StringEnumeration* otherKeywordList =other.getKeywords(status); |
+ if (U_FAILURE(status)) { |
+ return FALSE; |
+ } |
+ |
+ if (myKeywordList->count(status)!=otherKeywordList->count(status) || |
+ U_FAILURE(status)) { |
+ sameList = FALSE; |
+ } |
+ else { |
+ myKeywordList->reset(status); |
+ if (U_FAILURE(status)) { |
+ return FALSE; |
+ } |
+ while (sameList && (ptrKeyword=myKeywordList->snext(status))!=NULL) { |
+ if (U_FAILURE(status) || !other.isKeyword(*ptrKeyword)) { |
+ sameList = FALSE; |
+ } |
+ } |
+ otherKeywordList->reset(status); |
+ if (U_FAILURE(status)) { |
+ return FALSE; |
+ } |
+ while (sameList && (ptrKeyword=otherKeywordList->snext(status))!=NULL) { |
+ if (U_FAILURE(status)) { |
+ return FALSE; |
+ } |
+ 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; |
+ |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ 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; |
+ |
+ if (U_FAILURE(errCode)) { |
+ return emptyStr; |
+ } |
+ 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 = (int32_t)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 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; |
+ |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ 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); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ } |
+ *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 (U_FAILURE(status)) { |
+ return; |
+ } |
+ 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; |
+ |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ pos=0; |
+ fKeywordNames.removeAllElements(); |
+ while(node!=NULL) { |
+ fKeywordNames.addElement(new UnicodeString(node->keyword), status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ if (node->keyword == PLURAL_KEYWORD_OTHER) { |
+ addKeywordOther= false; |
+ } |
+ node=node->next; |
+ } |
+ |
+ if (addKeywordOther) { |
+ fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ } |
+} |
+ |
+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 |
Property changes on: icu46/source/i18n/plurrule.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |