1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 1997-2012, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  * Copyright (C) 2010 , Yahoo! Inc.
8  ********************************************************************
9  *
10  * File SELFMT.CPP
11  *
12  * Modification History:
13  *
14  *   Date        Name        Description
15  *   11/11/09    kirtig      Finished first cut of implementation.
16  *   11/16/09    kirtig      Improved version
17  ********************************************************************/
18 
19 #include "utypeinfo.h"  // for 'typeid' to work
20 
21 #include "unicode/messagepattern.h"
22 #include "unicode/rbnf.h"
23 #include "unicode/selfmt.h"
24 #include "unicode/uchar.h"
25 #include "unicode/ucnv_err.h"
26 #include "unicode/umsg.h"
27 #include "unicode/ustring.h"
28 #include "unicode/utypes.h"
29 #include "cmemory.h"
30 #include "messageimpl.h"
31 #include "patternprops.h"
32 #include "selfmtimpl.h"
33 #include "uassert.h"
34 #include "ustrfmt.h"
35 #include "util.h"
36 #include "uvector.h"
37 
38 #if !UCONFIG_NO_FORMATTING
39 
40 U_NAMESPACE_BEGIN
41 
42 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SelectFormat)
43 
44 static const UChar SELECT_KEYWORD_OTHER[] = {LOW_O, LOW_T, LOW_H, LOW_E, LOW_R, 0};
45 
SelectFormat(const UnicodeString & pat,UErrorCode & status)46 SelectFormat::SelectFormat(const UnicodeString& pat,
47                            UErrorCode& status) : msgPattern(status) {
48    applyPattern(pat, status);
49 }
50 
SelectFormat(const SelectFormat & other)51 SelectFormat::SelectFormat(const SelectFormat& other) : Format(other),
52                                                         msgPattern(other.msgPattern) {
53 }
54 
~SelectFormat()55 SelectFormat::~SelectFormat() {
56 }
57 
58 void
applyPattern(const UnicodeString & newPattern,UErrorCode & status)59 SelectFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
60     if (U_FAILURE(status)) {
61       return;
62     }
63 
64     msgPattern.parseSelectStyle(newPattern, NULL, status);
65     if (U_FAILURE(status)) {
66         msgPattern.clear();
67     }
68 }
69 
70 UnicodeString&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const71 SelectFormat::format(const Formattable& obj,
72                    UnicodeString& appendTo,
73                    FieldPosition& pos,
74                    UErrorCode& status) const
75 {
76     if (U_FAILURE(status)) {
77         return appendTo;
78     }
79     if (obj.getType() == Formattable::kString) {
80         return format(obj.getString(status), appendTo, pos, status);
81     } else {
82         status = U_ILLEGAL_ARGUMENT_ERROR;
83         return appendTo;
84     }
85 }
86 
87 UnicodeString&
format(const UnicodeString & keyword,UnicodeString & appendTo,FieldPosition &,UErrorCode & status) const88 SelectFormat::format(const UnicodeString& keyword,
89                      UnicodeString& appendTo,
90                      FieldPosition& /*pos */,
91                      UErrorCode& status) const {
92     if (U_FAILURE(status)) {
93         return appendTo;
94     }
95     // Check for the validity of the keyword
96     if (!PatternProps::isIdentifier(keyword.getBuffer(), keyword.length())) {
97         status = U_ILLEGAL_ARGUMENT_ERROR;  // Invalid formatting argument.
98     }
99     if (msgPattern.countParts() == 0) {
100         status = U_INVALID_STATE_ERROR;
101         return appendTo;
102     }
103     int32_t msgStart = findSubMessage(msgPattern, 0, keyword, status);
104     if (!MessageImpl::jdkAposMode(msgPattern)) {
105         int32_t patternStart = msgPattern.getPart(msgStart).getLimit();
106         int32_t msgLimit = msgPattern.getLimitPartIndex(msgStart);
107         appendTo.append(msgPattern.getPatternString(),
108                         patternStart,
109                         msgPattern.getPatternIndex(msgLimit) - patternStart);
110         return appendTo;
111     }
112     // JDK compatibility mode: Remove SKIP_SYNTAX.
113     return MessageImpl::appendSubMessageWithoutSkipSyntax(msgPattern, msgStart, appendTo);
114 }
115 
116 UnicodeString&
toPattern(UnicodeString & appendTo)117 SelectFormat::toPattern(UnicodeString& appendTo) {
118     if (0 == msgPattern.countParts()) {
119         appendTo.setToBogus();
120     } else {
121         appendTo.append(msgPattern.getPatternString());
122     }
123     return appendTo;
124 }
125 
126 
findSubMessage(const MessagePattern & pattern,int32_t partIndex,const UnicodeString & keyword,UErrorCode & ec)127 int32_t SelectFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
128                                      const UnicodeString& keyword, UErrorCode& ec) {
129     if (U_FAILURE(ec)) {
130         return 0;
131     }
132     UnicodeString other(FALSE, SELECT_KEYWORD_OTHER, 5);
133     int32_t count = pattern.countParts();
134     int32_t msgStart=0;
135     // Iterate over (ARG_SELECTOR, message) pairs until ARG_LIMIT or end of select-only pattern.
136     do {
137         const MessagePattern::Part& part=pattern.getPart(partIndex++);
138         const UMessagePatternPartType type=part.getType();
139         if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
140             break;
141         }
142         // part is an ARG_SELECTOR followed by a message
143         if(pattern.partSubstringMatches(part, keyword)) {
144             // keyword matches
145             return partIndex;
146         } else if(msgStart==0 && pattern.partSubstringMatches(part, other)) {
147             msgStart=partIndex;
148         }
149         partIndex=pattern.getLimitPartIndex(partIndex);
150     } while(++partIndex<count);
151     return msgStart;
152 }
153 
clone() const154 SelectFormat* SelectFormat::clone() const
155 {
156     return new SelectFormat(*this);
157 }
158 
159 SelectFormat&
operator =(const SelectFormat & other)160 SelectFormat::operator=(const SelectFormat& other) {
161     if (this != &other) {
162         msgPattern = other.msgPattern;
163     }
164     return *this;
165 }
166 
167 UBool
operator ==(const Format & other) const168 SelectFormat::operator==(const Format& other) const {
169     if (this == &other) {
170         return TRUE;
171     }
172     if (!Format::operator==(other)) {
173         return FALSE;
174     }
175     const SelectFormat& o = (const SelectFormat&)other;
176     return msgPattern == o.msgPattern;
177 }
178 
179 UBool
operator !=(const Format & other) const180 SelectFormat::operator!=(const Format& other) const {
181     return  !operator==(other);
182 }
183 
184 void
parseObject(const UnicodeString &,Formattable &,ParsePosition & pos) const185 SelectFormat::parseObject(const UnicodeString& /*source*/,
186                         Formattable& /*result*/,
187                         ParsePosition& pos) const
188 {
189     // Parsing not supported.
190     pos.setErrorIndex(pos.getIndex());
191 }
192 
193 U_NAMESPACE_END
194 
195 #endif /* #if !UCONFIG_NO_FORMATTING */
196 
197 //eof
198