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