]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/plurrule.cpp
ICU-511.32.tar.gz
[apple/icu.git] / icuSources / i18n / plurrule.cpp
index 987bb243f4766613fc3940134bf3ce38ab50df17..2ceb511f488da2204514c3c34b9c21c6e9cb82c0 100644 (file)
@@ -1,49 +1,38 @@
 /*
 *******************************************************************************
-* Copyright (C) 2007-2010, International Business Machines Corporation and
+* Copyright (C) 2007-2012, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
-* File PLURRULE.CPP
-*
-* Modification History:
-*
-*   Date        Name        Description
-*******************************************************************************
+* File plurrule.cpp
 */
 
-
-#include "unicode/uniset.h"
 #include "unicode/utypes.h"
-#include "unicode/ures.h"
+#include "unicode/localpointer.h"
 #include "unicode/plurrule.h"
+#include "unicode/upluralrules.h"
+#include "unicode/ures.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "hash.h"
 #include "mutex.h"
+#include "patternprops.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"
-*/
+#include "uassert.h"
 
 #if !UCONFIG_NO_FORMATTING
 
 U_NAMESPACE_BEGIN
 
+// shared by all instances when lazy-initializing samples
+static UMutex pluralMutex = U_MUTEX_INITIALIZER;
 
 #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};
@@ -60,7 +49,11 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
 
 PluralRules::PluralRules(UErrorCode& status)
 :   UObject(),
-    mRules(NULL)
+    mRules(NULL),
+    mParser(NULL),
+    mSamples(NULL),
+    mSampleInfo(NULL),
+    mSampleInfoCount(0)
 {
     if (U_FAILURE(status)) {
         return;
@@ -74,7 +67,10 @@ PluralRules::PluralRules(UErrorCode& status)
 PluralRules::PluralRules(const PluralRules& other)
 : UObject(other),
     mRules(NULL),
-    mParser(new RuleParser())
+  mParser(NULL),
+  mSamples(NULL),
+  mSampleInfo(NULL),
+  mSampleInfoCount(0)
 {
     *this=other;
 }
@@ -82,6 +78,8 @@ PluralRules::PluralRules(const PluralRules& other)
 PluralRules::~PluralRules() {
     delete mRules;
     delete mParser;
+    uprv_free(mSamples);
+    uprv_free(mSampleInfo);
 }
 
 PluralRules*
@@ -101,6 +99,13 @@ PluralRules::operator=(const PluralRules& other) {
         }
         delete mParser;
         mParser = new RuleParser();
+
+        uprv_free(mSamples);
+        mSamples = NULL;
+
+        uprv_free(mSampleInfo);
+        mSampleInfo = NULL;
+        mSampleInfoCount = 0;
     }
 
     return *this;
@@ -131,20 +136,30 @@ PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
 
 PluralRules* U_EXPORT2
 PluralRules::createDefaultRules(UErrorCode& status) {
-    return createRules(PLURAL_DEFAULT_RULE, status);
+    return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status);
 }
 
 PluralRules* U_EXPORT2
 PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
+    return forLocale(locale, UPLURAL_TYPE_CARDINAL, status);
+}
+
+PluralRules* U_EXPORT2
+PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
     RuleChain   rChain;
     if (U_FAILURE(status)) {
         return NULL;
     }
+    if (type >= UPLURAL_TYPE_COUNT) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return NULL;
+    }
     PluralRules *newObj = new PluralRules(status);
     if (newObj==NULL || U_FAILURE(status)) {
+        delete newObj;
         return NULL;
     }
