1 // © 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 
19 U_NAMESPACE_BEGIN
20 
21 class UnicodeString;
22 
23 class U_COMMON_API PluralMapBase : public UMemory {
24 public:
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  */
71 template<typename T>
72 class PluralMap : public PluralMapBase {
73 public:
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 
256 private:
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 
290 U_NAMESPACE_END
291 
292 #endif
293