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
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 absence 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 unrecognized
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 nullptr.
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 following:
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]) : nullptr;
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] != nullptr && other.fVariants[i] != nullptr) {
102 *fVariants[i] = *other.fVariants[i];
103 } else if (fVariants[i] != nullptr) {
104 delete fVariants[i];
105 fVariants[i] = nullptr;
106 } else if (other.fVariants[i] != nullptr) {
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] = nullptr;
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 nullptr. Each time next returns, caller may pass index
136 * to getCategoryName() to get the name of the plural category.
137 * When this function returns nullptr, 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] != nullptr) {
144 index = static_cast<Category>(idx);
145 return fVariants[idx];
146 }
147 }
148 index = static_cast<Category>(idx);
149 return nullptr;
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] == nullptr) {
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, nullptr, 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), nullptr, 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] == nullptr || rhs.fVariants[i] == nullptr) {
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 nullptr;
266 }
267 int32_t index = category;
268 if (index < 0 || index >= UPRV_LENGTHOF(fVariants)) {
269 status = U_ILLEGAL_ARGUMENT_ERROR;
270 return nullptr;
271 }
272 if (fVariants[index] == nullptr) {
273 fVariants[index] = defaultValue == nullptr ?
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] = nullptr;
286 }
287 }
288};
289
290U_NAMESPACE_END
291
292#endif
293