Index: icu46/source/i18n/selfmt.cpp |
=================================================================== |
--- icu46/source/i18n/selfmt.cpp (revision 0) |
+++ icu46/source/i18n/selfmt.cpp (revision 0) |
@@ -0,0 +1,447 @@ |
+/******************************************************************** |
+ * COPYRIGHT: |
+ * Copyright (c) 1997-2010, International Business Machines Corporation and |
+ * others. All Rights Reserved. |
+ * Copyright (C) 2010 , Yahoo! Inc. |
+ ******************************************************************** |
+ * |
+ * File SELFMT.CPP |
+ * |
+ * Modification History: |
+ * |
+ * Date Name Description |
+ * 11/11/09 kirtig Finished first cut of implementation. |
+ * 11/16/09 kirtig Improved version |
+ ********************************************************************/ |
+ |
+#include <typeinfo> // for 'typeid' to work |
+ |
+#include "unicode/utypes.h" |
+#include "unicode/ustring.h" |
+#include "unicode/ucnv_err.h" |
+#include "unicode/uchar.h" |
+#include "unicode/umsg.h" |
+#include "unicode/rbnf.h" |
+#include "cmemory.h" |
+#include "util.h" |
+#include "uassert.h" |
+#include "ustrfmt.h" |
+#include "uvector.h" |
+ |
+#include "unicode/selfmt.h" |
+#include "selfmtimpl.h" |
+ |
+#if !UCONFIG_NO_FORMATTING |
+ |
+U_NAMESPACE_BEGIN |
+ |
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SelectFormat) |
+ |
+#define MAX_KEYWORD_SIZE 30 |
+static const UChar SELECT_KEYWORD_OTHER[] = {LOW_O, LOW_T, LOW_H, LOW_E, LOW_R, 0}; |
+ |
+SelectFormat::SelectFormat(const UnicodeString& pat, UErrorCode& status) : parsedValuesHash(NULL) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ initHashTable(status); |
+ applyPattern(pat, status); |
+} |
+ |
+SelectFormat::SelectFormat(const SelectFormat& other) : Format(other), parsedValuesHash(NULL) { |
+ UErrorCode status = U_ZERO_ERROR; |
+ pattern = other.pattern; |
+ copyHashtable(other.parsedValuesHash, status); |
+} |
+ |
+SelectFormat::~SelectFormat() { |
+ cleanHashTable(); |
+} |
+ |
+void SelectFormat::initHashTable(UErrorCode &status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ // has inited |
+ if (parsedValuesHash != NULL) { |
+ return; |
+ } |
+ |
+ parsedValuesHash = new Hashtable(TRUE, status); |
+ if (U_FAILURE(status)) { |
+ cleanHashTable(); |
+ return; |
+ } else { |
+ if (parsedValuesHash == NULL) { |
+ status = U_MEMORY_ALLOCATION_ERROR; |
+ return; |
+ } |
+ } |
+ // to use hashtable->equals(), must set Value Compartor. |
+ parsedValuesHash->setValueComparator(uhash_compareCaselessUnicodeString); |
+} |
+ |
+void SelectFormat::cleanHashTable() { |
+ if (parsedValuesHash != NULL) { |
+ delete parsedValuesHash; |
+ parsedValuesHash = NULL; |
+ } |
+} |
+ |
+void |
+SelectFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ |
+ pattern = newPattern; |
+ enum State{ startState, keywordState, pastKeywordState, phraseState}; |
+ |
+ //Initialization |
+ UnicodeString keyword ; |
+ UnicodeString phrase ; |
+ UnicodeString* ptrPhrase ; |
+ int32_t braceCount = 0; |
+ |
+ if (parsedValuesHash == NULL) { |
+ initHashTable(status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ } |
+ parsedValuesHash->removeAll(); |
+ parsedValuesHash->setValueDeleter(uhash_deleteUnicodeString); |
+ |
+ //Process the state machine |
+ State state = startState; |
+ for (int32_t i = 0; i < pattern.length(); ++i) { |
+ //Get the character and check its type |
+ UChar ch = pattern.charAt(i); |
+ CharacterClass type = classifyCharacter(ch); |
+ |
+ //Allow any character in phrase but nowhere else |
+ if ( type == tOther ) { |
+ if ( state == phraseState ){ |
+ phrase += ch; |
+ continue; |
+ }else { |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ } |
+ } |
+ |
+ //Process the state machine |
+ switch (state) { |
+ //At the start of pattern |
+ case startState: |
+ switch (type) { |
+ case tSpace: |
+ break; |
+ case tStartKeyword: |
+ state = keywordState; |
+ keyword += ch; |
+ break; |
+ //If anything else is encountered, it's a syntax error |
+ default: |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ }//end of switch(type) |
+ break; |
+ |
+ //Handle the keyword state |
+ case keywordState: |
+ switch (type) { |
+ case tSpace: |
+ state = pastKeywordState; |
+ break; |
+ case tStartKeyword: |
+ case tContinueKeyword: |
+ keyword += ch; |
+ break; |
+ case tLeftBrace: |
+ state = phraseState; |
+ break; |
+ //If anything else is encountered, it's a syntax error |
+ default: |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ }//end of switch(type) |
+ break; |
+ |
+ //Handle the pastkeyword state |
+ case pastKeywordState: |
+ switch (type) { |
+ case tSpace: |
+ break; |
+ case tLeftBrace: |
+ state = phraseState; |
+ break; |
+ //If anything else is encountered, it's a syntax error |
+ default: |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ }//end of switch(type) |
+ break; |
+ |
+ //Handle the phrase state |
+ case phraseState: |
+ switch (type) { |
+ case tLeftBrace: |
+ braceCount++; |
+ phrase += ch; |
+ break; |
+ case tRightBrace: |
+ //Matching keyword, phrase pair found |
+ if (braceCount == 0){ |
+ //Check validity of keyword |
+ if (parsedValuesHash->get(keyword) != NULL) { |
+ status = U_DUPLICATE_KEYWORD; |
+ cleanHashTable(); |
+ return; |
+ } |
+ if (keyword.length() == 0) { |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ } |
+ |
+ //Store the keyword, phrase pair in hashTable |
+ ptrPhrase = new UnicodeString(phrase); |
+ parsedValuesHash->put( keyword, ptrPhrase, status); |
+ |
+ //Reinitialize |
+ keyword.remove(); |
+ phrase.remove(); |
+ ptrPhrase = NULL; |
+ state = startState; |
+ } |
+ |
+ if (braceCount > 0){ |
+ braceCount-- ; |
+ phrase += ch; |
+ } |
+ break; |
+ default: |
+ phrase += ch; |
+ }//end of switch(type) |
+ break; |
+ |
+ //Handle the default case of switch(state) |
+ default: |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ |
+ }//end of switch(state) |
+ } |
+ |
+ //Check if the state machine is back to startState |
+ if ( state != startState){ |
+ status = U_PATTERN_SYNTAX_ERROR; |
+ cleanHashTable(); |
+ return; |
+ } |
+ |
+ //Check if "other" keyword is present |
+ if ( !checkSufficientDefinition() ) { |
+ status = U_DEFAULT_KEYWORD_MISSING; |
+ cleanHashTable(); |
+ } |
+ return; |
+} |
+ |
+UnicodeString& |
+SelectFormat::format(const Formattable& obj, |
+ UnicodeString& appendTo, |
+ FieldPosition& pos, |
+ UErrorCode& status) const |
+{ |
+ switch (obj.getType()) |
+ { |
+ case Formattable::kString: |
+ return format(obj.getString(), appendTo, pos, status); |
+ default: |
+ if( U_SUCCESS(status) ){ |
+ status = U_ILLEGAL_ARGUMENT_ERROR; |
+ } |
+ return appendTo; |
+ } |
+} |
+ |
+UnicodeString& |
+SelectFormat::format(const UnicodeString& keyword, |
+ UnicodeString& appendTo, |
+ FieldPosition& /*pos */, |
+ UErrorCode& status) const { |
+ |
+ if (U_FAILURE(status)) return appendTo; |
+ |
+ if (parsedValuesHash == NULL) { |
+ status = U_INVALID_FORMAT_ERROR; |
+ return appendTo; |
+ } |
+ |
+ //Check for the validity of the keyword |
+ if ( !checkValidKeyword(keyword) ){ |
+ status = U_ILLEGAL_ARGUMENT_ERROR; |
+ return appendTo; |
+ } |
+ |
+ UnicodeString *selectedPattern = (UnicodeString *)parsedValuesHash->get(keyword); |
+ if (selectedPattern == NULL) { |
+ selectedPattern = (UnicodeString *)parsedValuesHash->get(SELECT_KEYWORD_OTHER); |
+ } |
+ |
+ return appendTo += *selectedPattern; |
+} |
+ |
+UnicodeString& |
+SelectFormat::toPattern(UnicodeString& appendTo) { |
+ return appendTo += pattern; |
+} |
+ |
+SelectFormat::CharacterClass |
+SelectFormat::classifyCharacter(UChar ch) const{ |
+ if ((ch >= CAP_A) && (ch <= CAP_Z)) { |
+ return tStartKeyword; |
+ } |
+ if ((ch >= LOW_A) && (ch <= LOW_Z)) { |
+ return tStartKeyword; |
+ } |
+ if ((ch >= U_ZERO) && (ch <= U_NINE)) { |
+ return tContinueKeyword; |
+ } |
+ if ( uprv_isRuleWhiteSpace(ch) ){ |
+ return tSpace; |
+ } |
+ switch (ch) { |
+ case LEFTBRACE: |
+ return tLeftBrace; |
+ case RIGHTBRACE: |
+ return tRightBrace; |
+ case HYPHEN: |
+ case LOWLINE: |
+ return tContinueKeyword; |
+ default : |
+ return tOther; |
+ } |
+} |
+ |
+UBool |
+SelectFormat::checkSufficientDefinition() { |
+ // Check that at least the default rule is defined. |
+ return (parsedValuesHash != NULL && |
+ parsedValuesHash->get(SELECT_KEYWORD_OTHER) != NULL) ; |
+} |
+ |
+UBool |
+SelectFormat::checkValidKeyword(const UnicodeString& argKeyword ) const{ |
+ int32_t len = argKeyword.length(); |
+ if (len < 1){ |
+ return FALSE; |
+ } |
+ CharacterClass type = classifyCharacter(argKeyword.charAt(0)); |
+ if( type != tStartKeyword ){ |
+ return FALSE; |
+ } |
+ |
+ for (int32_t i = 0; i < argKeyword.length(); ++i) { |
+ type = classifyCharacter(argKeyword.charAt(i)); |
+ if( type != tStartKeyword && type != tContinueKeyword ){ |
+ return FALSE; |
+ } |
+ } |
+ return TRUE; |
+} |
+ |
+Format* SelectFormat::clone() const |
+{ |
+ return new SelectFormat(*this); |
+} |
+ |
+SelectFormat& |
+SelectFormat::operator=(const SelectFormat& other) { |
+ if (this != &other) { |
+ UErrorCode status = U_ZERO_ERROR; |
+ pattern = other.pattern; |
+ copyHashtable(other.parsedValuesHash, status); |
+ } |
+ return *this; |
+} |
+ |
+UBool |
+SelectFormat::operator==(const Format& other) const { |
+ if( this == &other){ |
+ return TRUE; |
+ } |
+ if (typeid(*this) != typeid(other)) { |
+ return FALSE; |
+ } |
+ SelectFormat* fmt = (SelectFormat*)&other; |
+ Hashtable* hashOther = fmt->parsedValuesHash; |
+ if ( parsedValuesHash == NULL && hashOther == NULL) |
+ return TRUE; |
+ if ( parsedValuesHash == NULL || hashOther == NULL) |
+ return FALSE; |
+ return parsedValuesHash->equals(*hashOther); |
+} |
+ |
+UBool |
+SelectFormat::operator!=(const Format& other) const { |
+ return !operator==(other); |
+} |
+ |
+void |
+SelectFormat::parseObject(const UnicodeString& /*source*/, |
+ Formattable& /*result*/, |
+ ParsePosition& pos) const |
+{ |
+ // TODO: not yet supported in icu4j and icu4c |
+ pos.setErrorIndex(pos.getIndex()); |
+} |
+ |
+void |
+SelectFormat::copyHashtable(Hashtable *other, UErrorCode& status) { |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ if (other == NULL) { |
+ cleanHashTable(); |
+ return; |
+ } |
+ if (parsedValuesHash == NULL) { |
+ initHashTable(status); |
+ if (U_FAILURE(status)) { |
+ return; |
+ } |
+ } |
+ |
+ parsedValuesHash->removeAll(); |
+ parsedValuesHash->setValueDeleter(uhash_deleteUnicodeString); |
+ |
+ 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; |
+ parsedValuesHash->put(*otherKey, new UnicodeString(*otherValue), status); |
+ if (U_FAILURE(status)){ |
+ cleanHashTable(); |
+ return; |
+ } |
+ } |
+} |
+ |
+U_NAMESPACE_END |
+ |
+#endif /* #if !UCONFIG_NO_FORMATTING */ |
+ |
+//eof |
Property changes on: icu46/source/i18n/selfmt.cpp |
___________________________________________________________________ |
Added: svn:executable |
+ * |
Added: svn:eol-style |
+ LF |