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
29U_NAMESPACE_BEGIN
30
31QuantityFormatter::QuantityFormatter() {
32    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
33        formatters[i] = NULL;
34    }
35}
36
37QuantityFormatter::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
47QuantityFormatter &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
63QuantityFormatter::~QuantityFormatter() {
64    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
65        delete formatters[i];
66    }
67}
68
69void QuantityFormatter::reset() {
70    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
71        delete formatters[i];
72        formatters[i] = NULL;
73    }
74}
75
76UBool 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
100UBool QuantityFormatter::isValid() const {
101    return formatters[StandardPlural::OTHER] != NULL;
102}
103
104const 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
115UnicodeString &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
141StandardPlural::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
177UnicodeString &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(&param, 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
201U_NAMESPACE_END
202
203#endif /* #if !UCONFIG_NO_FORMATTING */
204