Index: icu46/source/i18n/plurfmt.cpp |
=================================================================== |
--- icu46/source/i18n/plurfmt.cpp (revision 0) |
+++ icu46/source/i18n/plurfmt.cpp (revision 0) |
@@ -0,0 +1,537 @@ |
+/* |
+******************************************************************************* |
+* Copyright (C) 2009, International Business Machines Corporation and |
+* others. All Rights Reserved. |
+******************************************************************************* |
+* |
+* File PLURFMT.CPP |
+* |
+* Modification History: |
+* |
+* Date Name Description |
+******************************************************************************* |
+*/ |
+ |
+ |
+#include "unicode/utypes.h" |
+#include "unicode/plurfmt.h" |
+#include "unicode/plurrule.h" |
+#include "plurrule_impl.h" |
+ |
+#if !UCONFIG_NO_FORMATTING |
+ |
+U_NAMESPACE_BEGIN |
+ |
+U_CDECL_BEGIN |
+static void U_CALLCONV |
+deleteHashStrings(void *obj) { |
+ delete (UnicodeString *)obj; |
+} |
+U_CDECL_END |
+ |
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat) |
+ |
+#define MAX_KEYWORD_SIZE 30 |
+ |
+PluralFormat::PluralFormat(UErrorCode& status) { |
+ init(NULL, Locale::getDefault(), status); |
+} |
+ |
+PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) { |
+ init(NULL, loc, status); |
+} |
+ |
+PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) { |
+ init(&rules, Locale::getDefault(), status); |
+} |
+ |
+PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, UErrorCode& status) { |
+ init(&rules, loc, status); |
+} |
+ |
+PluralFormat::PluralFormat(const UnicodeString& pat, UErrorCode& status) { |
+ init(NULL, Locale::getDefault(), status); |
+ applyPattern(pat, status); |
+} |
+ |
+PluralFormat::PluralFormat(const Locale& loc, const UnicodeString& pat, UErrorCode& status) { |
+ init(NULL, loc, status); |
+ applyPattern(pat, status); |
+} |
+ |
+PluralFormat::PluralFormat(const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) { |
+ init(&rules, Locale::getDefault(), status); |
+ applyPattern(pat, status); |
+} |
+ |
+PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) { |
+ init(&rules, loc, status); |
+ applyPattern(pat, status); |
+} |
+ |
+PluralFormat::PluralFormat(const PluralFormat& other) : Format(other) { |
+ UErrorCode status = U_ZERO_ERROR; |
+ locale = other.locale; |
+ pluralRules = other.pluralRules->clone(); |
+ pattern = other.pattern; |
+ copyHashtable(other.fParsedValuesHash, status); |
+ if (U_FAILURE(status)) { |
+ delete pluralRules; |
+ pluralRules = NULL; |
+ return; |
+ } |
+ numberFormat=NumberFormat::createInstance(locale, status); |
+ if (U_FAILURE(status)) { |
+ delete pluralRules; |
+ pluralRules = NULL; |
+ delete fParsedValuesHash; |
+ fParsedValuesHash = NULL; |
+ return; |
+ } |
+ replacedNumberFormat=other.replacedNumberFormat; |
+} |
+ |
+PluralFormat::~PluralFormat() { |
+ delete pluralRules; |
+ delete fParsedValuesHash; |
+ delete numberFormat; |
+} |
+ |
+void |
+PluralFormat::init(const PluralRules* rules, const Locale& curLocale, UErrorCode& status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ locale = curLocale; |
+ if ( rules==NULL) { |
+ pluralRules = PluralRules::forLocale(locale, status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ } |
+ else { |
+ pluralRules = rules->clone(); |
+ } |
+ fParsedValuesHash=NULL; |
+ pattern.remove(); |
+ numberFormat= NumberFormat::createInstance(curLocale, status); |
+ if (U_FAILURE(status)) { |
+ delete pluralRules; |
+ pluralRules = NULL; |
+ return; |
+ } |
+ replacedNumberFormat=NULL; |
+} |
+ |
+void |
+PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ this->pattern = newPattern; |
+ UnicodeString token; |
+ int32_t braceCount=0; |
+ fmtToken type; |
+ UBool spaceIncluded=FALSE; |
+ |
+ if (fParsedValuesHash==NULL) { |
+ fParsedValuesHash = new Hashtable(TRUE, status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ fParsedValuesHash->setValueDeleter(deleteHashStrings); |
+ } |
+ |
+ UBool getKeyword=TRUE; |
+ UnicodeString hashKeyword; |
+ UnicodeString *hashPattern; |
+ |
+ for (int32_t i=0; i<pattern.length(); ++i) { |
+ UChar ch=pattern.charAt(i); |
+ |
+ if ( !inRange(ch, type) ) { |
+ if (getKeyword) { |
+ status = U_ILLEGAL_CHARACTER; |
+ return; |
+ } |
+ else { |
+ token += ch; |
+ continue; |
+ } |
+ } |
+ switch (type) { |
+ case tSpace: |
+ if (token.length()==0) { |
+ continue; |
+ } |
+ if (getKeyword) { |
+ // space after keyword |
+ spaceIncluded = TRUE; |
+ } |
+ else { |
+ token += ch; |
+ } |
+ break; |
+ case tLeftBrace: |
+ if ( getKeyword ) { |
+ if (fParsedValuesHash->get(token)!= NULL) { |
+ status = U_DUPLICATE_KEYWORD; |
+ return; |
+ } |
+ if (token.length()==0) { |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ return; |
+ } |
+ if (!pluralRules->isKeyword(token)) { |
+ status = U_UNDEFINED_KEYWORD; |
+ return; |
+ } |
+ hashKeyword = token; |
+ getKeyword = FALSE; |
+ token.remove(); |
+ } |
+ else { |
+ if (braceCount==0) { |
+ status = U_UNEXPECTED_TOKEN; |
+ return; |
+ } |
+ else { |
+ token += ch; |
+ } |
+ } |
+ braceCount++; |
+ spaceIncluded = FALSE; |
+ break; |
+ case tRightBrace: |
+ if ( getKeyword ) { |
+ status = U_UNEXPECTED_TOKEN; |
+ return; |
+ } |
+ else { |
+ hashPattern = new UnicodeString(token); |
+ fParsedValuesHash->put(hashKeyword, hashPattern, status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ braceCount--; |
+ if ( braceCount==0 ) { |
+ getKeyword=TRUE; |
+ hashKeyword.remove(); |
+ hashPattern=NULL; |
+ token.remove(); |
+ } |
+ else { |
+ token += ch; |
+ } |
+ } |
+ spaceIncluded = FALSE; |
+ break; |
+ case tLetter: |
+ case tNumberSign: |
+ if (spaceIncluded) { |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ return; |
+ } |
+ default: |
+ token+=ch; |
+ break; |
+ } |
+ } |
+ if ( checkSufficientDefinition() ) { |
+ return; |
+ } |
+ else { |
+ status = U_DEFAULT_KEYWORD_MISSING; |
+ return; |
+ } |
+} |
+ |
+UnicodeString& |
+PluralFormat::format(const Formattable& obj, |
+ UnicodeString& appendTo, |
+ FieldPosition& pos, |
+ UErrorCode& status) const |
+{ |
+ if (U_FAILURE(status)) return appendTo; |
+ int32_t number; |
+ |
+ switch (obj.getType()) |
+ { |
+ case Formattable::kDouble: |
+ return format((int32_t)obj.getDouble(), appendTo, pos, status); |
+ break; |
+ case Formattable::kLong: |
+ number = (int32_t)obj.getLong(); |
+ return format(number, appendTo, pos, status); |
+ break; |
+ case Formattable::kInt64: |
+ return format((int32_t)obj.getInt64(), appendTo, pos, status); |
+ default: |
+ status = U_ILLEGAL_ARGUMENT_ERROR; |
+ return appendTo; |
+ } |
+} |
+ |
+UnicodeString |
+PluralFormat::format(int32_t number, UErrorCode& status) const { |
+ if (U_FAILURE(status)) { |
+ return UnicodeString(); |
+ } |
+ FieldPosition fpos(0); |
+ UnicodeString result; |
+ |
+ return format(number, result, fpos, status); |
+} |
+ |
+UnicodeString |
+PluralFormat::format(double number, UErrorCode& status) const { |
+ if (U_FAILURE(status)) { |
+ return UnicodeString(); |
+ } |
+ FieldPosition fpos(0); |
+ UnicodeString result; |
+ |
+ return format(number, result, fpos, status); |
+} |
+ |
+ |
+UnicodeString& |
+PluralFormat::format(int32_t number, |
+ UnicodeString& appendTo, |
+ FieldPosition& pos, |
+ UErrorCode& status) const { |
+ return format((double)number, appendTo, pos, status); |
+} |
+ |
+UnicodeString& |
+PluralFormat::format(double number, |
+ UnicodeString& appendTo, |
+ FieldPosition& pos, |
+ UErrorCode& /*status*/) const { |
+ |
+ if (fParsedValuesHash==NULL) { |
+ if ( replacedNumberFormat== NULL ) { |
+ return numberFormat->format(number, appendTo, pos); |
+ } |
+ else { |
+ replacedNumberFormat->format(number, appendTo, pos); |
+ } |
+ } |
+ UnicodeString selectedRule = pluralRules->select(number); |
+ UnicodeString *selectedPattern = (UnicodeString *)fParsedValuesHash->get(selectedRule); |
+ if (selectedPattern==NULL) { |
+ selectedPattern = (UnicodeString *)fParsedValuesHash->get(pluralRules->getKeywordOther()); |
+ } |
+ appendTo = insertFormattedNumber(number, *selectedPattern, appendTo, pos); |
+ |
+ return appendTo; |
+} |
+ |
+UnicodeString& |
+PluralFormat::toPattern(UnicodeString& appendTo) { |
+ appendTo+= pattern; |
+ return appendTo; |
+} |
+ |
+UBool |
+PluralFormat::inRange(UChar ch, fmtToken& 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; |
+ } |
+ switch (ch) { |
+ case LEFTBRACE: |
+ type = tLeftBrace; |
+ return TRUE; |
+ case SPACE: |
+ type = tSpace; |
+ return TRUE; |
+ case RIGHTBRACE: |
+ type = tRightBrace; |
+ return TRUE; |
+ case NUMBER_SIGN: |
+ type = tNumberSign; |
+ return TRUE; |
+ default : |
+ type = none; |
+ return FALSE; |
+ } |
+} |
+ |
+UBool |
+PluralFormat::checkSufficientDefinition() { |
+ // Check that at least the default rule is defined. |
+ if (fParsedValuesHash==NULL) return FALSE; |
+ if (fParsedValuesHash->get(pluralRules->getKeywordOther()) == NULL) { |
+ return FALSE; |
+ } |
+ else { |
+ return TRUE; |
+ } |
+} |
+ |
+void |
+PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ if (pluralRules!=NULL) { |
+ delete pluralRules; |
+ pluralRules=NULL; |
+ } |
+ if (fParsedValuesHash!= NULL) { |
+ delete fParsedValuesHash; |
+ fParsedValuesHash = NULL; |
+ } |
+ if (numberFormat!=NULL) { |
+ delete numberFormat; |
+ numberFormat = NULL; |
+ replacedNumberFormat=NULL; |
+ } |
+ init(NULL, loc, status); |
+} |
+ |
+void |
+PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& /*status*/) { |
+ // TODO: The copy constructor and assignment op of NumberFormat class are protected. |
+ // create a pointer as the workaround. |
+ replacedNumberFormat = (NumberFormat *)format; |
+} |
+ |
+Format* |
+PluralFormat::clone() const |
+{ |
+ return new PluralFormat(*this); |
+} |
+ |
+PluralFormat& |
+PluralFormat::operator=(const PluralFormat& other) { |
+ if (this != &other) { |
+ UErrorCode status = U_ZERO_ERROR; |
+ delete pluralRules; |
+ delete fParsedValuesHash; |
+ delete numberFormat; |
+ locale = other.locale; |
+ pluralRules = other.pluralRules->clone(); |
+ pattern = other.pattern; |
+ copyHashtable(other.fParsedValuesHash, status); |
+ if (U_FAILURE(status)) { |
+ delete pluralRules; |
+ pluralRules = NULL; |
+ fParsedValuesHash = NULL; |
+ numberFormat = NULL; |
+ return *this; |
+ } |
+ numberFormat=NumberFormat::createInstance(locale, status); |
+ if (U_FAILURE(status)) { |
+ delete pluralRules; |
+ delete fParsedValuesHash; |
+ pluralRules = NULL; |
+ fParsedValuesHash = NULL; |
+ numberFormat = NULL; |
+ return *this; |
+ } |
+ replacedNumberFormat=other.replacedNumberFormat; |
+ } |
+ |
+ return *this; |
+} |
+ |
+UBool |
+PluralFormat::operator==(const Format& other) const { |
+ // This protected comparison operator should only be called by subclasses |
+ // which have confirmed that the other object being compared against is |
+ // an instance of a sublcass of PluralFormat. THIS IS IMPORTANT. |
+ // Format::operator== guarantees that this cast is safe |
+ PluralFormat* fmt = (PluralFormat*)&other; |
+ return ((*pluralRules == *(fmt->pluralRules)) && |
+ (*numberFormat == *(fmt->numberFormat))); |
+} |
+ |
+UBool |
+PluralFormat::operator!=(const Format& other) const { |
+ return !operator==(other); |
+} |
+ |
+void |
+PluralFormat::parseObject(const UnicodeString& /*source*/, |
+ Formattable& /*result*/, |
+ ParsePosition& /*pos*/) const |
+{ |
+ // TODO: not yet supported in icu4j and icu4c |
+} |
+ |
+UnicodeString |
+PluralFormat::insertFormattedNumber(double number, |
+ UnicodeString& message, |
+ UnicodeString& appendTo, |
+ FieldPosition& pos) const { |
+ UnicodeString result; |
+ int32_t braceStack=0; |
+ int32_t startIndex=0; |
+ |
+ if (message.length()==0) { |
+ return result; |
+ } |
+ appendTo = numberFormat->format(number, appendTo, pos); |
+ for(int32_t i=0; i<message.length(); ++i) { |
+ switch(message.charAt(i)) { |
+ case LEFTBRACE: |
+ ++braceStack; |
+ break; |
+ case RIGHTBRACE: |
+ --braceStack; |
+ break; |
+ case NUMBER_SIGN: |
+ if (braceStack==0) { |
+ result += UnicodeString(message, startIndex, i); |
+ result += appendTo; |
+ startIndex = i + 1; |
+ } |
+ break; |
+ } |
+ } |
+ if ( startIndex < message.length() ) { |
+ result += UnicodeString(message, startIndex, message.length()-startIndex); |
+ } |
+ appendTo = result; |
+ return result; |
+} |
+ |
+void |
+PluralFormat::copyHashtable(Hashtable *other, UErrorCode& status) { |
+ if (other == NULL || U_FAILURE(status)) { |
+ fParsedValuesHash = NULL; |
+ return; |
+ } |
+ fParsedValuesHash = new Hashtable(TRUE, status); |
+ if(U_FAILURE(status)){ |
+ return; |
+ } |
+ fParsedValuesHash->setValueDeleter(deleteHashStrings); |
+ int32_t pos = -1; |
+ const UHashElement* elem = NULL; |
+ // walk through the hash table and create a deep clone |
+ while((elem = other->nextElement(pos))!= NULL){ |
+ const UHashTok otherKeyTok = elem->key; |
+ UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; |
+ const UHashTok otherKeyToVal = elem->value; |
+ UnicodeString* otherValue = (UnicodeString*)otherKeyToVal.pointer; |
+ fParsedValuesHash->put(*otherKey, new UnicodeString(*otherValue), status); |
+ if(U_FAILURE(status)){ |
+ return; |
+ } |
+ } |
+} |
+ |
+ |
+U_NAMESPACE_END |
+ |
+ |
+#endif /* #if !UCONFIG_NO_FORMATTING */ |
+ |
+//eof |
Property changes on: icu46/source/i18n/plurfmt.cpp |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |