1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 * Copyright (c) 2014, International Business Machines
6 * Corporation and others.  All Rights Reserved.
7 **********************************************************************
8 */
9 #include "unicode/utypes.h"
10 
11 #if !UCONFIG_NO_FORMATTING
12 
13 #include "unicode/scientificnumberformatter.h"
14 #include "unicode/dcfmtsym.h"
15 #include "unicode/fpositer.h"
16 #include "unicode/utf16.h"
17 #include "unicode/uniset.h"
18 #include "unicode/decimfmt.h"
19 #include "static_unicode_sets.h"
20 
21 U_NAMESPACE_BEGIN
22 
23 static const UChar kSuperscriptDigits[] = {
24         0x2070,
25         0xB9,
26         0xB2,
27         0xB3,
28         0x2074,
29         0x2075,
30         0x2076,
31         0x2077,
32         0x2078,
33         0x2079};
34 
35 static const UChar kSuperscriptPlusSign = 0x207A;
36 static const UChar kSuperscriptMinusSign = 0x207B;
37 
copyAsSuperscript(const UnicodeString & s,int32_t beginIndex,int32_t endIndex,UnicodeString & result,UErrorCode & status)38 static UBool copyAsSuperscript(
39         const UnicodeString &s,
40         int32_t beginIndex,
41         int32_t endIndex,
42         UnicodeString &result,
43         UErrorCode &status) {
44     if (U_FAILURE(status)) {
45         return FALSE;
46     }
47     for (int32_t i = beginIndex; i < endIndex;) {
48         UChar32 c = s.char32At(i);
49         int32_t digit = u_charDigitValue(c);
50         if (digit < 0) {
51             status = U_INVALID_CHAR_FOUND;
52             return FALSE;
53         }
54         result.append(kSuperscriptDigits[digit]);
55         i += U16_LENGTH(c);
56     }
57     return TRUE;
58 }
59 
createSuperscriptInstance(DecimalFormat * fmtToAdopt,UErrorCode & status)60 ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance(
61             DecimalFormat *fmtToAdopt, UErrorCode &status) {
62     return createInstance(fmtToAdopt, new SuperscriptStyle(), status);
63 }
64 
createSuperscriptInstance(const Locale & locale,UErrorCode & status)65 ScientificNumberFormatter *ScientificNumberFormatter::createSuperscriptInstance(
66             const Locale &locale, UErrorCode &status) {
67     return createInstance(
68             static_cast<DecimalFormat *>(
69                     DecimalFormat::createScientificInstance(locale, status)),
70             new SuperscriptStyle(),
71             status);
72 }
73 
createMarkupInstance(DecimalFormat * fmtToAdopt,const UnicodeString & beginMarkup,const UnicodeString & endMarkup,UErrorCode & status)74 ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance(
75         DecimalFormat *fmtToAdopt,
76         const UnicodeString &beginMarkup,
77         const UnicodeString &endMarkup,
78         UErrorCode &status) {
79     return createInstance(
80             fmtToAdopt,
81             new MarkupStyle(beginMarkup, endMarkup),
82             status);
83 }
84 
createMarkupInstance(const Locale & locale,const UnicodeString & beginMarkup,const UnicodeString & endMarkup,UErrorCode & status)85 ScientificNumberFormatter *ScientificNumberFormatter::createMarkupInstance(
86         const Locale &locale,
87         const UnicodeString &beginMarkup,
88         const UnicodeString &endMarkup,
89         UErrorCode &status) {
90     return createInstance(
91             static_cast<DecimalFormat *>(
92                     DecimalFormat::createScientificInstance(locale, status)),
93             new MarkupStyle(beginMarkup, endMarkup),
94             status);
95 }
96 
createInstance(DecimalFormat * fmtToAdopt,Style * styleToAdopt,UErrorCode & status)97 ScientificNumberFormatter *ScientificNumberFormatter::createInstance(
98             DecimalFormat *fmtToAdopt,
99             Style *styleToAdopt,
100             UErrorCode &status) {
101     LocalPointer<DecimalFormat> fmt(fmtToAdopt);
102     LocalPointer<Style> style(styleToAdopt);
103     if (U_FAILURE(status)) {
104         return NULL;
105     }
106     ScientificNumberFormatter *result =
107             new ScientificNumberFormatter(
108                     fmt.getAlias(),
109                     style.getAlias(),
110                     status);
111     if (result == NULL) {
112         status = U_MEMORY_ALLOCATION_ERROR;
113         return NULL;
114     }
115     fmt.orphan();
116     style.orphan();
117     if (U_FAILURE(status)) {
118         delete result;
119         return NULL;
120     }
121     return result;
122 }
123 
clone() const124 ScientificNumberFormatter::SuperscriptStyle *ScientificNumberFormatter::SuperscriptStyle::clone() const {
125     return new ScientificNumberFormatter::SuperscriptStyle(*this);
126 }
127 
format(const UnicodeString & original,FieldPositionIterator & fpi,const UnicodeString & preExponent,UnicodeString & appendTo,UErrorCode & status) const128 UnicodeString &ScientificNumberFormatter::SuperscriptStyle::format(
129         const UnicodeString &original,
130         FieldPositionIterator &fpi,
131         const UnicodeString &preExponent,
132         UnicodeString &appendTo,
133         UErrorCode &status) const {
134     if (U_FAILURE(status)) {
135         return appendTo;
136     }
137     FieldPosition fp;
138     int32_t copyFromOffset = 0;
139     while (fpi.next(fp)) {
140         switch (fp.getField()) {
141         case UNUM_EXPONENT_SYMBOL_FIELD:
142             appendTo.append(
143                     original,
144                     copyFromOffset,
145                     fp.getBeginIndex() - copyFromOffset);
146             copyFromOffset = fp.getEndIndex();
147             appendTo.append(preExponent);
148             break;
149         case UNUM_EXPONENT_SIGN_FIELD:
150             {
151                 using namespace icu::numparse::impl;
152                 int32_t beginIndex = fp.getBeginIndex();
153                 int32_t endIndex = fp.getEndIndex();
154                 UChar32 aChar = original.char32At(beginIndex);
155                 if (unisets::get(unisets::MINUS_SIGN)->contains(aChar)) {
156                     appendTo.append(
157                             original,
158                             copyFromOffset,
159                             beginIndex - copyFromOffset);
160                     appendTo.append(kSuperscriptMinusSign);
161                 } else if (unisets::get(unisets::PLUS_SIGN)->contains(aChar)) {
162                     appendTo.append(
163                            original,
164                            copyFromOffset,
165                            beginIndex - copyFromOffset);
166                     appendTo.append(kSuperscriptPlusSign);
167                 } else {
168                     status = U_INVALID_CHAR_FOUND;
169                     return appendTo;
170                 }
171                 copyFromOffset = endIndex;
172             }
173             break;
174         case UNUM_EXPONENT_FIELD:
175             appendTo.append(
176                     original,
177                     copyFromOffset,
178                     fp.getBeginIndex() - copyFromOffset);
179             if (!copyAsSuperscript(
180                     original,
181                     fp.getBeginIndex(),
182                     fp.getEndIndex(),
183                     appendTo,
184                     status)) {
185               return appendTo;
186             }
187             copyFromOffset = fp.getEndIndex();
188             break;
189         default:
190             break;
191         }
192     }
193     appendTo.append(
194             original, copyFromOffset, original.length() - copyFromOffset);
195     return appendTo;
196 }
197 
clone() const198 ScientificNumberFormatter::MarkupStyle *ScientificNumberFormatter::MarkupStyle::clone() const {
199     return new ScientificNumberFormatter::MarkupStyle(*this);
200 }
201 
format(const UnicodeString & original,FieldPositionIterator & fpi,const UnicodeString & preExponent,UnicodeString & appendTo,UErrorCode & status) const202 UnicodeString &ScientificNumberFormatter::MarkupStyle::format(
203         const UnicodeString &original,
204         FieldPositionIterator &fpi,
205         const UnicodeString &preExponent,
206         UnicodeString &appendTo,
207         UErrorCode &status) const {
208     if (U_FAILURE(status)) {
209         return appendTo;
210     }
211     FieldPosition fp;
212     int32_t copyFromOffset = 0;
213     while (fpi.next(fp)) {
214         switch (fp.getField()) {
215         case UNUM_EXPONENT_SYMBOL_FIELD:
216             appendTo.append(
217                     original,
218                     copyFromOffset,
219                     fp.getBeginIndex() - copyFromOffset);
220             copyFromOffset = fp.getEndIndex();
221             appendTo.append(preExponent);
222             appendTo.append(fBeginMarkup);
223             break;
224         case UNUM_EXPONENT_FIELD:
225             appendTo.append(
226                     original,
227                     copyFromOffset,
228                     fp.getEndIndex() - copyFromOffset);
229             copyFromOffset = fp.getEndIndex();
230             appendTo.append(fEndMarkup);
231             break;
232         default:
233             break;
234         }
235     }
236     appendTo.append(
237             original, copyFromOffset, original.length() - copyFromOffset);
238     return appendTo;
239 }
240 
ScientificNumberFormatter(DecimalFormat * fmtToAdopt,Style * styleToAdopt,UErrorCode & status)241 ScientificNumberFormatter::ScientificNumberFormatter(
242         DecimalFormat *fmtToAdopt, Style *styleToAdopt, UErrorCode &status)
243         : fPreExponent(),
244           fDecimalFormat(fmtToAdopt),
245           fStyle(styleToAdopt) {
246     if (U_FAILURE(status)) {
247         return;
248     }
249     if (fDecimalFormat == NULL || fStyle == NULL) {
250         status = U_ILLEGAL_ARGUMENT_ERROR;
251         return;
252     }
253     const DecimalFormatSymbols *sym = fDecimalFormat->getDecimalFormatSymbols();
254     if (sym == NULL) {
255         status = U_ILLEGAL_ARGUMENT_ERROR;
256         return;
257     }
258     getPreExponent(*sym, fPreExponent);
259 }
260 
ScientificNumberFormatter(const ScientificNumberFormatter & other)261 ScientificNumberFormatter::ScientificNumberFormatter(
262         const ScientificNumberFormatter &other)
263         : UObject(other),
264           fPreExponent(other.fPreExponent),
265           fDecimalFormat(NULL),
266           fStyle(NULL) {
267     fDecimalFormat = static_cast<DecimalFormat *>(
268             other.fDecimalFormat->clone());
269     fStyle = other.fStyle->clone();
270 }
271 
~ScientificNumberFormatter()272 ScientificNumberFormatter::~ScientificNumberFormatter() {
273     delete fDecimalFormat;
274     delete fStyle;
275 }
276 
format(const Formattable & number,UnicodeString & appendTo,UErrorCode & status) const277 UnicodeString &ScientificNumberFormatter::format(
278         const Formattable &number,
279         UnicodeString &appendTo,
280         UErrorCode &status) const {
281     if (U_FAILURE(status)) {
282         return appendTo;
283     }
284     UnicodeString original;
285     FieldPositionIterator fpi;
286     fDecimalFormat->format(number, original, &fpi, status);
287     return fStyle->format(
288             original,
289             fpi,
290             fPreExponent,
291             appendTo,
292             status);
293 }
294 
getPreExponent(const DecimalFormatSymbols & dfs,UnicodeString & preExponent)295 void ScientificNumberFormatter::getPreExponent(
296         const DecimalFormatSymbols &dfs, UnicodeString &preExponent) {
297     preExponent.append(dfs.getConstSymbol(
298             DecimalFormatSymbols::kExponentMultiplicationSymbol));
299     preExponent.append(dfs.getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol));
300     preExponent.append(dfs.getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol));
301 }
302 
303 U_NAMESPACE_END
304 
305 #endif /* !UCONFIG_NO_FORMATTING */
306