| 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
|
|
|
|
|