-    UnicodeString locRule = newObj->getRuleFromResource(locale, status);
+    UnicodeString locRule = newObj->getRuleFromResource(locale, type, status);
     if ((locRule.length() != 0) && U_SUCCESS(status)) {
         newObj->parseDescription(locRule, rChain, status);
         if (U_SUCCESS(status)) {
@@ -158,14 +173,14 @@ PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
         newObj->parseDescription(defRule, rChain, status);
         newObj->addRules(rChain);
     }
-    
+
     return newObj;
 }
 
 UnicodeString
 PluralRules::select(int32_t number) const {
     if (mRules == NULL) {
-        return PLURAL_DEFAULT_RULE;
+        return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
     }
     else {
         return mRules->select(number);
@@ -175,7 +190,7 @@ PluralRules::select(int32_t number) const {
 UnicodeString
 PluralRules::select(double number) const {
     if (mRules == NULL) {
-        return PLURAL_DEFAULT_RULE;
+        return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
     }
     else {
         return mRules->select(number);
@@ -186,15 +201,81 @@ StringEnumeration*
 PluralRules::getKeywords(UErrorCode& status) const {
     if (U_FAILURE(status))  return NULL;
     StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status);
-    if (U_FAILURE(status))  return NULL;
+    if (U_FAILURE(status)) {
+      delete nameEnumerator;
+      return NULL;
+    }
 
     return nameEnumerator;
 }
 
+double
+PluralRules::getUniqueKeywordValue(const UnicodeString& keyword) {
+  double val = 0.0;
+  UErrorCode status = U_ZERO_ERROR;
+  int32_t count = getSamplesInternal(keyword, &val, 1, FALSE, status);
+  return count == 1 ? val : UPLRULES_NO_UNIQUE_VALUE;
+}
+
+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;
+    }
+    if (destCapacity < 0 || (dest == NULL && destCapacity > 0)) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return -1;
+    }
+
+    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;
+    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];
+    }
+    return len;
+}
+
 
 UBool
 PluralRules::isKeyword(const UnicodeString& keyword) const {
-    if ( keyword == PLURAL_KEYWORD_OTHER ) {
+    if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
         return true;
     }
     else {
@@ -209,60 +290,42 @@ PluralRules::isKeyword(const UnicodeString& keyword) const {
 
 UnicodeString
 PluralRules::getKeywordOther() const {
-    return PLURAL_KEYWORD_OTHER;
+    return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5);
 }
 
 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);
+    LocalPointer<StringEnumeration> myKeywordList(getKeywords(status));
+    LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status));
     if (U_FAILURE(status)) {
         return FALSE;
     }
 
-    if (myKeywordList->count(status)!=otherKeywordList->count(status) ||
-        U_FAILURE(status)) {
-        sameList = FALSE;
+    if (myKeywordList->count(status)!=otherKeywordList->count(status)) {
+        return 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)) {
+    myKeywordList->reset(status);
+    while ((ptrKeyword=myKeywordList->snext(status))!=NULL) {
+        if (!other.isKeyword(*ptrKeyword)) {
             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) {
+    }
+    otherKeywordList->reset(status);
+    while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) {
+        if (!this->isKeyword(*ptrKeyword)) {
             return FALSE;
         }
     }
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
 
     if ((limit=this->getRepeatLimit()) != other.getRepeatLimit()) {
         return FALSE;
@@ -293,7 +356,7 @@ PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode
     if (U_FAILURE(status)) {
         return;
     }
-    UnicodeString ruleData = data.toLower();
+    UnicodeString ruleData = data.toLower("");
     while (ruleIndex< ruleData.length()) {
         mParser->getNextToken(ruleData, &ruleIndex, token, type, status);
         if (U_FAILURE(status)) {
@@ -305,6 +368,7 @@ PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode
         }
         switch (type) {
         case tAnd:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint = curAndConstraint->add();
             break;
         case tOr:
@@ -322,19 +386,24 @@ PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode
             curAndConstraint = orNode->add();
             break;
         case tIs:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint->rangeHigh=-1;
             break;
         case tNot:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint->notIn=TRUE;
             break;
         case tIn:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
             curAndConstraint->integerOnly = TRUE;
             break;
         case tWithin:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint->rangeHigh=PLURAL_RANGE_HIGH;
             break;
         case tNumber:
+            U_ASSERT(curAndConstraint != NULL);
             if ( (curAndConstraint->op==AndConstraint::MOD)&&
                  (curAndConstraint->opNum == -1 ) ) {
                 curAndConstraint->opNum=getNumberValue(token);
@@ -349,6 +418,7 @@ PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode
             }
             break;
         case tMod:
+            U_ASSERT(curAndConstraint != NULL);
             curAndConstraint->op=AndConstraint::MOD;
             break;
         case tKeyword:
@@ -361,6 +431,9 @@ PluralRules::parseDescription(UnicodeString& data, RuleChain& rules, UErrorCode
                 }
                 ruleChain=ruleChain->next=new RuleChain();
             }
+            if (ruleChain->ruleHeader != NULL) {
+                delete ruleChain->ruleHeader;
+            }
             orNode = ruleChain->ruleHeader = new OrConstraint();
             curAndConstraint = orNode->add();
             ruleChain->keyword = token;
@@ -416,6 +489,172 @@ PluralRules::getRepeatLimit() const {
     }
 }
 
+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 (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
+            return n;
+        }
+    }
+    return -1;
+}
+
+typedef struct SampleRecord {
+    int32_t ruleIndex;
+    double  value;
+} SampleRecord;
+
+void
+PluralRules::initSamples(UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    Mutex lock(&pluralMutex);
+
+    if (mSamples) {
+        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 && 0 == rc->keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
+                otherIndex = maxIndex;
+            }
+            ++maxIndex;
+        }
+        rc = rc->next;
+    }
+    if (otherIndex == -1) {
+        ++maxIndex;
+    }
+
+    LocalMemory<int32_t> newSampleInfo;
+    if (NULL == newSampleInfo.allocateInsteadAndCopy(maxIndex)) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+
+    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
+    }
+
+    MaybeStackArray<SampleRecord, 10> newSamples;
+    int32_t sampleCount = 0;
+
+    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;
+
+        n = 0;
+        rc = mRules;
+        int32_t found = -1;
+        while (rc != NULL) {
+            if (rc->ruleHeader != NULL) {
+                if (rc->ruleHeader->isFulfilled(val)) {
+                    found = n;
+                    break;
+                }
+                ++n;
+            }
+            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
+
+        if (sampleCount == newSamples.getCapacity()) {
+            int32_t newCapacity = sampleCount < 20 ? 128 : sampleCount * 2;
+            if (NULL == newSamples.resize(newCapacity, sampleCount)) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                return;
+            }
+        }
+        newSamples[sampleCount].ruleIndex = found;
+        newSamples[sampleCount].value = val;
+        ++sampleCount;
+
+        if (newSampleInfo[found] == MAX_SAMPLES) { // limit flag not set
+            --keywordsRemaining;
+        }
+    }
+
+    // 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;
+            }
+        }
+    }
+
+    // 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;
+}
 
 void
 PluralRules::addRules(RuleChain& rules) {
@@ -425,38 +664,48 @@ PluralRules::addRules(RuleChain& rules) {
 }
 
 UnicodeString
-PluralRules::getRuleFromResource(const Locale& locale, UErrorCode& errCode) {
+PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) {
     UnicodeString emptyStr;
-    
+
     if (U_FAILURE(errCode)) {
         return emptyStr;
     }
-    UResourceBundle *rb=ures_openDirect(NULL, "plurals", &errCode);
+    LocalUResourceBundlePointer 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);
+    const char *typeKey;
+    switch (type) {
+    case UPLURAL_TYPE_CARDINAL:
+        typeKey = "locales";
+        break;
+    case UPLURAL_TYPE_ORDINAL:
+        typeKey = "locales_ordinals";
+        break;
+    default:
+        // Must not occur: The caller should have checked for valid types.
+        errCode = U_ILLEGAL_ARGUMENT_ERROR;
+        return emptyStr;
+    }
+    LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, 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);
+    const UChar* s = ures_getStringByKey(locRes.getAlias(), 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) {
+
+        while (uloc_getParent(parentLocaleName, parentLocaleName,
+                                       ULOC_FULLNAME_CAPACITY, &status) > 0) {
             resLen=0;
-            s = ures_getStringByKey(locRes, parentLocaleName, &resLen, &status);
+            s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status);
             if (s != NULL) {
                 errCode = U_ZERO_ERROR;
                 break;
@@ -465,39 +714,32 @@ PluralRules::getRuleFromResource(const Locale& locale, UErrorCode& errCode) {
         }
     }
     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);
