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