2007-12-17 01:39:55 +00:00
|
|
|
/*
|
|
|
|
*******************************************************************************
|
2011-03-17 21:36:03 +00:00
|
|
|
* Copyright (C) 2007-2011, International Business Machines Corporation and
|
2007-12-17 01:39:55 +00:00
|
|
|
* others. All Rights Reserved.
|
|
|
|
*******************************************************************************
|
|
|
|
*
|
|
|
|
* File PLURRULE.CPP
|
|
|
|
*
|
|
|
|
* Modification History:
|
|
|
|
*
|
|
|
|
* Date Name Description
|
|
|
|
*******************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "unicode/utypes.h"
|
2011-04-28 13:35:56 +00:00
|
|
|
#include "unicode/localpointer.h"
|
2007-12-17 01:39:55 +00:00
|
|
|
#include "unicode/plurrule.h"
|
2011-04-28 13:35:56 +00:00
|
|
|
#include "unicode/ures.h"
|
2007-12-17 01:39:55 +00:00
|
|
|
#include "cmemory.h"
|
|
|
|
#include "cstring.h"
|
|
|
|
#include "hash.h"
|
2008-01-25 18:54:43 +00:00
|
|
|
#include "mutex.h"
|
2011-04-25 20:47:32 +00:00
|
|
|
#include "patternprops.h"
|
2007-12-17 01:39:55 +00:00
|
|
|
#include "plurrule_impl.h"
|
|
|
|
#include "putilimp.h"
|
2008-01-25 18:54:43 +00:00
|
|
|
#include "ucln_in.h"
|
2011-04-28 13:35:56 +00:00
|
|
|
#include "uhash.h"
|
2007-12-17 01:39:55 +00:00
|
|
|
#include "ustrfmt.h"
|
2008-03-10 20:09:55 +00:00
|
|
|
#include "locutil.h"
|
2007-12-17 01:39:55 +00:00
|
|
|
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
|
2008-04-23 16:14:27 +00:00
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
// shared by all instances when lazy-initializing samples
|
|
|
|
static UMTX pluralMutex;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2008-03-26 18:45:50 +00:00
|
|
|
#define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0])
|
|
|
|
|
2007-12-19 22:39:09 +00:00
|
|
|
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};
|
2008-06-19 20:23:49 +00:00
|
|
|
static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0};
|
2007-12-19 22:39:09 +00:00
|
|
|
|
2007-12-29 05:51:50 +00:00
|
|
|
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules)
|
2007-12-17 01:39:55 +00:00
|
|
|
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
|
|
|
|
|
2008-01-21 21:51:09 +00:00
|
|
|
PluralRules::PluralRules(UErrorCode& status)
|
2008-06-20 23:47:59 +00:00
|
|
|
: UObject(),
|
2011-03-17 21:36:03 +00:00
|
|
|
mRules(NULL),
|
|
|
|
mParser(NULL),
|
|
|
|
mSamples(NULL),
|
|
|
|
mSampleInfo(NULL),
|
|
|
|
mSampleInfoCount(0)
|
2008-01-21 21:51:09 +00:00
|
|
|
{
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mParser = new RuleParser();
|
2008-06-20 23:47:59 +00:00
|
|
|
if (mParser==NULL) {
|
|
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
2008-01-20 02:30:30 +00:00
|
|
|
PluralRules::PluralRules(const PluralRules& other)
|
2008-01-21 21:51:09 +00:00
|
|
|
: UObject(other),
|
|
|
|
mRules(NULL),
|
2011-03-17 21:36:03 +00:00
|
|
|
mParser(NULL),
|
|
|
|
mSamples(NULL),
|
|
|
|
mSampleInfo(NULL),
|
|
|
|
mSampleInfoCount(0)
|
2007-12-29 05:51:50 +00:00
|
|
|
{
|
2007-12-17 01:39:55 +00:00
|
|
|
*this=other;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluralRules::~PluralRules() {
|
2008-01-21 21:51:09 +00:00
|
|
|
delete mRules;
|
|
|
|
delete mParser;
|
2011-04-28 13:35:56 +00:00
|
|
|
uprv_free(mSamples);
|
|
|
|
uprv_free(mSampleInfo);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PluralRules*
|
|
|
|
PluralRules::clone() const {
|
|
|
|
return new PluralRules(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
PluralRules&
|
2007-12-29 05:51:50 +00:00
|
|
|
PluralRules::operator=(const PluralRules& other) {
|
2008-01-21 22:38:52 +00:00
|
|
|
if (this != &other) {
|
2008-01-21 21:51:09 +00:00
|
|
|
delete mRules;
|
2008-04-17 01:33:23 +00:00
|
|
|
if (other.mRules==NULL) {
|
|
|
|
mRules = NULL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mRules = new RuleChain(*other.mRules);
|
|
|
|
}
|
2008-01-21 21:51:09 +00:00
|
|
|
delete mParser;
|
|
|
|
mParser = new RuleParser();
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
uprv_free(mSamples);
|
2011-03-17 21:36:03 +00:00
|
|
|
mSamples = NULL;
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
uprv_free(mSampleInfo);
|
2011-03-17 21:36:03 +00:00
|
|
|
mSampleInfo = NULL;
|
|
|
|
mSampleInfoCount = 0;
|
2008-01-21 21:51:09 +00:00
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluralRules* U_EXPORT2
|
|
|
|
PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
|
|
|
|
RuleChain rules;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
PluralRules *newRules = new PluralRules(status);
|
|
|
|
if ( (newRules != NULL)&& U_SUCCESS(status) ) {
|
2007-12-29 05:51:50 +00:00
|
|
|
newRules->parseDescription((UnicodeString &)description, rules, status);
|
2007-12-17 01:39:55 +00:00
|
|
|
if (U_SUCCESS(status)) {
|
2008-06-19 20:23:49 +00:00
|
|
|
newRules->addRules(rules);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
2008-06-19 20:23:49 +00:00
|
|
|
RuleChain rChain;
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
PluralRules *newObj = new PluralRules(status);
|
2009-02-05 01:03:16 +00:00
|
|
|
if (newObj==NULL || U_FAILURE(status)) {
|
2011-03-17 21:36:03 +00:00
|
|
|
delete newObj;
|
2007-12-29 05:51:50 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
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);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
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);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2008-06-19 20:23:49 +00:00
|
|
|
return newObj;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString
|
|
|
|
PluralRules::select(int32_t number) const {
|
2008-01-21 21:51:09 +00:00
|
|
|
if (mRules == NULL) {
|
2007-12-17 01:39:55 +00:00
|
|
|
return PLURAL_DEFAULT_RULE;
|
|
|
|
}
|
|
|
|
else {
|
2008-01-21 21:51:09 +00:00
|
|
|
return mRules->select(number);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-26 17:51:59 +00:00
|
|
|
UnicodeString
|
|
|
|
PluralRules::select(double number) const {
|
|
|
|
if (mRules == NULL) {
|
|
|
|
return PLURAL_DEFAULT_RULE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return mRules->select(number);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
StringEnumeration*
|
|
|
|
PluralRules::getKeywords(UErrorCode& status) const {
|
|
|
|
if (U_FAILURE(status)) return NULL;
|
2008-04-17 01:33:23 +00:00
|
|
|
StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status);
|
2011-03-17 21:36:03 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
delete nameEnumerator;
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-02-05 01:03:16 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
return nameEnumerator;
|
|
|
|
}
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
double
|
|
|
|
PluralRules::getUniqueKeywordValue(const UnicodeString& keyword) {
|
|
|
|
double val;
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
|
int32_t count = getSamplesInternal(keyword, &val, 1, FALSE, status);
|
2011-04-27 20:04:06 +00:00
|
|
|
return count == 1 ? val : UPLRULES_NO_UNIQUE_VALUE;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
PluralRules::getAllKeywordValues(const UnicodeString &keyword, double *dest,
|
|
|
|
int32_t destCapacity, UErrorCode& error) {
|
|
|
|
return getSamplesInternal(keyword, dest, destCapacity, FALSE, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
PluralRules::getSamples(const UnicodeString &keyword, double *dest,
|
|
|
|
int32_t destCapacity, UErrorCode& status) {
|
|
|
|
return getSamplesInternal(keyword, dest, destCapacity, TRUE, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t
|
|
|
|
PluralRules::getSamplesInternal(const UnicodeString &keyword, double *dest,
|
|
|
|
int32_t destCapacity, UBool includeUnlimited,
|
|
|
|
UErrorCode& status) {
|
|
|
|
initSamples(status);
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
if (destCapacity < 0 || (dest == NULL && destCapacity > 0)) {
|
|
|
|
status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
|
|
return -1;
|
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
|
|
|
int32_t index = getKeywordIndex(keyword, status);
|
|
|
|
if (index == -1) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int32_t LIMIT_MASK = 0x1 << 31;
|
|
|
|
|
|
|
|
if (!includeUnlimited) {
|
|
|
|
if ((mSampleInfo[index] & LIMIT_MASK) == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t start = index == 0 ? 0 : mSampleInfo[index - 1] & ~LIMIT_MASK;
|
|
|
|
int32_t limit = mSampleInfo[index] & ~LIMIT_MASK;
|
|
|
|
int32_t len = limit - start;
|
2011-04-28 13:35:56 +00:00
|
|
|
if (len <= destCapacity) {
|
|
|
|
destCapacity = len;
|
|
|
|
} else if (includeUnlimited) {
|
|
|
|
len = destCapacity; // no overflow, and don't report more than we copy
|
|
|
|
} else {
|
|
|
|
status = U_BUFFER_OVERFLOW_ERROR;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
for (int32_t i = 0; i < destCapacity; ++i, ++start) {
|
|
|
|
dest[i] = mSamples[start];
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
|
|
|
|
UBool
|
|
|
|
PluralRules::isKeyword(const UnicodeString& keyword) const {
|
2008-04-17 01:33:23 +00:00
|
|
|
if ( keyword == PLURAL_KEYWORD_OTHER ) {
|
|
|
|
return true;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-04-17 01:33:23 +00:00
|
|
|
if (mRules==NULL) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return mRules->isKeyword(keyword);
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString
|
|
|
|
PluralRules::getKeywordOther() const {
|
|
|
|
return PLURAL_KEYWORD_OTHER;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
PluralRules::operator==(const PluralRules& other) const {
|
|
|
|
int32_t limit;
|
|
|
|
const UnicodeString *ptrKeyword;
|
|
|
|
UErrorCode status= U_ZERO_ERROR;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( this == &other ) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
LocalPointer<StringEnumeration> myKeywordList(getKeywords(status));
|
|
|
|
LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status));
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
if (myKeywordList->count(status)!=otherKeywordList->count(status)) {
|
|
|
|
return FALSE;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
myKeywordList->reset(status);
|
|
|
|
while ((ptrKeyword=myKeywordList->snext(status))!=NULL) {
|
|
|
|
if (!other.isKeyword(*ptrKeyword)) {
|
2009-02-05 01:03:16 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
}
|
|
|
|
otherKeywordList->reset(status);
|
|
|
|
while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) {
|
|
|
|
if (!this->isKeyword(*ptrKeyword)) {
|
2007-12-17 01:39:55 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-12-29 05:51:50 +00:00
|
|
|
void
|
|
|
|
PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode &status)
|
|
|
|
{
|
2007-12-17 01:39:55 +00:00
|
|
|
int32_t ruleIndex=0;
|
|
|
|
UnicodeString token;
|
|
|
|
tokenType type;
|
|
|
|
tokenType prevType=none;
|
|
|
|
RuleChain *ruleChain=NULL;
|
|
|
|
AndConstraint *curAndConstraint=NULL;
|
|
|
|
OrConstraint *orNode=NULL;
|
2008-06-19 20:23:49 +00:00
|
|
|
RuleChain *lastChain=NULL;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
UnicodeString ruleData = data.toLower();
|
|
|
|
while (ruleIndex< ruleData.length()) {
|
2008-01-21 21:51:09 +00:00
|
|
|
mParser->getNextToken(ruleData, &ruleIndex, token, type, status);
|
2007-12-29 05:51:50 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2008-01-21 21:51:09 +00:00
|
|
|
mParser->checkSyntax(prevType, type, status);
|
2007-12-29 05:51:50 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
switch (type) {
|
2007-12-29 05:51:50 +00:00
|
|
|
case tAnd:
|
|
|
|
curAndConstraint = curAndConstraint->add();
|
|
|
|
break;
|
|
|
|
case tOr:
|
2008-06-19 20:23:49 +00:00
|
|
|
lastChain = &rules;
|
|
|
|
while (lastChain->next !=NULL) {
|
|
|
|
lastChain = lastChain->next;
|
|
|
|
}
|
|
|
|
orNode=lastChain->ruleHeader;
|
2007-12-29 05:51:50 +00:00
|
|
|
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:
|
2008-06-26 17:51:59 +00:00
|
|
|
curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
|
|
|
|
curAndConstraint->integerOnly = TRUE;
|
|
|
|
break;
|
|
|
|
case tWithin:
|
2007-12-29 05:51:50 +00:00
|
|
|
curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
|
|
|
|
break;
|
|
|
|
case tNumber:
|
2008-01-20 02:30:30 +00:00
|
|
|
if ( (curAndConstraint->op==AndConstraint::MOD)&&
|
2007-12-29 05:51:50 +00:00
|
|
|
(curAndConstraint->opNum == -1 ) ) {
|
|
|
|
curAndConstraint->opNum=getNumberValue(token);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (curAndConstraint->rangeLow == -1) {
|
|
|
|
curAndConstraint->rangeLow=getNumberValue(token);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
else {
|
2007-12-29 05:51:50 +00:00
|
|
|
curAndConstraint->rangeHigh=getNumberValue(token);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case tMod:
|
|
|
|
curAndConstraint->op=AndConstraint::MOD;
|
|
|
|
break;
|
|
|
|
case tKeyword:
|
|
|
|
if (ruleChain==NULL) {
|
|
|
|
ruleChain = &rules;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
while (ruleChain->next!=NULL){
|
|
|
|
ruleChain=ruleChain->next;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
ruleChain=ruleChain->next=new RuleChain();
|
|
|
|
}
|
|
|
|
orNode = ruleChain->ruleHeader = new OrConstraint();
|
|
|
|
curAndConstraint = orNode->add();
|
|
|
|
ruleChain->keyword = token;
|
|
|
|
break;
|
2008-01-20 02:30:30 +00:00
|
|
|
default:
|
|
|
|
break;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
prevType=type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-20 02:30:30 +00:00
|
|
|
int32_t
|
2007-12-17 01:39:55 +00:00
|
|
|
PluralRules::getNumberValue(const UnicodeString& token) const {
|
|
|
|
int32_t i;
|
|
|
|
char digits[128];
|
|
|
|
|
2008-03-26 18:45:50 +00:00
|
|
|
i = token.extract(0, token.length(), digits, ARRAY_SIZE(digits), US_INV);
|
2007-12-17 01:39:55 +00:00
|
|
|
digits[i]='\0';
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
return((int32_t)atoi(digits));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PluralRules::getNextLocale(const UnicodeString& localeData, int32_t* curIndex, UnicodeString& localeName) {
|
|
|
|
int32_t i=*curIndex;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
localeName.remove();
|
|
|
|
while (i< localeData.length()) {
|
|
|
|
if ( (localeData.charAt(i)!= SPACE) && (localeData.charAt(i)!= COMMA) ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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 {
|
2008-04-17 01:33:23 +00:00
|
|
|
if (mRules!=NULL) {
|
|
|
|
return mRules->getRepeatLimit();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
int32_t
|
|
|
|
PluralRules::getKeywordIndex(const UnicodeString& keyword,
|
|
|
|
UErrorCode& status) const {
|
|
|
|
if (U_SUCCESS(status)) {
|
|
|
|
int32_t n = 0;
|
|
|
|
RuleChain* rc = mRules;
|
|
|
|
while (rc != NULL) {
|
|
|
|
if (rc->ruleHeader != NULL) {
|
|
|
|
if (rc->keyword == keyword) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
rc = rc->next;
|
|
|
|
}
|
|
|
|
if (keyword == PLURAL_KEYWORD_OTHER) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
typedef struct SampleRecord {
|
|
|
|
int32_t ruleIndex;
|
|
|
|
double value;
|
|
|
|
} SampleRecord;
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
void
|
|
|
|
PluralRules::initSamples(UErrorCode& status) {
|
2011-04-28 13:35:56 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
Mutex lock(&pluralMutex);
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
if (mSamples) {
|
2011-03-17 21:36:03 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note, the original design let you have multiple rules with the same keyword. But
|
|
|
|
// we don't use that in our data and existing functions in this implementation don't
|
|
|
|
// fully support it (for example, the returned keywords is a list and not a set).
|
|
|
|
//
|
|
|
|
// So I don't support this here either. If you ask for samples, or for all values,
|
|
|
|
// you will get information about the first rule with that keyword, not all rules with
|
|
|
|
// that keyword.
|
|
|
|
|
|
|
|
int32_t maxIndex = 0;
|
|
|
|
int32_t otherIndex = -1; // the value -1 will indicate we added 'other' at end
|
|
|
|
RuleChain* rc = mRules;
|
|
|
|
while (rc != NULL) {
|
|
|
|
if (rc->ruleHeader != NULL) {
|
|
|
|
if (otherIndex == -1 && rc->keyword == PLURAL_KEYWORD_OTHER) {
|
|
|
|
otherIndex = maxIndex;
|
|
|
|
}
|
|
|
|
++maxIndex;
|
|
|
|
}
|
|
|
|
rc = rc->next;
|
|
|
|
}
|
|
|
|
if (otherIndex == -1) {
|
|
|
|
++maxIndex;
|
|
|
|
}
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
LocalMemory<int32_t> newSampleInfo;
|
|
|
|
if (NULL == newSampleInfo.allocateInsteadAndCopy(maxIndex)) {
|
2011-03-17 21:36:03 +00:00
|
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
2011-04-28 13:35:56 +00:00
|
|
|
return;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const int32_t LIMIT_MASK = 0x1 << 31;
|
|
|
|
|
|
|
|
rc = mRules;
|
|
|
|
int32_t n = 0;
|
|
|
|
while (rc != NULL) {
|
|
|
|
if (rc->ruleHeader != NULL) {
|
|
|
|
newSampleInfo[n++] = rc->ruleHeader->isLimited() ? LIMIT_MASK : 0;
|
|
|
|
}
|
|
|
|
rc = rc->next;
|
|
|
|
}
|
|
|
|
if (otherIndex == -1) {
|
|
|
|
newSampleInfo[maxIndex - 1] = 0; // unlimited
|
|
|
|
}
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
MaybeStackArray<SampleRecord, 10> newSamples;
|
2011-03-17 21:36:03 +00:00
|
|
|
int32_t sampleCount = 0;
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
int32_t limit = getRepeatLimit() * MAX_SAMPLES * 2;
|
|
|
|
if (limit < 10) {
|
|
|
|
limit = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0, keywordsRemaining = maxIndex;
|
|
|
|
keywordsRemaining > 0 && i < limit;
|
|
|
|
++i) {
|
|
|
|
double val = i / 2.0;
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
n = 0;
|
|
|
|
rc = mRules;
|
|
|
|
int32_t found = -1;
|
|
|
|
while (rc != NULL) {
|
|
|
|
if (rc->ruleHeader != NULL) {
|
|
|
|
if (rc->ruleHeader->isFulfilled(val)) {
|
|
|
|
found = n;
|
2011-03-17 21:36:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
++n;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
rc = rc->next;
|
|
|
|
}
|
|
|
|
if (found == -1) {
|
|
|
|
// 'other'. If there is an 'other' rule, the rule set is bad since nothing
|
|
|
|
// should leak through, but we don't bother to report that here.
|
|
|
|
found = otherIndex == -1 ? maxIndex - 1 : otherIndex;
|
|
|
|
}
|
|
|
|
if (newSampleInfo[found] == MAX_SAMPLES) { // limit flag not set
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
newSampleInfo[found] += 1; // won't impact limit flag
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
if (sampleCount == newSamples.getCapacity()) {
|
|
|
|
int32_t newCapacity = sampleCount < 20 ? 128 : sampleCount * 2;
|
|
|
|
if (NULL == newSamples.resize(newCapacity, sampleCount)) {
|
|
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
return;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
newSamples[sampleCount].ruleIndex = found;
|
|
|
|
newSamples[sampleCount].value = val;
|
|
|
|
++sampleCount;
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
if (newSampleInfo[found] == MAX_SAMPLES) { // limit flag not set
|
|
|
|
--keywordsRemaining;
|
|
|
|
}
|
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
// sort the values by index, leaving order otherwise unchanged
|
|
|
|
// this is just a selection sort for simplicity
|
|
|
|
LocalMemory<double> values;
|
|
|
|
if (NULL == values.allocateInsteadAndCopy(sampleCount)) {
|
|
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 0, j = 0; i < maxIndex; ++i) {
|
|
|
|
for (int k = 0; k < sampleCount; ++k) {
|
|
|
|
if (newSamples[k].ruleIndex == i) {
|
|
|
|
values[j++] = newSamples[k].value;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
// convert array of mask/lengths to array of mask/limits
|
|
|
|
limit = 0;
|
|
|
|
for (int i = 0; i < maxIndex; ++i) {
|
|
|
|
int32_t info = newSampleInfo[i];
|
|
|
|
int32_t len = info & ~LIMIT_MASK;
|
|
|
|
limit += len;
|
|
|
|
// if a rule is 'unlimited' but has fewer than MAX_SAMPLES samples,
|
|
|
|
// it's not really unlimited, so mark it as limited
|
|
|
|
int32_t mask = len < MAX_SAMPLES ? LIMIT_MASK : info & LIMIT_MASK;
|
|
|
|
newSampleInfo[i] = limit | mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ok, we've got good data
|
|
|
|
mSamples = values.orphan();
|
|
|
|
mSampleInfo = newSampleInfo.orphan();
|
|
|
|
mSampleInfoCount = maxIndex;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
|
2008-01-20 02:30:30 +00:00
|
|
|
void
|
2008-06-19 20:23:49 +00:00
|
|
|
PluralRules::addRules(RuleChain& rules) {
|
|
|
|
RuleChain *newRule = new RuleChain(rules);
|
|
|
|
this->mRules=newRule;
|
|
|
|
newRule->setRepeatLimit();
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
2008-06-19 20:23:49 +00:00
|
|
|
UnicodeString
|
|
|
|
PluralRules::getRuleFromResource(const Locale& locale, UErrorCode& errCode) {
|
|
|
|
UnicodeString emptyStr;
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(errCode)) {
|
|
|
|
return emptyStr;
|
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
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;
|
2011-03-17 21:36:03 +00:00
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
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);
|
2011-03-17 21:36:03 +00:00
|
|
|
|
|
|
|
while ((localeNameLen=uloc_getParent(parentLocaleName, parentLocaleName,
|
2008-06-19 20:23:49 +00:00
|
|
|
ULOC_FULLNAME_CAPACITY, &status)) > 0) {
|
|
|
|
resLen=0;
|
|
|
|
s = ures_getStringByKey(locRes, parentLocaleName, &resLen, &status);
|
|
|
|
if (s != NULL) {
|
|
|
|
errCode = U_ZERO_ERROR;
|
|
|
|
break;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
status = U_ZERO_ERROR;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
2008-06-19 20:23:49 +00:00
|
|
|
if (s==NULL) {
|
|
|
|
ures_close(locRes);
|
|
|
|
ures_close(rb);
|
|
|
|
return emptyStr;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2008-06-19 20:23:49 +00:00
|
|
|
char setKey[256];
|
|
|
|
UChar result[256];
|
|
|
|
u_UCharsToChars(s, setKey, resLen + 1);
|
|
|
|
// printf("\n PluralRule: %s\n", setKey);
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2008-06-19 20:23:49 +00:00
|
|
|
|
|
|
|
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);
|
2009-11-11 15:47:22 +00:00
|
|
|
keyLen = (int32_t)uprv_strlen(key);
|
2008-06-19 20:23:49 +00:00
|
|
|
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);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AndConstraint::AndConstraint() {
|
|
|
|
op = AndConstraint::NONE;
|
|
|
|
opNum=-1;
|
|
|
|
rangeLow=-1;
|
|
|
|
rangeHigh=-1;
|
|
|
|
notIn=FALSE;
|
2008-06-26 17:51:59 +00:00
|
|
|
integerOnly=FALSE;
|
2007-12-17 01:39:55 +00:00
|
|
|
next=NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AndConstraint::AndConstraint(const AndConstraint& other) {
|
|
|
|
this->op = other.op;
|
|
|
|
this->opNum=other.opNum;
|
|
|
|
this->rangeLow=other.rangeLow;
|
|
|
|
this->rangeHigh=other.rangeHigh;
|
2008-06-26 17:51:59 +00:00
|
|
|
this->integerOnly=other.integerOnly;
|
2007-12-17 01:39:55 +00:00
|
|
|
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;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
UBool
|
2008-06-26 17:51:59 +00:00
|
|
|
AndConstraint::isFulfilled(double number) {
|
2007-12-17 01:39:55 +00:00
|
|
|
UBool result=TRUE;
|
2008-06-26 17:51:59 +00:00
|
|
|
double value=number;
|
2011-03-17 21:36:03 +00:00
|
|
|
|
|
|
|
// arrrrrrgh
|
|
|
|
if ((rangeHigh == -1 || integerOnly) && number != uprv_floor(number)) {
|
|
|
|
return notIn;
|
|
|
|
}
|
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( op == MOD ) {
|
2008-06-26 17:51:59 +00:00
|
|
|
value = (int32_t)value % opNum;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
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)) {
|
2008-06-26 17:51:59 +00:00
|
|
|
if (integerOnly) {
|
|
|
|
if ( value != (int32_t)value) {
|
|
|
|
result = FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = TRUE;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
result = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (notIn) {
|
|
|
|
return !result;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
UBool
|
|
|
|
AndConstraint::isLimited() {
|
|
|
|
return (rangeHigh == -1 || integerOnly) && !notIn && op != MOD;
|
|
|
|
}
|
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
int32_t
|
|
|
|
AndConstraint::updateRepeatLimit(int32_t maxLimit) {
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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
|
2008-06-26 17:51:59 +00:00
|
|
|
OrConstraint::isFulfilled(double number) {
|
2007-12-17 01:39:55 +00:00
|
|
|
OrConstraint* orRule=this;
|
|
|
|
UBool result=FALSE;
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
while (orRule!=NULL && !result) {
|
|
|
|
result=TRUE;
|
|
|
|
AndConstraint* andRule = orRule->childNode;
|
|
|
|
while (andRule!=NULL && result) {
|
|
|
|
result = andRule->isFulfilled(number);
|
|
|
|
andRule=andRule->next;
|
|
|
|
}
|
|
|
|
orRule = orRule->next;
|
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-03-17 21:36:03 +00:00
|
|
|
UBool
|
|
|
|
OrConstraint::isLimited() {
|
|
|
|
for (OrConstraint *orc = this; orc != NULL; orc = orc->next) {
|
|
|
|
UBool result = FALSE;
|
|
|
|
for (AndConstraint *andc = orc->childNode; andc != NULL; andc = andc->next) {
|
|
|
|
if (andc->isLimited()) {
|
|
|
|
result = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (result == FALSE) {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
|
|
|
|
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
|
2008-06-26 17:51:59 +00:00
|
|
|
RuleChain::select(double number) const {
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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];
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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) {
|
2008-06-26 17:51:59 +00:00
|
|
|
if ( andRule->integerOnly ) {
|
|
|
|
result += UNICODE_STRING_SIMPLE(" not in ");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result += UNICODE_STRING_SIMPLE(" not within ");
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
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 {
|
2008-06-26 17:51:59 +00:00
|
|
|
if ( andRule->integerOnly ) {
|
|
|
|
result += UNICODE_STRING_SIMPLE(" in ");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
result += UNICODE_STRING_SIMPLE(" within ");
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
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;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( next != NULL ) {
|
|
|
|
next->setRepeatLimit();
|
|
|
|
limit = next->repeatLimit;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( next != NULL ) {
|
|
|
|
return next->getKeywords(capacityOfKeywords, keywords, arraySize);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return U_ZERO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
2008-03-07 19:40:46 +00:00
|
|
|
RuleChain::isKeyword(const UnicodeString& keywordParam) const {
|
|
|
|
if ( keyword == keywordParam ) {
|
2007-12-17 01:39:55 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( next != NULL ) {
|
2008-03-07 19:40:46 +00:00
|
|
|
return next->isKeyword(keywordParam);
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RuleParser::RuleParser() {
|
|
|
|
}
|
|
|
|
|
|
|
|
RuleParser::~RuleParser() {
|
|
|
|
}
|
|
|
|
|
2007-12-29 05:51:50 +00:00
|
|
|
void
|
|
|
|
RuleParser::checkSyntax(tokenType prevType, tokenType curType, UErrorCode &status)
|
|
|
|
{
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
switch(prevType) {
|
2007-12-29 05:51:50 +00:00
|
|
|
case none:
|
|
|
|
case tSemiColon:
|
|
|
|
if (curType!=tKeyword) {
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
}
|
|
|
|
break;
|
2008-01-20 02:30:30 +00:00
|
|
|
case tVariableN :
|
2011-03-17 21:36:03 +00:00
|
|
|
if (curType != tIs && curType != tMod && curType != tIn &&
|
2008-06-26 17:51:59 +00:00
|
|
|
curType != tNot && curType != tWithin) {
|
2007-12-29 05:51:50 +00:00
|
|
|
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:
|
2008-06-26 17:51:59 +00:00
|
|
|
if (curType != tNumber && curType != tIn && curType != tWithin) {
|
2007-12-29 05:51:50 +00:00
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case tMod:
|
|
|
|
case tDot:
|
|
|
|
case tIn:
|
2008-06-26 17:51:59 +00:00
|
|
|
case tWithin:
|
2007-12-29 05:51:50 +00:00
|
|
|
case tAnd:
|
|
|
|
case tOr:
|
|
|
|
if (curType != tNumber && curType != tVariableN) {
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case tNumber:
|
2008-01-20 02:30:30 +00:00
|
|
|
if (curType != tDot && curType != tSemiColon && curType != tIs && curType != tNot &&
|
2008-06-26 17:51:59 +00:00
|
|
|
curType != tIn && curType != tWithin && curType != tAnd && curType != tOr)
|
2007-12-29 05:51:50 +00:00
|
|
|
{
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
break;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-29 05:51:50 +00:00
|
|
|
void
|
2008-01-20 02:30:30 +00:00
|
|
|
RuleParser::getNextToken(const UnicodeString& ruleData,
|
2007-12-17 01:39:55 +00:00
|
|
|
int32_t *ruleIndex,
|
|
|
|
UnicodeString& token,
|
2007-12-29 05:51:50 +00:00
|
|
|
tokenType& type,
|
|
|
|
UErrorCode &status)
|
|
|
|
{
|
2007-12-17 01:39:55 +00:00
|
|
|
int32_t curIndex= *ruleIndex;
|
|
|
|
UChar ch;
|
|
|
|
tokenType prevType=none;
|
2008-01-20 02:30:30 +00:00
|
|
|
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
while (curIndex<ruleData.length()) {
|
|
|
|
ch = ruleData.charAt(curIndex);
|
|
|
|
if ( !inRange(ch, type) ) {
|
2007-12-29 05:51:50 +00:00
|
|
|
status = U_ILLEGAL_CHARACTER;
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
switch (type) {
|
2007-12-29 05:51:50 +00:00
|
|
|
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;
|
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
break;
|
2007-12-29 05:51:50 +00:00
|
|
|
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 ) {
|
2007-12-17 01:39:55 +00:00
|
|
|
token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
|
2007-12-29 05:51:50 +00:00
|
|
|
*ruleIndex=curIndex; // letter
|
2007-12-17 01:39:55 +00:00
|
|
|
type=prevType;
|
2007-12-29 05:51:50 +00:00
|
|
|
getKeyType(token, type, status);
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else { // two consective dots
|
|
|
|
*ruleIndex=curIndex+2;
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
}
|
2008-01-20 02:30:30 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
return;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
curIndex++;
|
|
|
|
}
|
|
|
|
if ( curIndex>=ruleData.length() ) {
|
|
|
|
if ( (type == tLetter)||(type == tNumber) ) {
|
|
|
|
token=UnicodeString(ruleData, *ruleIndex, curIndex-*ruleIndex);
|
2007-12-29 05:51:50 +00:00
|
|
|
getKeyType(token, type, status);
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
*ruleIndex = ruleData.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
RuleParser::inRange(UChar ch, tokenType& type) {
|
2007-12-17 19:30:03 +00:00
|
|
|
if ((ch>=CAP_A) && (ch<=CAP_Z)) {
|
2007-12-17 01:39:55 +00:00
|
|
|
// we assume all characters are in lower case already.
|
|
|
|
return FALSE;
|
|
|
|
}
|
2007-12-17 19:30:03 +00:00
|
|
|
if ((ch>=LOW_A) && (ch<=LOW_Z)) {
|
2007-12-17 01:39:55 +00:00
|
|
|
type = tLetter;
|
|
|
|
return TRUE;
|
|
|
|
}
|
2007-12-17 19:30:03 +00:00
|
|
|
if ((ch>=U_ZERO) && (ch<=U_NINE)) {
|
2007-12-17 01:39:55 +00:00
|
|
|
type = tNumber;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
switch (ch) {
|
2008-01-20 02:30:30 +00:00
|
|
|
case COLON:
|
2007-12-29 05:51:50 +00:00
|
|
|
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;
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-12-29 05:51:50 +00:00
|
|
|
void
|
|
|
|
RuleParser::getKeyType(const UnicodeString& token, tokenType& keyType, UErrorCode &status)
|
|
|
|
{
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
if ( keyType==tNumber) {
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_VAR_N) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tVariableN;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_IS) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tIs;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_AND) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tAnd;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_IN) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tIn;
|
|
|
|
}
|
2008-06-27 03:33:14 +00:00
|
|
|
else if (token==PK_WITHIN) {
|
2008-06-26 17:51:59 +00:00
|
|
|
keyType = tWithin;
|
2008-06-19 20:23:49 +00:00
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_NOT) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tNot;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_MOD) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tMod;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if (token==PK_OR) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tOr;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else if ( isValidKeyword(token) ) {
|
2007-12-17 01:39:55 +00:00
|
|
|
keyType = tKeyword;
|
|
|
|
}
|
2007-12-29 05:51:50 +00:00
|
|
|
else {
|
|
|
|
status = U_UNEXPECTED_TOKEN;
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
RuleParser::isValidKeyword(const UnicodeString& token) {
|
2011-04-25 20:47:32 +00:00
|
|
|
return PatternProps::isIdentifier(token.getBuffer(), token.length());
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
2011-04-28 13:35:56 +00:00
|
|
|
PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status)
|
|
|
|
: pos(0), fKeywordNames(status) {
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2011-04-28 13:35:56 +00:00
|
|
|
fKeywordNames.setDeleter(uhash_deleteUObject);
|
|
|
|
UBool addKeywordOther=TRUE;
|
|
|
|
RuleChain *node=header;
|
2008-04-17 01:33:23 +00:00
|
|
|
while(node!=NULL) {
|
|
|
|
fKeywordNames.addElement(new UnicodeString(node->keyword), status);
|
2009-02-05 01:03:16 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2008-04-17 01:33:23 +00:00
|
|
|
if (node->keyword == PLURAL_KEYWORD_OTHER) {
|
2011-04-28 13:35:56 +00:00
|
|
|
addKeywordOther= FALSE;
|
2008-04-17 01:33:23 +00:00
|
|
|
}
|
|
|
|
node=node->next;
|
|
|
|
}
|
2011-03-17 21:36:03 +00:00
|
|
|
|
2008-04-17 01:33:23 +00:00
|
|
|
if (addKeywordOther) {
|
|
|
|
fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
|
|
|
|
}
|
2007-12-17 01:39:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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() {
|
|
|
|
}
|
|
|
|
|
|
|
|
U_NAMESPACE_END
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
|
|
|
|
|
|
//eof
|