+
+    LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "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);
+    LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, NULL, &errCode));
     if (U_FAILURE(errCode)) {
-        ures_close(ruleRes);
-        ures_close(locRes);
-        ures_close(rb);
         return emptyStr;
     }
 
-    int32_t numberKeys = ures_getSize(setRes);
+    int32_t numberKeys = ures_getSize(setRes.getAlias());
     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);
+        s=ures_getNextString(setRes.getAlias(), &resLen, (const char**)&key, &errCode);
         keyLen = (int32_t)uprv_strlen(key);
         u_charsToUChars(key, result+len, keyLen);
         len += keyLen;
@@ -510,12 +752,7 @@ PluralRules::getRuleFromResource(const Locale& locale, UErrorCode& errCode) {
     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() {
@@ -555,7 +792,12 @@ UBool
 AndConstraint::isFulfilled(double number) {
     UBool result=TRUE;
     double value=number;
-    
+
+    // arrrrrrgh
+    if ((rangeHigh == -1 || integerOnly) && number != uprv_floor(number)) {
+      return notIn;
+    }
+
     if ( op == MOD ) {
         value = (int32_t)value % opNum;
     }
@@ -598,9 +840,14 @@ AndConstraint::isFulfilled(double number) {
     }
 }
 
+UBool 
+AndConstraint::isLimited() {
+    return (rangeHigh == -1 || integerOnly) && !notIn && op != MOD;
+}
+
 int32_t
 AndConstraint::updateRepeatLimit(int32_t maxLimit) {
-    
+
     if ( op == MOD ) {
         return uprv_max(opNum, maxLimit);
     }
@@ -669,7 +916,7 @@ UBool
 OrConstraint::isFulfilled(double number) {
     OrConstraint* orRule=this;
     UBool result=FALSE;
-    
+
     while (orRule!=NULL && !result) {
         result=TRUE;
         AndConstraint* andRule = orRule->childNode;
@@ -679,10 +926,26 @@ OrConstraint::isFulfilled(double number) {
         }
         orRule = orRule->next;
     }
-    
+
     return result;
 }
 
+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;
+}
 
 RuleChain::RuleChain() {
     ruleHeader=NULL;
@@ -719,7 +982,7 @@ RuleChain::~RuleChain() {
 
 UnicodeString
 RuleChain::select(double number) const {
-   
+
    if ( ruleHeader != NULL ) {
        if (ruleHeader->isFulfilled(number)) {
            return keyword;
@@ -729,7 +992,7 @@ RuleChain::select(double number) const {
        return next->select(number);
    }
    else {
-       return PLURAL_KEYWORD_OTHER;
+       return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5);
    }
 
 }
@@ -737,7 +1000,7 @@ RuleChain::select(double number) const {
 void
 RuleChain::dumpRules(UnicodeString& result) {
     UChar digitString[16];
-    
+
     if ( ruleHeader != NULL ) {
         result +=  keyword;
         OrConstraint* orRule=ruleHeader;
@@ -802,11 +1065,11 @@ RuleChain::dumpRules(UnicodeString& result) {
                     }
                 }
                 if ( (andRule=andRule->next) != NULL) {
-                    result += PK_AND;
+                    result.append(PK_AND, 3);
                 }
             }
             if ( (orRule = orRule->next) != NULL ) {
-                result += PK_OR;
+                result.append(PK_OR, 2);
             }
         }
     }
@@ -876,16 +1139,9 @@ RuleChain::isKeyword(const UnicodeString& keywordParam) const {
 
 
 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
@@ -902,7 +1158,7 @@ RuleParser::checkSyntax(tokenType prevType, tokenType curType, UErrorCode &statu
         }
         break;
     case tVariableN :
-        if (curType != tIs && curType != tMod && curType != tIn && 
+        if (curType != tIs && curType != tMod && curType != tIn &&
             curType != tNot && curType != tWithin) {
             status = U_UNEXPECTED_TOKEN;
         }
@@ -1035,7 +1291,6 @@ RuleParser::getNextToken(const UnicodeString& ruleData,
                     return;
                  }
              }
-             break;
          default:
              status = U_UNEXPECTED_TOKEN;
              return;
@@ -1096,28 +1351,28 @@ RuleParser::getKeyType(const UnicodeString& token, tokenType& keyType, UErrorCod
     }
     if ( keyType==tNumber) {
     }
-    else if (token==PK_VAR_N) {
+    else if (0 == token.compare(PK_VAR_N, 1)) {
         keyType = tVariableN;
     }
-    else if (token==PK_IS) {
+    else if (0 == token.compare(PK_IS, 2)) {
         keyType = tIs;
     }
-    else if (token==PK_AND) {
+    else if (0 == token.compare(PK_AND, 3)) {
         keyType = tAnd;
     }
-    else if (token==PK_IN) {
+    else if (0 == token.compare(PK_IN, 2)) {
         keyType = tIn;
     }
-    else if (token==PK_WITHIN) {
+    else if (0 == token.compare(PK_WITHIN, 6)) {
         keyType = tWithin;
     }
-    else if (token==PK_NOT) {
+    else if (0 == token.compare(PK_NOT, 3)) {
         keyType = tNot;
     }
-    else if (token==PK_MOD) {
+    else if (0 == token.compare(PK_MOD, 3)) {
         keyType = tMod;
     }
-    else if (token==PK_OR) {
+    else if (0 == token.compare(PK_OR, 2)) {
         keyType = tOr;
     }
     else if ( isValidKeyword(token) ) {
@@ -1130,50 +1385,30 @@ RuleParser::getKeyType(const UnicodeString& token, tokenType& keyType, UErrorCod
 
 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;
-    }
+    return PatternProps::isIdentifier(token.getBuffer(), token.length());
 }
 
-PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) :
-fKeywordNames(status)
-{
-    RuleChain *node=header;
-    UBool  addKeywordOther=true;
-
+PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status)
+        : pos(0), fKeywordNames(status) {
     if (U_FAILURE(status)) {
         return;
     }
-    pos=0;
-    fKeywordNames.removeAllElements();
+    fKeywordNames.setDeleter(uprv_deleteUObject);
+    UBool  addKeywordOther=TRUE;
+    RuleChain *node=header;
     while(node!=NULL) {
         fKeywordNames.addElement(new UnicodeString(node->keyword), status);
         if (U_FAILURE(status)) {
             return;
         }
-        if (node->keyword == PLURAL_KEYWORD_OTHER) {
-            addKeywordOther= false;
+        if (0 == node->keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
+            addKeywordOther= FALSE;
         }
         node=node->next;
     }
-    
+
     if (addKeywordOther) {
         fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
-        if (U_FAILURE(status)) {
-            return;
-        }
     }
 }
 
@@ -1196,12 +1431,6 @@ PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
 }
 
 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