1/*
2******************************************************************************
3* Copyright (C) 2014, International Business Machines
4* Corporation and others.  All Rights Reserved.
5******************************************************************************
6* quantityformatter.cpp
7*/
8#include "quantityformatter.h"
9#include "simplepatternformatter.h"
10#include "uassert.h"
11#include "unicode/unistr.h"
12#include "unicode/decimfmt.h"
13#include "cstring.h"
14#include "plurrule_impl.h"
15#include "unicode/plurrule.h"
16#include "charstr.h"
17#include "unicode/fmtable.h"
18#include "unicode/fieldpos.h"
19
20#if !UCONFIG_NO_FORMATTING
21
22U_NAMESPACE_BEGIN
23
24// other must always be first.
25static const char * const gPluralForms[] = {
26        "other", "zero", "one", "two", "few", "many"};
27
28static int32_t getPluralIndex(const char *pluralForm) {
29    int32_t len = UPRV_LENGTHOF(gPluralForms);
30    for (int32_t i = 0; i < len; ++i) {
31        if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
32            return i;
33        }
34    }
35    return -1;
36}
37
38QuantityFormatter::QuantityFormatter() {
39    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
40        formatters[i] = NULL;
41    }
42}
43
44QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
45    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
46        if (other.formatters[i] == NULL) {
47            formatters[i] = NULL;
48        } else {
49            formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
50        }
51    }
52}
53
54QuantityFormatter &QuantityFormatter::operator=(
55        const QuantityFormatter& other) {
56    if (this == &other) {
57        return *this;
58    }
59    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
60        delete formatters[i];
61        if (other.formatters[i] == NULL) {
62            formatters[i] = NULL;
63        } else {
64            formatters[i] = new SimplePatternFormatter(*other.formatters[i]);
65        }
66    }
67    return *this;
68}
69
70QuantityFormatter::~QuantityFormatter() {
71    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
72        delete formatters[i];
73    }
74}
75
76void QuantityFormatter::reset() {
77    for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
78        delete formatters[i];
79        formatters[i] = NULL;
80    }
81}
82
83UBool QuantityFormatter::add(
84        const char *variant,
85        const UnicodeString &rawPattern,
86        UErrorCode &status) {
87    if (U_FAILURE(status)) {
88        return FALSE;
89    }
90    int32_t pluralIndex = getPluralIndex(variant);
91    if (pluralIndex == -1) {
92        status = U_ILLEGAL_ARGUMENT_ERROR;
93        return FALSE;
94    }
95    SimplePatternFormatter *newFmt =
96            new SimplePatternFormatter(rawPattern);
97    if (newFmt == NULL) {
98        status = U_MEMORY_ALLOCATION_ERROR;
99        return FALSE;
100    }
101    if (newFmt->getPlaceholderCount() > 1) {
102        delete newFmt;
103        status = U_ILLEGAL_ARGUMENT_ERROR;
104        return FALSE;
105    }
106    delete formatters[pluralIndex];
107    formatters[pluralIndex] = newFmt;
108    return TRUE;
109}
110
111UBool QuantityFormatter::isValid() const {
112    return formatters[0] != NULL;
113}
114
115const SimplePatternFormatter *QuantityFormatter::getByVariant(
116        const char *variant) const {
117    int32_t pluralIndex = getPluralIndex(variant);
118    if (pluralIndex == -1) {
119        pluralIndex = 0;
120    }
121    const SimplePatternFormatter *pattern = formatters[pluralIndex];
122    if (pattern == NULL) {
123        pattern = formatters[0];
124    }
125    return pattern;
126}
127
128UnicodeString &QuantityFormatter::format(
129            const Formattable& quantity,
130            const NumberFormat &fmt,
131            const PluralRules &rules,
132            UnicodeString &appendTo,
133            FieldPosition &pos,
134            UErrorCode &status) const {
135    if (U_FAILURE(status)) {
136        return appendTo;
137    }
138    UnicodeString count;
139    const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
140    if (decFmt != NULL) {
141        FixedDecimal fd = decFmt->getFixedDecimal(quantity, status);
142        if (U_FAILURE(status)) {
143            return appendTo;
144        }
145        count = rules.select(fd);
146    } else {
147        if (quantity.getType() == Formattable::kDouble) {
148            count = rules.select(quantity.getDouble());
149        } else if (quantity.getType() == Formattable::kLong) {
150            count = rules.select(quantity.getLong());
151        } else if (quantity.getType() == Formattable::kInt64) {
152            count = rules.select((double) quantity.getInt64());
153        } else {
154            status = U_ILLEGAL_ARGUMENT_ERROR;
155            return appendTo;
156        }
157    }
158    CharString buffer;
159    buffer.appendInvariantChars(count, status);
160    if (U_FAILURE(status)) {
161        return appendTo;
162    }
163    const SimplePatternFormatter *pattern = getByVariant(buffer.data());
164    if (pattern == NULL) {
165        status = U_INVALID_STATE_ERROR;
166        return appendTo;
167    }
168    UnicodeString formattedNumber;
169    FieldPosition fpos(pos.getField());
170    fmt.format(quantity, formattedNumber, fpos, status);
171    const UnicodeString *params[1] = {&formattedNumber};
172    int32_t offsets[1];
173    pattern->formatAndAppend(
174            params,
175            UPRV_LENGTHOF(params),
176            appendTo,
177            offsets,
178            UPRV_LENGTHOF(offsets),
179            status);
180    if (offsets[0] != -1) {
181        if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
182            pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);
183            pos.setEndIndex(fpos.getEndIndex() + offsets[0]);
184        }
185    }
186    return appendTo;
187}
188
189U_NAMESPACE_END
190
191#endif /* #if !UCONFIG_NO_FORMATTING */
192