1 /********************************************************************
3 * Copyright (c) 1997-2010, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 * Copyright (C) 2010 , Yahoo! Inc.
6 ********************************************************************
10 * Modification History:
12 * Date Name Description
13 * 11/11/09 kirtig Finished first cut of implementation.
14 * 11/16/09 kirtig Improved version
15 ********************************************************************/
17 #include <typeinfo> // for 'typeid' to work
19 #include "unicode/utypes.h"
20 #include "unicode/ustring.h"
21 #include "unicode/ucnv_err.h"
22 #include "unicode/uchar.h"
23 #include "unicode/umsg.h"
24 #include "unicode/rbnf.h"
31 #include "unicode/selfmt.h"
32 #include "selfmtimpl.h"
34 #if !UCONFIG_NO_FORMATTING
38 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SelectFormat
)
40 #define MAX_KEYWORD_SIZE 30
41 static const UChar SELECT_KEYWORD_OTHER
[] = {LOW_O
, LOW_T
, LOW_H
, LOW_E
, LOW_R
, 0};
43 SelectFormat::SelectFormat(const UnicodeString
& pat
, UErrorCode
& status
) : parsedValuesHash(NULL
) {
44 if (U_FAILURE(status
)) {
47 initHashTable(status
);
48 applyPattern(pat
, status
);
51 SelectFormat::SelectFormat(const SelectFormat
& other
) : Format(other
), parsedValuesHash(NULL
) {
52 UErrorCode status
= U_ZERO_ERROR
;
53 pattern
= other
.pattern
;
54 copyHashtable(other
.parsedValuesHash
, status
);
57 SelectFormat::~SelectFormat() {
61 void SelectFormat::initHashTable(UErrorCode
&status
) {
62 if (U_FAILURE(status
)) {
66 if (parsedValuesHash
!= NULL
) {
70 parsedValuesHash
= new Hashtable(TRUE
, status
);
71 if (U_FAILURE(status
)) {
75 if (parsedValuesHash
== NULL
) {
76 status
= U_MEMORY_ALLOCATION_ERROR
;
80 // to use hashtable->equals(), must set Value Compartor.
81 parsedValuesHash
->setValueComparator(uhash_compareCaselessUnicodeString
);
84 void SelectFormat::cleanHashTable() {
85 if (parsedValuesHash
!= NULL
) {
86 delete parsedValuesHash
;
87 parsedValuesHash
= NULL
;
92 SelectFormat::applyPattern(const UnicodeString
& newPattern
, UErrorCode
& status
) {
93 if (U_FAILURE(status
)) {
98 enum State
{ startState
, keywordState
, pastKeywordState
, phraseState
};
101 UnicodeString keyword
;
102 UnicodeString phrase
;
103 UnicodeString
* ptrPhrase
;
104 int32_t braceCount
= 0;
106 if (parsedValuesHash
== NULL
) {
107 initHashTable(status
);
108 if (U_FAILURE(status
)) {
112 parsedValuesHash
->removeAll();
113 parsedValuesHash
->setValueDeleter(uhash_deleteUnicodeString
);
115 //Process the state machine
116 State state
= startState
;
117 for (int32_t i
= 0; i
< pattern
.length(); ++i
) {
118 //Get the character and check its type
119 UChar ch
= pattern
.charAt(i
);
120 CharacterClass type
= classifyCharacter(ch
);
122 //Allow any character in phrase but nowhere else
123 if ( type
== tOther
) {
124 if ( state
== phraseState
){
128 status
= U_PATTERN_SYNTAX_ERROR
;
134 //Process the state machine
136 //At the start of pattern
142 state
= keywordState
;
145 //If anything else is encountered, it's a syntax error
147 status
= U_PATTERN_SYNTAX_ERROR
;
150 }//end of switch(type)
153 //Handle the keyword state
157 state
= pastKeywordState
;
160 case tContinueKeyword
:
166 //If anything else is encountered, it's a syntax error
168 status
= U_PATTERN_SYNTAX_ERROR
;
171 }//end of switch(type)
174 //Handle the pastkeyword state
175 case pastKeywordState
:
182 //If anything else is encountered, it's a syntax error
184 status
= U_PATTERN_SYNTAX_ERROR
;
187 }//end of switch(type)
190 //Handle the phrase state
198 //Matching keyword, phrase pair found
199 if (braceCount
== 0){
200 //Check validity of keyword
201 if (parsedValuesHash
->get(keyword
) != NULL
) {
202 status
= U_DUPLICATE_KEYWORD
;
206 if (keyword
.length() == 0) {
207 status
= U_PATTERN_SYNTAX_ERROR
;
212 //Store the keyword, phrase pair in hashTable
213 ptrPhrase
= new UnicodeString(phrase
);
214 parsedValuesHash
->put( keyword
, ptrPhrase
, status
);
230 }//end of switch(type)
233 //Handle the default case of switch(state)
235 status
= U_PATTERN_SYNTAX_ERROR
;
239 }//end of switch(state)
242 //Check if the state machine is back to startState
243 if ( state
!= startState
){
244 status
= U_PATTERN_SYNTAX_ERROR
;
249 //Check if "other" keyword is present
250 if ( !checkSufficientDefinition() ) {
251 status
= U_DEFAULT_KEYWORD_MISSING
;
258 SelectFormat::format(const Formattable
& obj
,
259 UnicodeString
& appendTo
,
261 UErrorCode
& status
) const
263 switch (obj
.getType())
265 case Formattable::kString
:
266 return format(obj
.getString(), appendTo
, pos
, status
);
268 if( U_SUCCESS(status
) ){
269 status
= U_ILLEGAL_ARGUMENT_ERROR
;
276 SelectFormat::format(const UnicodeString
& keyword
,
277 UnicodeString
& appendTo
,
278 FieldPosition
& /*pos */,
279 UErrorCode
& status
) const {
281 if (U_FAILURE(status
)) return appendTo
;
283 if (parsedValuesHash
== NULL
) {
284 status
= U_INVALID_FORMAT_ERROR
;
288 //Check for the validity of the keyword
289 if ( !checkValidKeyword(keyword
) ){
290 status
= U_ILLEGAL_ARGUMENT_ERROR
;
294 UnicodeString
*selectedPattern
= (UnicodeString
*)parsedValuesHash
->get(keyword
);
295 if (selectedPattern
== NULL
) {
296 selectedPattern
= (UnicodeString
*)parsedValuesHash
->get(SELECT_KEYWORD_OTHER
);
299 return appendTo
+= *selectedPattern
;
303 SelectFormat::toPattern(UnicodeString
& appendTo
) {
304 return appendTo
+= pattern
;
307 SelectFormat::CharacterClass
308 SelectFormat::classifyCharacter(UChar ch
) const{
309 if ((ch
>= CAP_A
) && (ch
<= CAP_Z
)) {
310 return tStartKeyword
;
312 if ((ch
>= LOW_A
) && (ch
<= LOW_Z
)) {
313 return tStartKeyword
;
315 if ((ch
>= U_ZERO
) && (ch
<= U_NINE
)) {
316 return tContinueKeyword
;
318 if ( uprv_isRuleWhiteSpace(ch
) ){
328 return tContinueKeyword
;
335 SelectFormat::checkSufficientDefinition() {
336 // Check that at least the default rule is defined.
337 return (parsedValuesHash
!= NULL
&&
338 parsedValuesHash
->get(SELECT_KEYWORD_OTHER
) != NULL
) ;
342 SelectFormat::checkValidKeyword(const UnicodeString
& argKeyword
) const{
343 int32_t len
= argKeyword
.length();
347 CharacterClass type
= classifyCharacter(argKeyword
.charAt(0));
348 if( type
!= tStartKeyword
){
352 for (int32_t i
= 0; i
< argKeyword
.length(); ++i
) {
353 type
= classifyCharacter(argKeyword
.charAt(i
));
354 if( type
!= tStartKeyword
&& type
!= tContinueKeyword
){
361 Format
* SelectFormat::clone() const
363 return new SelectFormat(*this);
367 SelectFormat::operator=(const SelectFormat
& other
) {
368 if (this != &other
) {
369 UErrorCode status
= U_ZERO_ERROR
;
370 pattern
= other
.pattern
;
371 copyHashtable(other
.parsedValuesHash
, status
);
377 SelectFormat::operator==(const Format
& other
) const {
381 if (typeid(*this) != typeid(other
)) {
384 SelectFormat
* fmt
= (SelectFormat
*)&other
;
385 Hashtable
* hashOther
= fmt
->parsedValuesHash
;
386 if ( parsedValuesHash
== NULL
&& hashOther
== NULL
)
388 if ( parsedValuesHash
== NULL
|| hashOther
== NULL
)
390 return parsedValuesHash
->equals(*hashOther
);
394 SelectFormat::operator!=(const Format
& other
) const {
395 return !operator==(other
);
399 SelectFormat::parseObject(const UnicodeString
& /*source*/,
400 Formattable
& /*result*/,
401 ParsePosition
& pos
) const
403 // TODO: not yet supported in icu4j and icu4c
404 pos
.setErrorIndex(pos
.getIndex());
408 SelectFormat::copyHashtable(Hashtable
*other
, UErrorCode
& status
) {
409 if (U_FAILURE(status
)) {
416 if (parsedValuesHash
== NULL
) {
417 initHashTable(status
);
418 if (U_FAILURE(status
)) {
423 parsedValuesHash
->removeAll();
424 parsedValuesHash
->setValueDeleter(uhash_deleteUnicodeString
);
427 const UHashElement
* elem
= NULL
;
429 // walk through the hash table and create a deep clone
430 while ((elem
= other
->nextElement(pos
)) != NULL
){
431 const UHashTok otherKeyTok
= elem
->key
;
432 UnicodeString
* otherKey
= (UnicodeString
*)otherKeyTok
.pointer
;
433 const UHashTok otherKeyToVal
= elem
->value
;
434 UnicodeString
* otherValue
= (UnicodeString
*)otherKeyToVal
.pointer
;
435 parsedValuesHash
->put(*otherKey
, new UnicodeString(*otherValue
), status
);
436 if (U_FAILURE(status
)){
445 #endif /* #if !UCONFIG_NO_FORMATTING */