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) 2015, International Business Machines Corporation and
6* others. All Rights Reserved.
7******************************************************************************
8*
9* File pluralmap.h - PluralMap class that maps plural categories to values.
10******************************************************************************
11*/
12
13#ifndef __PLURAL_MAP_H__
14#define __PLURAL_MAP_H__
15
16#include "unicode/uobject.h"
17#include "cmemory.h"
18
19U_NAMESPACE_BEGIN
20
21class UnicodeString;
22
23class U_COMMON_API PluralMapBase : public UMemory {
24public:
25    /**
26     * The names of all the plural categories. NONE is not an actual plural
27     * category, but rather represents the absense of a plural category.
28     */
29    enum Category {
30        NONE = -1,
31        OTHER,
32        ZERO,
33        ONE,
34        TWO,
35        FEW,
36        MANY,
37        CATEGORY_COUNT
38    };
39
40    /**
41     * Converts a category name such as "zero", "one", "two", "few", "many"
42     * or "other" to a category enum. Returns NONE for an unrecognized
43     * category name.
44     */
45    static Category toCategory(const char *categoryName);
46
47    /**
48     * Converts a category name such as "zero", "one", "two", "few", "many"
49     * or "other" to a category enum.  Returns NONE for urecongized
50     * category name.
51     */
52    static Category toCategory(const UnicodeString &categoryName);
53
54    /**
55     * Converts a category to a name.
56     * Passing NONE or CATEGORY_COUNT for category returns NULL.
57     */
58    static const char *getCategoryName(Category category);
59};
60
61/**
62 * A Map of plural categories to values. It maintains ownership of the
63 * values.
64 *
65 * Type T is the value type. T must provide the followng:
66 * 1) Default constructor
67 * 2) Copy constructor
68 * 3) Assignment operator
69 * 4) Must extend UMemory
70 */
71template<typename T>
72class PluralMap : public PluralMapBase {
73public:
74    /**
75     * Other category is maps to a copy of the default value.
76     */
77    PluralMap() : fOtherVariant() {
78        initializeNew();
79    }
80
81    /**
82     * Other category is mapped to otherVariant.
83     */
84    PluralMap(const T &otherVariant) : fOtherVariant(otherVariant) {
85        initializeNew();
86    }
87
88    PluralMap(const PluralMap<T> &other) : fOtherVariant(other.fOtherVariant) {
89        fVariants[0] = &fOtherVariant;
90        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
91            fVariants[i] = other.fVariants[i] ?
92                    new T(*other.fVariants[i]) : NULL;
93        }
94    }
95
96    PluralMap<T> &operator=(const PluralMap<T> &other) {
97        if (this == &other) {
98            return *this;
99        }
100        for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
101            if (fVariants[i] != NULL && other.fVariants[i] != NULL) {
102                *fVariants[i] = *other.fVariants[i];
103            } else if (fVariants[i] != NULL) {
104                delete fVariants[i];
105                fVariants[i] = NULL;
106            } else if (other.fVariants[i] != NULL) {
107                fVariants[i] = new T(*other.fVariants[i]);
108            } else {
109                // do nothing
110            }
111        }
112        return *this;
113    }
114
115    ~PluralMap() {
116        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
117            delete fVariants[i];
118        }
119    }
120
121    /**
122     * Removes all mappings and makes 'other' point to the default value.
123     */
124    void clear() {
125        *fVariants[0] = T();
126        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
127            delete fVariants[i];
128            fVariants[i] = NULL;
129        }
130    }
131
132    /**
133     * Iterates through the mappings in this instance, set index to NONE
134     * prior to using. Call next repeatedly to get the values until it
135     * returns NULL. Each time next returns, caller may pass index
136     * to getCategoryName() to get the name of the plural category.
137     * When this function returns NULL, index is CATEGORY_COUNT
138     */
139    const T *next(Category &index) const {
140        int32_t idx = index;
141        ++idx;
142        for (; idx < UPRV_LENGTHOF(fVariants); ++idx) {
143            if (fVariants[idx] != NULL) {
144                index = static_cast<Category>(idx);
145                return fVariants[idx];
146            }
147        }
148        index = static_cast<Category>(idx);
149        return NULL;
150    }
151
152    /**
153     * non const version of next.
154     */
155    T *nextMutable(Category &index) {
156        const T *result = next(index);
157        return const_cast<T *>(result);
158    }
159
160    /**
161     * Returns the 'other' variant.
162     * Same as calling get(OTHER).
163     */
164    const T &getOther() const {
165        return get(OTHER);
166    }
167
168    /**
169     * Returns the value associated with a category.
170     * If no value found, or v is NONE or CATEGORY_COUNT, falls
171     * back to returning the value for the 'other' category.
172     */
173    const T &get(Category v) const {
174        int32_t index = v;
175        if (index < 0 || index >= UPRV_LENGTHOF(fVariants) || fVariants[index] == NULL) {
176            return *fVariants[0];
177        }
178        return *fVariants[index];
179    }
180
181    /**
182     * Convenience routine to get the value by category name. Otherwise
183     * works just like get(Category).
184     */
185    const T &get(const char *category) const {
186        return get(toCategory(category));
187    }
188
189    /**
190     * Convenience routine to get the value by category name as a
191     * UnicodeString. Otherwise works just like get(category).
192     */
193    const T &get(const UnicodeString &category) const {
194        return get(toCategory(category));
195    }
196
197    /**
198     * Returns a pointer to the value associated with a category
199     * that caller can safely modify. If the value was defaulting to the 'other'
200     * variant because no explicit value was stored, this method creates a
201     * new value using the default constructor at the returned pointer.
202     *
203     * @param category the category with the value to change.
204     * @param status error returned here if index is NONE or CATEGORY_COUNT
205     *  or memory could not be allocated, or any other error happens.
206     */
207    T *getMutable(
208            Category category,
209            UErrorCode &status) {
210        return getMutable(category, NULL, status);
211    }
212
213    /**
214     * Convenience routine to get a mutable pointer to a value by category name.
215     * Otherwise works just like getMutable(Category, UErrorCode &).
216     * reports an error if the category name is invalid.
217     */
218    T *getMutable(
219            const char *category,
220            UErrorCode &status) {
221        return getMutable(toCategory(category), NULL, status);
222    }
223
224    /**
225     * Just like getMutable(Category, UErrorCode &) but copies defaultValue to
226     * returned pointer if it was defaulting to the 'other' variant
227     * because no explicit value was stored.
228     */
229    T *getMutableWithDefault(
230            Category category,
231            const T &defaultValue,
232            UErrorCode &status) {
233        return getMutable(category, &defaultValue, status);
234    }
235
236    /**
237     * Returns TRUE if this object equals rhs.
238     */
239    UBool equals(
240            const PluralMap<T> &rhs,
241            UBool (*eqFunc)(const T &, const T &)) const {
242        for (int32_t i = 0; i < UPRV_LENGTHOF(fVariants); ++i) {
243            if (fVariants[i] == rhs.fVariants[i]) {
244                continue;
245            }
246            if (fVariants[i] == NULL || rhs.fVariants[i] == NULL) {
247                return FALSE;
248            }
249            if (!eqFunc(*fVariants[i], *rhs.fVariants[i])) {
250                return FALSE;
251            }
252        }
253        return TRUE;
254    }
255
256private:
257    T fOtherVariant;
258    T* fVariants[6];
259
260    T *getMutable(
261            Category category,
262            const T *defaultValue,
263            UErrorCode &status) {
264        if (U_FAILURE(status)) {
265            return NULL;
266        }
267        int32_t index = category;
268        if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) {
269            status = U_ILLEGAL_ARGUMENT_ERROR;
270            return NULL;
271        }
272        if (fVariants[index] == NULL) {
273            fVariants[index] = defaultValue == NULL ?
274                    new T() : new T(*defaultValue);
275        }
276        if (!fVariants[index]) {
277            status = U_MEMORY_ALLOCATION_ERROR;
278        }
279        return fVariants[index];
280    }
281
282    void initializeNew() {
283        fVariants[0] = &fOtherVariant;
284        for (int32_t i = 1; i < UPRV_LENGTHOF(fVariants); ++i) {
285            fVariants[i] = NULL;
286        }
287    }
288};
289
290U_NAMESPACE_END
291
292#endif
293