/src/icu/source/i18n/plurfmt.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  | *******************************************************************************  | 
5  |  | * Copyright (C) 2009-2015, International Business Machines Corporation and  | 
6  |  | * others. All Rights Reserved.  | 
7  |  | *******************************************************************************  | 
8  |  | *  | 
9  |  | * File PLURFMT.CPP  | 
10  |  | *******************************************************************************  | 
11  |  | */  | 
12  |  |  | 
13  |  | #include "unicode/decimfmt.h"  | 
14  |  | #include "unicode/messagepattern.h"  | 
15  |  | #include "unicode/plurfmt.h"  | 
16  |  | #include "unicode/plurrule.h"  | 
17  |  | #include "unicode/utypes.h"  | 
18  |  | #include "cmemory.h"  | 
19  |  | #include "messageimpl.h"  | 
20  |  | #include "nfrule.h"  | 
21  |  | #include "plurrule_impl.h"  | 
22  |  | #include "uassert.h"  | 
23  |  | #include "uhash.h"  | 
24  |  | #include "number_decimalquantity.h"  | 
25  |  | #include "number_utils.h"  | 
26  |  | #include "number_utypes.h"  | 
27  |  |  | 
28  |  | #if !UCONFIG_NO_FORMATTING  | 
29  |  |  | 
30  |  | U_NAMESPACE_BEGIN  | 
31  |  |  | 
32  |  | using number::impl::DecimalQuantity;  | 
33  |  |  | 
34  |  | static const UChar OTHER_STRING[] = { | 
35  |  |     0x6F, 0x74, 0x68, 0x65, 0x72, 0  // "other"  | 
36  |  | };  | 
37  |  |  | 
38  |  | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)  | 
39  |  |  | 
40  |  | PluralFormat::PluralFormat(UErrorCode& status)  | 
41  | 0  |         : locale(Locale::getDefault()),  | 
42  | 0  |           msgPattern(status),  | 
43  |  |           numberFormat(NULL),  | 
44  | 0  |           offset(0) { | 
45  | 0  |     init(NULL, UPLURAL_TYPE_CARDINAL, status);  | 
46  | 0  | }  | 
47  |  |  | 
48  |  | PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status)  | 
49  | 0  |         : locale(loc),  | 
50  | 0  |           msgPattern(status),  | 
51  |  |           numberFormat(NULL),  | 
52  | 0  |           offset(0) { | 
53  | 0  |     init(NULL, UPLURAL_TYPE_CARDINAL, status);  | 
54  | 0  | }  | 
55  |  |  | 
56  |  | PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status)  | 
57  | 0  |         : locale(Locale::getDefault()),  | 
58  | 0  |           msgPattern(status),  | 
59  |  |           numberFormat(NULL),  | 
60  | 0  |           offset(0) { | 
61  | 0  |     init(&rules, UPLURAL_TYPE_COUNT, status);  | 
62  | 0  | }  | 
63  |  |  | 
64  |  | PluralFormat::PluralFormat(const Locale& loc,  | 
65  |  |                            const PluralRules& rules,  | 
66  |  |                            UErrorCode& status)  | 
67  | 0  |         : locale(loc),  | 
68  | 0  |           msgPattern(status),  | 
69  |  |           numberFormat(NULL),  | 
70  | 0  |           offset(0) { | 
71  | 0  |     init(&rules, UPLURAL_TYPE_COUNT, status);  | 
72  | 0  | }  | 
73  |  |  | 
74  |  | PluralFormat::PluralFormat(const Locale& loc,  | 
75  |  |                            UPluralType type,  | 
76  |  |                            UErrorCode& status)  | 
77  | 0  |         : locale(loc),  | 
78  | 0  |           msgPattern(status),  | 
79  |  |           numberFormat(NULL),  | 
80  | 0  |           offset(0) { | 
81  | 0  |     init(NULL, type, status);  | 
82  | 0  | }  | 
83  |  |  | 
84  |  | PluralFormat::PluralFormat(const UnicodeString& pat,  | 
85  |  |                            UErrorCode& status)  | 
86  | 0  |         : locale(Locale::getDefault()),  | 
87  | 0  |           msgPattern(status),  | 
88  |  |           numberFormat(NULL),  | 
89  | 0  |           offset(0) { | 
90  | 0  |     init(NULL, UPLURAL_TYPE_CARDINAL, status);  | 
91  | 0  |     applyPattern(pat, status);  | 
92  | 0  | }  | 
93  |  |  | 
94  |  | PluralFormat::PluralFormat(const Locale& loc,  | 
95  |  |                            const UnicodeString& pat,  | 
96  |  |                            UErrorCode& status)  | 
97  | 0  |         : locale(loc),  | 
98  | 0  |           msgPattern(status),  | 
99  |  |           numberFormat(NULL),  | 
100  | 0  |           offset(0) { | 
101  | 0  |     init(NULL, UPLURAL_TYPE_CARDINAL, status);  | 
102  | 0  |     applyPattern(pat, status);  | 
103  | 0  | }  | 
104  |  |  | 
105  |  | PluralFormat::PluralFormat(const PluralRules& rules,  | 
106  |  |                            const UnicodeString& pat,  | 
107  |  |                            UErrorCode& status)  | 
108  | 0  |         : locale(Locale::getDefault()),  | 
109  | 0  |           msgPattern(status),  | 
110  |  |           numberFormat(NULL),  | 
111  | 0  |           offset(0) { | 
112  | 0  |     init(&rules, UPLURAL_TYPE_COUNT, status);  | 
113  | 0  |     applyPattern(pat, status);  | 
114  | 0  | }  | 
115  |  |  | 
116  |  | PluralFormat::PluralFormat(const Locale& loc,  | 
117  |  |                            const PluralRules& rules,  | 
118  |  |                            const UnicodeString& pat,  | 
119  |  |                            UErrorCode& status)  | 
120  | 0  |         : locale(loc),  | 
121  | 0  |           msgPattern(status),  | 
122  |  |           numberFormat(NULL),  | 
123  | 0  |           offset(0) { | 
124  | 0  |     init(&rules, UPLURAL_TYPE_COUNT, status);  | 
125  | 0  |     applyPattern(pat, status);  | 
126  | 0  | }  | 
127  |  |  | 
128  |  | PluralFormat::PluralFormat(const Locale& loc,  | 
129  |  |                            UPluralType type,  | 
130  |  |                            const UnicodeString& pat,  | 
131  |  |                            UErrorCode& status)  | 
132  | 0  |         : locale(loc),  | 
133  | 0  |           msgPattern(status),  | 
134  |  |           numberFormat(NULL),  | 
135  | 0  |           offset(0) { | 
136  | 0  |     init(NULL, type, status);  | 
137  | 0  |     applyPattern(pat, status);  | 
138  | 0  | }  | 
139  |  |  | 
140  |  | PluralFormat::PluralFormat(const PluralFormat& other)  | 
141  | 0  |         : Format(other),  | 
142  | 0  |           locale(other.locale),  | 
143  | 0  |           msgPattern(other.msgPattern),  | 
144  |  |           numberFormat(NULL),  | 
145  | 0  |           offset(other.offset) { | 
146  | 0  |     copyObjects(other);  | 
147  | 0  | }  | 
148  |  |  | 
149  |  | void  | 
150  | 0  | PluralFormat::copyObjects(const PluralFormat& other) { | 
151  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
152  | 0  |     if (numberFormat != NULL) { | 
153  | 0  |         delete numberFormat;  | 
154  | 0  |     }  | 
155  | 0  |     if (pluralRulesWrapper.pluralRules != NULL) { | 
156  | 0  |         delete pluralRulesWrapper.pluralRules;  | 
157  | 0  |     }  | 
158  |  | 
  | 
159  | 0  |     if (other.numberFormat == NULL) { | 
160  | 0  |         numberFormat = NumberFormat::createInstance(locale, status);  | 
161  | 0  |     } else { | 
162  | 0  |         numberFormat = other.numberFormat->clone();  | 
163  | 0  |     }  | 
164  | 0  |     if (other.pluralRulesWrapper.pluralRules == NULL) { | 
165  | 0  |         pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status);  | 
166  | 0  |     } else { | 
167  | 0  |         pluralRulesWrapper.pluralRules = other.pluralRulesWrapper.pluralRules->clone();  | 
168  | 0  |     }  | 
169  | 0  | }  | 
170  |  |  | 
171  |  |  | 
172  | 0  | PluralFormat::~PluralFormat() { | 
173  | 0  |     delete numberFormat;  | 
174  | 0  | }  | 
175  |  |  | 
176  |  | void  | 
177  | 0  | PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) { | 
178  | 0  |     if (U_FAILURE(status)) { | 
179  | 0  |         return;  | 
180  | 0  |     }  | 
181  |  |  | 
182  | 0  |     if (rules==NULL) { | 
183  | 0  |         pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status);  | 
184  | 0  |     } else { | 
185  | 0  |         pluralRulesWrapper.pluralRules = rules->clone();  | 
186  | 0  |         if (pluralRulesWrapper.pluralRules == NULL) { | 
187  | 0  |             status = U_MEMORY_ALLOCATION_ERROR;  | 
188  | 0  |             return;  | 
189  | 0  |         }  | 
190  | 0  |     }  | 
191  |  |  | 
192  | 0  |     numberFormat= NumberFormat::createInstance(locale, status);  | 
193  | 0  | }  | 
194  |  |  | 
195  |  | void  | 
196  | 0  | PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) { | 
197  | 0  |     msgPattern.parsePluralStyle(newPattern, NULL, status);  | 
198  | 0  |     if (U_FAILURE(status)) { | 
199  | 0  |         msgPattern.clear();  | 
200  | 0  |         offset = 0;  | 
201  | 0  |         return;  | 
202  | 0  |     }  | 
203  | 0  |     offset = msgPattern.getPluralOffset(0);  | 
204  | 0  | }  | 
205  |  |  | 
206  |  | UnicodeString&  | 
207  |  | PluralFormat::format(const Formattable& obj,  | 
208  |  |                    UnicodeString& appendTo,  | 
209  |  |                    FieldPosition& pos,  | 
210  |  |                    UErrorCode& status) const  | 
211  | 0  | { | 
212  | 0  |     if (U_FAILURE(status)) return appendTo;  | 
213  |  |  | 
214  | 0  |     if (obj.isNumeric()) { | 
215  | 0  |         return format(obj, obj.getDouble(), appendTo, pos, status);  | 
216  | 0  |     } else { | 
217  | 0  |         status = U_ILLEGAL_ARGUMENT_ERROR;  | 
218  | 0  |         return appendTo;  | 
219  | 0  |     }  | 
220  | 0  | }  | 
221  |  |  | 
222  |  | UnicodeString  | 
223  | 0  | PluralFormat::format(int32_t number, UErrorCode& status) const { | 
224  | 0  |     FieldPosition fpos(FieldPosition::DONT_CARE);  | 
225  | 0  |     UnicodeString result;  | 
226  | 0  |     return format(Formattable(number), number, result, fpos, status);  | 
227  | 0  | }  | 
228  |  |  | 
229  |  | UnicodeString  | 
230  | 0  | PluralFormat::format(double number, UErrorCode& status) const { | 
231  | 0  |     FieldPosition fpos(FieldPosition::DONT_CARE);  | 
232  | 0  |     UnicodeString result;  | 
233  | 0  |     return format(Formattable(number), number, result, fpos, status);  | 
234  | 0  | }  | 
235  |  |  | 
236  |  |  | 
237  |  | UnicodeString&  | 
238  |  | PluralFormat::format(int32_t number,  | 
239  |  |                      UnicodeString& appendTo,  | 
240  |  |                      FieldPosition& pos,  | 
241  | 0  |                      UErrorCode& status) const { | 
242  | 0  |     return format(Formattable(number), (double)number, appendTo, pos, status);  | 
243  | 0  | }  | 
244  |  |  | 
245  |  | UnicodeString&  | 
246  |  | PluralFormat::format(double number,  | 
247  |  |                      UnicodeString& appendTo,  | 
248  |  |                      FieldPosition& pos,  | 
249  | 0  |                      UErrorCode& status) const { | 
250  | 0  |     return format(Formattable(number), (double)number, appendTo, pos, status);  | 
251  | 0  | }  | 
252  |  |  | 
253  |  | UnicodeString&  | 
254  |  | PluralFormat::format(const Formattable& numberObject, double number,  | 
255  |  |                      UnicodeString& appendTo,  | 
256  |  |                      FieldPosition& pos,  | 
257  | 0  |                      UErrorCode& status) const { | 
258  | 0  |     if (U_FAILURE(status)) { | 
259  | 0  |         return appendTo;  | 
260  | 0  |     }  | 
261  | 0  |     if (msgPattern.countParts() == 0) { | 
262  | 0  |         return numberFormat->format(numberObject, appendTo, pos, status);  | 
263  | 0  |     }  | 
264  |  |  | 
265  |  |     // Get the appropriate sub-message.  | 
266  |  |     // Select it based on the formatted number-offset.  | 
267  | 0  |     double numberMinusOffset = number - offset;  | 
268  |  |     // Call NumberFormatter to get both the DecimalQuantity and the string.  | 
269  |  |     // This call site needs to use more internal APIs than the Java equivalent.  | 
270  | 0  |     number::impl::UFormattedNumberData data;  | 
271  | 0  |     if (offset == 0) { | 
272  |  |         // could be BigDecimal etc.  | 
273  | 0  |         numberObject.populateDecimalQuantity(data.quantity, status);  | 
274  | 0  |     } else { | 
275  | 0  |         data.quantity.setToDouble(numberMinusOffset);  | 
276  | 0  |     }  | 
277  | 0  |     UnicodeString numberString;  | 
278  | 0  |     auto *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);  | 
279  | 0  |     if(decFmt != nullptr) { | 
280  | 0  |         const number::LocalizedNumberFormatter* lnf = decFmt->toNumberFormatter(status);  | 
281  | 0  |         if (U_FAILURE(status)) { | 
282  | 0  |             return appendTo;  | 
283  | 0  |         }  | 
284  | 0  |         lnf->formatImpl(&data, status); // mutates &data  | 
285  | 0  |         if (U_FAILURE(status)) { | 
286  | 0  |             return appendTo;  | 
287  | 0  |         }  | 
288  | 0  |         numberString = data.getStringRef().toUnicodeString();  | 
289  | 0  |     } else { | 
290  | 0  |         if (offset == 0) { | 
291  | 0  |             numberFormat->format(numberObject, numberString, status);  | 
292  | 0  |         } else { | 
293  | 0  |             numberFormat->format(numberMinusOffset, numberString, status);  | 
294  | 0  |         }  | 
295  | 0  |     }  | 
296  |  |  | 
297  | 0  |     int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &data.quantity, number, status);  | 
298  | 0  |     if (U_FAILURE(status)) { return appendTo; } | 
299  |  |     // Replace syntactic # signs in the top level of this sub-message  | 
300  |  |     // (not in nested arguments) with the formatted number-offset.  | 
301  | 0  |     const UnicodeString& pattern = msgPattern.getPatternString();  | 
302  | 0  |     int32_t prevIndex = msgPattern.getPart(partIndex).getLimit();  | 
303  | 0  |     for (;;) { | 
304  | 0  |         const MessagePattern::Part& part = msgPattern.getPart(++partIndex);  | 
305  | 0  |         const UMessagePatternPartType type = part.getType();  | 
306  | 0  |         int32_t index = part.getIndex();  | 
307  | 0  |         if (type == UMSGPAT_PART_TYPE_MSG_LIMIT) { | 
308  | 0  |             return appendTo.append(pattern, prevIndex, index - prevIndex);  | 
309  | 0  |         } else if ((type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) ||  | 
310  | 0  |             (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) { | 
311  | 0  |             appendTo.append(pattern, prevIndex, index - prevIndex);  | 
312  | 0  |             if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { | 
313  | 0  |                 appendTo.append(numberString);  | 
314  | 0  |             }  | 
315  | 0  |             prevIndex = part.getLimit();  | 
316  | 0  |         } else if (type == UMSGPAT_PART_TYPE_ARG_START) { | 
317  | 0  |             appendTo.append(pattern, prevIndex, index - prevIndex);  | 
318  | 0  |             prevIndex = index;  | 
319  | 0  |             partIndex = msgPattern.getLimitPartIndex(partIndex);  | 
320  | 0  |             index = msgPattern.getPart(partIndex).getLimit();  | 
321  | 0  |             MessageImpl::appendReducedApostrophes(pattern, prevIndex, index, appendTo);  | 
322  | 0  |             prevIndex = index;  | 
323  | 0  |         }  | 
324  | 0  |     }  | 
325  | 0  | }  | 
326  |  |  | 
327  |  | UnicodeString&  | 
328  | 0  | PluralFormat::toPattern(UnicodeString& appendTo) { | 
329  | 0  |     if (0 == msgPattern.countParts()) { | 
330  | 0  |         appendTo.setToBogus();  | 
331  | 0  |     } else { | 
332  | 0  |         appendTo.append(msgPattern.getPatternString());  | 
333  | 0  |     }  | 
334  | 0  |     return appendTo;  | 
335  | 0  | }  | 
336  |  |  | 
337  |  | void  | 
338  | 0  | PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { | 
339  | 0  |     if (U_FAILURE(status)) { | 
340  | 0  |         return;  | 
341  | 0  |     }  | 
342  | 0  |     locale = loc;  | 
343  | 0  |     msgPattern.clear();  | 
344  | 0  |     delete numberFormat;  | 
345  | 0  |     offset = 0;  | 
346  | 0  |     numberFormat = NULL;  | 
347  | 0  |     pluralRulesWrapper.reset();  | 
348  | 0  |     init(NULL, UPLURAL_TYPE_CARDINAL, status);  | 
349  | 0  | }  | 
350  |  |  | 
351  |  | void  | 
352  | 0  | PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { | 
353  | 0  |     if (U_FAILURE(status)) { | 
354  | 0  |         return;  | 
355  | 0  |     }  | 
356  | 0  |     NumberFormat* nf = format->clone();  | 
357  | 0  |     if (nf != NULL) { | 
358  | 0  |         delete numberFormat;  | 
359  | 0  |         numberFormat = nf;  | 
360  | 0  |     } else { | 
361  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
362  | 0  |     }  | 
363  | 0  | }  | 
364  |  |  | 
365  |  | PluralFormat*  | 
366  |  | PluralFormat::clone() const  | 
367  | 0  | { | 
368  | 0  |     return new PluralFormat(*this);  | 
369  | 0  | }  | 
370  |  |  | 
371  |  |  | 
372  |  | PluralFormat&  | 
373  | 0  | PluralFormat::operator=(const PluralFormat& other) { | 
374  | 0  |     if (this != &other) { | 
375  | 0  |         locale = other.locale;  | 
376  | 0  |         msgPattern = other.msgPattern;  | 
377  | 0  |         offset = other.offset;  | 
378  | 0  |         copyObjects(other);  | 
379  | 0  |     }  | 
380  |  | 
  | 
381  | 0  |     return *this;  | 
382  | 0  | }  | 
383  |  |  | 
384  |  | bool  | 
385  | 0  | PluralFormat::operator==(const Format& other) const { | 
386  | 0  |     if (this == &other) { | 
387  | 0  |         return TRUE;  | 
388  | 0  |     }  | 
389  | 0  |     if (!Format::operator==(other)) { | 
390  | 0  |         return FALSE;  | 
391  | 0  |     }  | 
392  | 0  |     const PluralFormat& o = (const PluralFormat&)other;  | 
393  | 0  |     return  | 
394  | 0  |         locale == o.locale &&  | 
395  | 0  |         msgPattern == o.msgPattern &&  // implies same offset  | 
396  | 0  |         (numberFormat == NULL) == (o.numberFormat == NULL) &&  | 
397  | 0  |         (numberFormat == NULL || *numberFormat == *o.numberFormat) &&  | 
398  | 0  |         (pluralRulesWrapper.pluralRules == NULL) == (o.pluralRulesWrapper.pluralRules == NULL) &&  | 
399  | 0  |         (pluralRulesWrapper.pluralRules == NULL ||  | 
400  | 0  |             *pluralRulesWrapper.pluralRules == *o.pluralRulesWrapper.pluralRules);  | 
401  | 0  | }  | 
402  |  |  | 
403  |  | bool  | 
404  | 0  | PluralFormat::operator!=(const Format& other) const { | 
405  | 0  |     return  !operator==(other);  | 
406  | 0  | }  | 
407  |  |  | 
408  |  | void  | 
409  |  | PluralFormat::parseObject(const UnicodeString& /*source*/,  | 
410  |  |                         Formattable& /*result*/,  | 
411  |  |                         ParsePosition& pos) const  | 
412  | 0  | { | 
413  |  |     // Parsing not supported.  | 
414  | 0  |     pos.setErrorIndex(pos.getIndex());  | 
415  | 0  | }  | 
416  |  |  | 
417  |  | int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,  | 
418  |  |                                      const PluralSelector& selector, void *context,  | 
419  | 0  |                                      double number, UErrorCode& ec) { | 
420  | 0  |     if (U_FAILURE(ec)) { | 
421  | 0  |         return 0;  | 
422  | 0  |     }  | 
423  | 0  |     int32_t count=pattern.countParts();  | 
424  | 0  |     double offset;  | 
425  | 0  |     const MessagePattern::Part* part=&pattern.getPart(partIndex);  | 
426  | 0  |     if (MessagePattern::Part::hasNumericValue(part->getType())) { | 
427  | 0  |         offset=pattern.getNumericValue(*part);  | 
428  | 0  |         ++partIndex;  | 
429  | 0  |     } else { | 
430  | 0  |         offset=0;  | 
431  | 0  |     }  | 
432  |  |     // The keyword is empty until we need to match against a non-explicit, not-"other" value.  | 
433  |  |     // Then we get the keyword from the selector.  | 
434  |  |     // (In other words, we never call the selector if we match against an explicit value,  | 
435  |  |     // or if the only non-explicit keyword is "other".)  | 
436  | 0  |     UnicodeString keyword;  | 
437  | 0  |     UnicodeString other(FALSE, OTHER_STRING, 5);  | 
438  |  |     // When we find a match, we set msgStart>0 and also set this boolean to true  | 
439  |  |     // to avoid matching the keyword again (duplicates are allowed)  | 
440  |  |     // while we continue to look for an explicit-value match.  | 
441  | 0  |     UBool haveKeywordMatch=FALSE;  | 
442  |  |     // msgStart is 0 until we find any appropriate sub-message.  | 
443  |  |     // We remember the first "other" sub-message if we have not seen any  | 
444  |  |     // appropriate sub-message before.  | 
445  |  |     // We remember the first matching-keyword sub-message if we have not seen  | 
446  |  |     // one of those before.  | 
447  |  |     // (The parser allows [does not check for] duplicate keywords.  | 
448  |  |     // We just have to make sure to take the first one.)  | 
449  |  |     // We avoid matching the keyword twice by also setting haveKeywordMatch=true  | 
450  |  |     // at the first keyword match.  | 
451  |  |     // We keep going until we find an explicit-value match or reach the end of the plural style.  | 
452  | 0  |     int32_t msgStart=0;  | 
453  |  |     // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples  | 
454  |  |     // until ARG_LIMIT or end of plural-only pattern.  | 
455  | 0  |     do { | 
456  | 0  |         part=&pattern.getPart(partIndex++);  | 
457  | 0  |         const UMessagePatternPartType type = part->getType();  | 
458  | 0  |         if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) { | 
459  | 0  |             break;  | 
460  | 0  |         }  | 
461  | 0  |         U_ASSERT (type==UMSGPAT_PART_TYPE_ARG_SELECTOR);  | 
462  |  |         // part is an ARG_SELECTOR followed by an optional explicit value, and then a message  | 
463  | 0  |         if(MessagePattern::Part::hasNumericValue(pattern.getPartType(partIndex))) { | 
464  |  |             // explicit value like "=2"  | 
465  | 0  |             part=&pattern.getPart(partIndex++);  | 
466  | 0  |             if(number==pattern.getNumericValue(*part)) { | 
467  |  |                 // matches explicit value  | 
468  | 0  |                 return partIndex;  | 
469  | 0  |             }  | 
470  | 0  |         } else if(!haveKeywordMatch) { | 
471  |  |             // plural keyword like "few" or "other"  | 
472  |  |             // Compare "other" first and call the selector if this is not "other".  | 
473  | 0  |             if(pattern.partSubstringMatches(*part, other)) { | 
474  | 0  |                 if(msgStart==0) { | 
475  | 0  |                     msgStart=partIndex;  | 
476  | 0  |                     if(0 == keyword.compare(other)) { | 
477  |  |                         // This is the first "other" sub-message,  | 
478  |  |                         // and the selected keyword is also "other".  | 
479  |  |                         // Do not match "other" again.  | 
480  | 0  |                         haveKeywordMatch=TRUE;  | 
481  | 0  |                     }  | 
482  | 0  |                 }  | 
483  | 0  |             } else { | 
484  | 0  |                 if(keyword.isEmpty()) { | 
485  | 0  |                     keyword=selector.select(context, number-offset, ec);  | 
486  | 0  |                     if(msgStart!=0 && (0 == keyword.compare(other))) { | 
487  |  |                         // We have already seen an "other" sub-message.  | 
488  |  |                         // Do not match "other" again.  | 
489  | 0  |                         haveKeywordMatch=TRUE;  | 
490  |  |                         // Skip keyword matching but do getLimitPartIndex().  | 
491  | 0  |                     }  | 
492  | 0  |                 }  | 
493  | 0  |                 if(!haveKeywordMatch && pattern.partSubstringMatches(*part, keyword)) { | 
494  |  |                     // keyword matches  | 
495  | 0  |                     msgStart=partIndex;  | 
496  |  |                     // Do not match this keyword again.  | 
497  | 0  |                     haveKeywordMatch=TRUE;  | 
498  | 0  |                 }  | 
499  | 0  |             }  | 
500  | 0  |         }  | 
501  | 0  |         partIndex=pattern.getLimitPartIndex(partIndex);  | 
502  | 0  |     } while(++partIndex<count);  | 
503  | 0  |     return msgStart;  | 
504  | 0  | }  | 
505  |  |  | 
506  | 0  | void PluralFormat::parseType(const UnicodeString& source, const NFRule *rbnfLenientScanner, Formattable& result, FieldPosition& pos) const { | 
507  |  |     // If no pattern was applied, return null.  | 
508  | 0  |     if (msgPattern.countParts() == 0) { | 
509  | 0  |         pos.setBeginIndex(-1);  | 
510  | 0  |         pos.setEndIndex(-1);  | 
511  | 0  |         return;  | 
512  | 0  |     }  | 
513  | 0  |     int partIndex = 0;  | 
514  | 0  |     int currMatchIndex;  | 
515  | 0  |     int count=msgPattern.countParts();  | 
516  | 0  |     int startingAt = pos.getBeginIndex();  | 
517  | 0  |     if (startingAt < 0) { | 
518  | 0  |         startingAt = 0;  | 
519  | 0  |     }  | 
520  |  |  | 
521  |  |     // The keyword is null until we need to match against a non-explicit, not-"other" value.  | 
522  |  |     // Then we get the keyword from the selector.  | 
523  |  |     // (In other words, we never call the selector if we match against an explicit value,  | 
524  |  |     // or if the only non-explicit keyword is "other".)  | 
525  | 0  |     UnicodeString keyword;  | 
526  | 0  |     UnicodeString matchedWord;  | 
527  | 0  |     const UnicodeString& pattern = msgPattern.getPatternString();  | 
528  | 0  |     int matchedIndex = -1;  | 
529  |  |     // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples  | 
530  |  |     // until the end of the plural-only pattern.  | 
531  | 0  |     while (partIndex < count) { | 
532  | 0  |         const MessagePattern::Part* partSelector = &msgPattern.getPart(partIndex++);  | 
533  | 0  |         if (partSelector->getType() != UMSGPAT_PART_TYPE_ARG_SELECTOR) { | 
534  |  |             // Bad format  | 
535  | 0  |             continue;  | 
536  | 0  |         }  | 
537  |  |  | 
538  | 0  |         const MessagePattern::Part* partStart = &msgPattern.getPart(partIndex++);  | 
539  | 0  |         if (partStart->getType() != UMSGPAT_PART_TYPE_MSG_START) { | 
540  |  |             // Bad format  | 
541  | 0  |             continue;  | 
542  | 0  |         }  | 
543  |  |  | 
544  | 0  |         const MessagePattern::Part* partLimit = &msgPattern.getPart(partIndex++);  | 
545  | 0  |         if (partLimit->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT) { | 
546  |  |             // Bad format  | 
547  | 0  |             continue;  | 
548  | 0  |         }  | 
549  |  |  | 
550  | 0  |         UnicodeString currArg = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit());  | 
551  | 0  |         if (rbnfLenientScanner != NULL) { | 
552  |  |             // Check if non-lenient rule finds the text before call lenient parsing  | 
553  | 0  |             int32_t tempIndex = source.indexOf(currArg, startingAt);  | 
554  | 0  |             if (tempIndex >= 0) { | 
555  | 0  |                 currMatchIndex = tempIndex;  | 
556  | 0  |             } else { | 
557  |  |                 // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us.  | 
558  | 0  |                 int32_t length = -1;  | 
559  | 0  |                 currMatchIndex = rbnfLenientScanner->findTextLenient(source, currArg, startingAt, &length);  | 
560  | 0  |             }  | 
561  | 0  |         }  | 
562  | 0  |         else { | 
563  | 0  |             currMatchIndex = source.indexOf(currArg, startingAt);  | 
564  | 0  |         }  | 
565  | 0  |         if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) { | 
566  | 0  |             matchedIndex = currMatchIndex;  | 
567  | 0  |             matchedWord = currArg;  | 
568  | 0  |             keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit());  | 
569  | 0  |         }  | 
570  | 0  |     }  | 
571  | 0  |     if (matchedIndex >= 0) { | 
572  | 0  |         pos.setBeginIndex(matchedIndex);  | 
573  | 0  |         pos.setEndIndex(matchedIndex + matchedWord.length());  | 
574  | 0  |         result.setString(keyword);  | 
575  | 0  |         return;  | 
576  | 0  |     }  | 
577  |  |  | 
578  |  |     // Not found!  | 
579  | 0  |     pos.setBeginIndex(-1);  | 
580  | 0  |     pos.setEndIndex(-1);  | 
581  | 0  | }  | 
582  |  |  | 
583  | 0  | PluralFormat::PluralSelector::~PluralSelector() {} | 
584  |  |  | 
585  | 0  | PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { | 
586  | 0  |     delete pluralRules;  | 
587  | 0  | }  | 
588  |  |  | 
589  |  | UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number,  | 
590  | 0  |                                                           UErrorCode& /*ec*/) const { | 
591  | 0  |     (void)number;  // unused except in the assertion  | 
592  | 0  |     IFixedDecimal *dec=static_cast<IFixedDecimal *>(context);  | 
593  | 0  |     return pluralRules->select(*dec);  | 
594  | 0  | }  | 
595  |  |  | 
596  | 0  | void PluralFormat::PluralSelectorAdapter::reset() { | 
597  | 0  |     delete pluralRules;  | 
598  | 0  |     pluralRules = NULL;  | 
599  | 0  | }  | 
600  |  |  | 
601  |  |  | 
602  |  | U_NAMESPACE_END  | 
603  |  |  | 
604  |  |  | 
605  |  | #endif /* #if !UCONFIG_NO_FORMATTING */  | 
606  |  |  | 
607  |  | //eof  |