2010-01-19 19:25:25 +00:00
|
|
|
/********************************************************************
|
|
|
|
* 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
|
|
|
|
********************************************************************/
|
|
|
|
|
2010-05-19 17:29:33 +00:00
|
|
|
#include <typeinfo> // for 'typeid' to work
|
|
|
|
|
2010-01-19 19:25:25 +00:00
|
|
|
#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) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
init(status);
|
|
|
|
applyPattern(pat, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectFormat::SelectFormat(const SelectFormat& other) : Format(other) {
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
|
pattern = other.pattern;
|
|
|
|
copyHashtable(other.parsedValuesHash, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectFormat::~SelectFormat() {
|
|
|
|
delete parsedValuesHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SelectFormat::init(UErrorCode& status) {
|
2010-01-30 03:46:13 +00:00
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
2010-01-19 19:25:25 +00:00
|
|
|
parsedValuesHash = NULL;
|
|
|
|
pattern.remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
SelectFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->pattern = newPattern;
|
|
|
|
enum State{ startState, keywordState, pastKeywordState, phraseState};
|
|
|
|
|
|
|
|
//Initialization
|
2010-01-30 03:46:13 +00:00
|
|
|
UnicodeString keyword ;
|
|
|
|
UnicodeString phrase ;
|
2010-01-19 19:25:25 +00:00
|
|
|
UnicodeString* ptrPhrase ;
|
|
|
|
int32_t braceCount = 0;
|
|
|
|
|
2010-01-30 03:46:13 +00:00
|
|
|
delete parsedValuesHash;
|
|
|
|
this->parsedValuesHash = NULL;
|
|
|
|
parsedValuesHash = new Hashtable(TRUE, status);
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
2010-02-25 19:19:53 +00:00
|
|
|
parsedValuesHash->setValueDeleter(uhash_deleteUnicodeString);
|
2010-01-19 19:25:25 +00:00
|
|
|
|
|
|
|
//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);
|
2010-02-01 23:32:57 +00:00
|
|
|
CharacterClass type = classifyCharacter(ch);
|
2010-01-19 19:25:25 +00:00
|
|
|
|
|
|
|
//Allow any character in phrase but nowhere else
|
|
|
|
if ( type == tOther ) {
|
|
|
|
if ( state == phraseState ){
|
|
|
|
phrase += ch;
|
|
|
|
continue;
|
|
|
|
}else {
|
|
|
|
status = U_PATTERN_SYNTAX_ERROR;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (keyword.length() == 0) {
|
|
|
|
status = U_PATTERN_SYNTAX_ERROR;
|
|
|
|
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;
|
|
|
|
return;
|
|
|
|
|
|
|
|
}//end of switch(state)
|
|
|
|
}
|
|
|
|
|
2010-01-30 04:48:30 +00:00
|
|
|
//Check if the state machine is back to startState
|
2010-01-19 19:25:25 +00:00
|
|
|
if ( state != startState){
|
|
|
|
status = U_PATTERN_SYNTAX_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check if "other" keyword is present
|
|
|
|
if ( !checkSufficientDefinition() ) {
|
|
|
|
status = U_DEFAULT_KEYWORD_MISSING;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString&
|
|
|
|
SelectFormat::format(const Formattable& obj,
|
|
|
|
UnicodeString& appendTo,
|
|
|
|
FieldPosition& pos,
|
|
|
|
UErrorCode& status) const
|
|
|
|
{
|
|
|
|
switch (obj.getType())
|
|
|
|
{
|
|
|
|
case Formattable::kString:
|
2010-01-30 04:48:30 +00:00
|
|
|
return format(obj.getString(), appendTo, pos, status);
|
2010-01-19 19:25:25 +00:00
|
|
|
default:
|
2010-02-01 23:32:57 +00:00
|
|
|
if( U_SUCCESS(status) ){
|
|
|
|
status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
|
|
}
|
2010-01-19 19:25:25 +00:00
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString&
|
2010-02-01 23:32:57 +00:00
|
|
|
SelectFormat::format(const UnicodeString& keyword,
|
2010-01-19 19:25:25 +00:00
|
|
|
UnicodeString& appendTo,
|
2010-07-12 18:03:29 +00:00
|
|
|
FieldPosition& /*pos */,
|
2010-01-19 19:25:25 +00:00
|
|
|
UErrorCode& status) const {
|
|
|
|
|
2010-02-01 23:32:57 +00:00
|
|
|
if (U_FAILURE(status)) return appendTo;
|
|
|
|
|
2010-01-19 19:25:25 +00:00
|
|
|
//Check for the validity of the keyword
|
2010-02-01 23:32:57 +00:00
|
|
|
if ( !checkValidKeyword(keyword) ){
|
2010-01-19 19:25:25 +00:00
|
|
|
status = U_ILLEGAL_ARGUMENT_ERROR;
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsedValuesHash == NULL) {
|
|
|
|
status = U_INVALID_FORMAT_ERROR;
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
2010-02-01 23:32:57 +00:00
|
|
|
UnicodeString *selectedPattern = (UnicodeString *)parsedValuesHash->get(keyword);
|
2010-01-19 19:25:25 +00:00
|
|
|
if (selectedPattern == NULL) {
|
|
|
|
selectedPattern = (UnicodeString *)parsedValuesHash->get(SELECT_KEYWORD_OTHER);
|
|
|
|
}
|
|
|
|
|
|
|
|
return appendTo += *selectedPattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString&
|
|
|
|
SelectFormat::toPattern(UnicodeString& appendTo) {
|
2010-01-30 04:48:30 +00:00
|
|
|
return appendTo += pattern;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
|
2010-02-01 23:32:57 +00:00
|
|
|
SelectFormat::CharacterClass
|
|
|
|
SelectFormat::classifyCharacter(UChar ch) const{
|
2010-01-19 19:25:25 +00:00
|
|
|
if ((ch >= CAP_A) && (ch <= CAP_Z)) {
|
2010-02-01 23:32:57 +00:00
|
|
|
return tStartKeyword;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
if ((ch >= LOW_A) && (ch <= LOW_Z)) {
|
2010-02-01 23:32:57 +00:00
|
|
|
return tStartKeyword;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
if ((ch >= U_ZERO) && (ch <= U_NINE)) {
|
2010-02-01 23:32:57 +00:00
|
|
|
return tContinueKeyword;
|
|
|
|
}
|
|
|
|
if ( uprv_isRuleWhiteSpace(ch) ){
|
|
|
|
return tSpace;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
switch (ch) {
|
|
|
|
case LEFTBRACE:
|
2010-02-01 23:32:57 +00:00
|
|
|
return tLeftBrace;
|
2010-01-19 19:25:25 +00:00
|
|
|
case RIGHTBRACE:
|
2010-02-01 23:32:57 +00:00
|
|
|
return tRightBrace;
|
2010-01-19 19:25:25 +00:00
|
|
|
case HYPHEN:
|
|
|
|
case LOWLINE:
|
2010-02-01 23:32:57 +00:00
|
|
|
return tContinueKeyword;
|
2010-01-19 19:25:25 +00:00
|
|
|
default :
|
2010-02-01 23:32:57 +00:00
|
|
|
return tOther;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
SelectFormat::checkSufficientDefinition() {
|
|
|
|
// Check that at least the default rule is defined.
|
2010-01-30 04:48:30 +00:00
|
|
|
return (parsedValuesHash != NULL &&
|
|
|
|
parsedValuesHash->get(SELECT_KEYWORD_OTHER) != NULL) ;
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
SelectFormat::checkValidKeyword(const UnicodeString& argKeyword ) const{
|
2010-01-30 00:11:31 +00:00
|
|
|
int32_t len = argKeyword.length();
|
|
|
|
if (len < 1){
|
|
|
|
return FALSE;
|
|
|
|
}
|
2010-02-01 23:32:57 +00:00
|
|
|
CharacterClass type = classifyCharacter(argKeyword.charAt(0));
|
2010-01-30 00:11:31 +00:00
|
|
|
if( type != tStartKeyword ){
|
|
|
|
return FALSE;
|
|
|
|
}
|
2010-01-19 19:25:25 +00:00
|
|
|
|
|
|
|
for (int32_t i = 0; i < argKeyword.length(); ++i) {
|
2010-02-01 23:32:57 +00:00
|
|
|
type = classifyCharacter(argKeyword.charAt(i));
|
2010-01-30 00:11:31 +00:00
|
|
|
if( type != tStartKeyword && type != tContinueKeyword ){
|
2010-01-19 19:25:25 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2010-01-30 00:11:31 +00:00
|
|
|
}
|
2010-01-19 19:25:25 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
Format* SelectFormat::clone() const
|
|
|
|
{
|
|
|
|
return new SelectFormat(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectFormat&
|
|
|
|
SelectFormat::operator=(const SelectFormat& other) {
|
|
|
|
if (this != &other) {
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
|
|
delete parsedValuesHash;
|
|
|
|
pattern = other.pattern;
|
|
|
|
copyHashtable(other.parsedValuesHash, status);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
SelectFormat::operator==(const Format& other) const {
|
2010-02-01 23:32:57 +00:00
|
|
|
if( this == &other){
|
|
|
|
return TRUE;
|
|
|
|
}
|
2010-05-19 17:29:33 +00:00
|
|
|
if (typeid(*this) != typeid(other)) {
|
2010-02-01 23:32:57 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2010-01-19 19:25:25 +00:00
|
|
|
SelectFormat* fmt = (SelectFormat*)&other;
|
|
|
|
Hashtable* hashOther = fmt->parsedValuesHash;
|
|
|
|
if ( parsedValuesHash == NULL && hashOther == NULL)
|
|
|
|
return TRUE;
|
|
|
|
if ( parsedValuesHash == NULL || hashOther == NULL)
|
|
|
|
return FALSE;
|
|
|
|
if ( hashOther->count() != parsedValuesHash->count() ){
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
const UHashElement* elem = NULL;
|
|
|
|
int32_t pos = -1;
|
|
|
|
while ((elem = hashOther->nextElement(pos)) != NULL) {
|
|
|
|
const UHashTok otherKeyTok = elem->key;
|
|
|
|
UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
|
|
|
|
const UHashTok otherKeyToVal = elem->value;
|
|
|
|
UnicodeString* otherValue = (UnicodeString*)otherKeyToVal.pointer;
|
|
|
|
|
|
|
|
UnicodeString* thisElemValue = (UnicodeString*)parsedValuesHash->get(*otherKey);
|
|
|
|
if ( thisElemValue == NULL ){
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if ( *thisElemValue != *otherValue){
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
pos = -1;
|
|
|
|
while ((elem = parsedValuesHash->nextElement(pos)) != NULL) {
|
|
|
|
const UHashTok thisKeyTok = elem->key;
|
|
|
|
UnicodeString* thisKey = (UnicodeString*)thisKeyTok.pointer;
|
|
|
|
const UHashTok thisKeyToVal = elem->value;
|
|
|
|
UnicodeString* thisValue = (UnicodeString*)thisKeyToVal.pointer;
|
|
|
|
|
|
|
|
UnicodeString* otherElemValue = (UnicodeString*)hashOther->get(*thisKey);
|
|
|
|
if ( otherElemValue == NULL ){
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if ( *otherElemValue != *thisValue){
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBool
|
|
|
|
SelectFormat::operator!=(const Format& other) const {
|
|
|
|
return !operator==(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SelectFormat::parseObject(const UnicodeString& /*source*/,
|
|
|
|
Formattable& /*result*/,
|
2010-02-01 23:32:57 +00:00
|
|
|
ParsePosition& pos) const
|
2010-01-19 19:25:25 +00:00
|
|
|
{
|
|
|
|
// TODO: not yet supported in icu4j and icu4c
|
2010-02-01 23:32:57 +00:00
|
|
|
pos.setErrorIndex(pos.getIndex());
|
2010-01-19 19:25:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SelectFormat::copyHashtable(Hashtable *other, UErrorCode& status) {
|
|
|
|
if (other == NULL) {
|
|
|
|
parsedValuesHash = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
parsedValuesHash = new Hashtable(TRUE, status);
|
|
|
|
if (U_FAILURE(status)){
|
|
|
|
return;
|
|
|
|
}
|
2010-03-12 16:28:01 +00:00
|
|
|
parsedValuesHash->setValueDeleter(uhash_deleteUnicodeString);
|
2010-01-19 19:25:25 +00:00
|
|
|
|
|
|
|
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)){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
U_NAMESPACE_END
|
|
|
|
|
|
|
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
|
|
|
|
|
|
//eof
|