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