1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 * Copyright (C) 2014-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 ******************************************************************************
8 * quantityformatter.cpp
9 */
10
11 #include "unicode/utypes.h"
12
13 #if !UCONFIG_NO_FORMATTING
14
15 #include "unicode/simpleformatter.h"
16 #include "quantityformatter.h"
17 #include "uassert.h"
18 #include "unicode/unistr.h"
19 #include "unicode/decimfmt.h"
20 #include "cstring.h"
21 #include "unicode/plurrule.h"
22 #include "charstr.h"
23 #include "unicode/fmtable.h"
24 #include "unicode/fieldpos.h"
25 #include "standardplural.h"
26 #include "visibledigits.h"
27 #include "uassert.h"
28
29 U_NAMESPACE_BEGIN
30
QuantityFormatter()31 QuantityFormatter::QuantityFormatter() {
32 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
33 formatters[i] = NULL;
34 }
35 }
36
QuantityFormatter(const QuantityFormatter & other)37 QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
38 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
39 if (other.formatters[i] == NULL) {
40 formatters[i] = NULL;
41 } else {
42 formatters[i] = new SimpleFormatter(*other.formatters[i]);
43 }
44 }
45 }
46
operator =(const QuantityFormatter & other)47 QuantityFormatter &QuantityFormatter::operator=(
48 const QuantityFormatter& other) {
49 if (this == &other) {
50 return *this;
51 }
52 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
53 delete formatters[i];
54 if (other.formatters[i] == NULL) {
55 formatters[i] = NULL;
56 } else {
57 formatters[i] = new SimpleFormatter(*other.formatters[i]);
58 }
59 }
60 return *this;
61 }
62
~QuantityFormatter()63 QuantityFormatter::~QuantityFormatter() {
64 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
65 delete formatters[i];
66 }
67 }
68
reset()69 void QuantityFormatter::reset() {
70 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
71 delete formatters[i];
72 formatters[i] = NULL;
73 }
74 }
75
addIfAbsent(const char * variant,const UnicodeString & rawPattern,UErrorCode & status)76 UBool QuantityFormatter::addIfAbsent(
77 const char *variant,
78 const UnicodeString &rawPattern,
79 UErrorCode &status) {
80 int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
81 if (U_FAILURE(status)) {
82 return FALSE;
83 }
84 if (formatters[pluralIndex] != NULL) {
85 return TRUE;
86 }
87 SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status);
88 if (newFmt == NULL) {
89 status = U_MEMORY_ALLOCATION_ERROR;
90 return FALSE;
91 }
92 if (U_FAILURE(status)) {
93 delete newFmt;
94 return FALSE;
95 }
96 formatters[pluralIndex] = newFmt;
97 return TRUE;
98 }
99
isValid() const100 UBool QuantityFormatter::isValid() const {
101 return formatters[StandardPlural::OTHER] != NULL;
102 }
103
getByVariant(const char * variant) const104 const SimpleFormatter *QuantityFormatter::getByVariant(
105 const char *variant) const {
106 U_ASSERT(isValid());
107 int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
108 const SimpleFormatter *pattern = formatters[pluralIndex];
109 if (pattern == NULL) {
110 pattern = formatters[StandardPlural::OTHER];
111 }
112 return pattern;
113 }
114
format(const Formattable & number,const NumberFormat & fmt,const PluralRules & rules,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const115 UnicodeString &QuantityFormatter::format(
116 const Formattable &number,
117 const NumberFormat &fmt,
118 const PluralRules &rules,
119 UnicodeString &appendTo,
120 FieldPosition &pos,
121 UErrorCode &status) const {
122 UnicodeString formattedNumber;
123 StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
124 if (U_FAILURE(status)) {
125 return appendTo;
126 }
127 const SimpleFormatter *pattern = formatters[p];
128 if (pattern == NULL) {
129 pattern = formatters[StandardPlural::OTHER];
130 if (pattern == NULL) {
131 status = U_INVALID_STATE_ERROR;
132 return appendTo;
133 }
134 }
135 return format(*pattern, formattedNumber, appendTo, pos, status);
136 }
137
138 // The following methods live here so that class PluralRules does not depend on number formatting,
139 // and the SimpleFormatter does not depend on FieldPosition.
140
selectPlural(const Formattable & number,const NumberFormat & fmt,const PluralRules & rules,UnicodeString & formattedNumber,FieldPosition & pos,UErrorCode & status)141 StandardPlural::Form QuantityFormatter::selectPlural(
142 const Formattable &number,
143 const NumberFormat &fmt,
144 const PluralRules &rules,
145 UnicodeString &formattedNumber,
146 FieldPosition &pos,
147 UErrorCode &status) {
148 if (U_FAILURE(status)) {
149 return StandardPlural::OTHER;
150 }
151 UnicodeString pluralKeyword;
152 VisibleDigitsWithExponent digits;
153 const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
154 if (decFmt != NULL) {
155 decFmt->initVisibleDigitsWithExponent(number, digits, status);
156 if (U_FAILURE(status)) {
157 return StandardPlural::OTHER;
158 }
159 pluralKeyword = rules.select(digits);
160 decFmt->format(digits, formattedNumber, pos, status);
161 } else {
162 if (number.getType() == Formattable::kDouble) {
163 pluralKeyword = rules.select(number.getDouble());
164 } else if (number.getType() == Formattable::kLong) {
165 pluralKeyword = rules.select(number.getLong());
166 } else if (number.getType() == Formattable::kInt64) {
167 pluralKeyword = rules.select((double) number.getInt64());
168 } else {
169 status = U_ILLEGAL_ARGUMENT_ERROR;
170 return StandardPlural::OTHER;
171 }
172 fmt.format(number, formattedNumber, pos, status);
173 }
174 return StandardPlural::orOtherFromString(pluralKeyword);
175 }
176
format(const SimpleFormatter & pattern,const UnicodeString & value,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status)177 UnicodeString &QuantityFormatter::format(
178 const SimpleFormatter &pattern,
179 const UnicodeString &value,
180 UnicodeString &appendTo,
181 FieldPosition &pos,
182 UErrorCode &status) {
183 if (U_FAILURE(status)) {
184 return appendTo;
185 }
186 const UnicodeString *param = &value;
187 int32_t offset;
188 pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status);
189 if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
190 if (offset >= 0) {
191 pos.setBeginIndex(pos.getBeginIndex() + offset);
192 pos.setEndIndex(pos.getEndIndex() + offset);
193 } else {
194 pos.setBeginIndex(0);
195 pos.setEndIndex(0);
196 }
197 }
198 return appendTo;
199 }
200
201 U_NAMESPACE_END
202
203 #endif /* #if !UCONFIG_NO_FORMATTING */
204