]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/duration/impl/PeriodFormatterData.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / duration / impl / PeriodFormatterData.java
1 /*\r
2 ******************************************************************************\r
3 * Copyright (C) 2009, International Business Machines Corporation and   *\r
4 * others. All Rights Reserved.                                               *\r
5 ******************************************************************************\r
6 */\r
7 \r
8 package com.ibm.icu.impl.duration.impl;\r
9 \r
10 import com.ibm.icu.impl.duration.TimeUnit;\r
11 \r
12 import com.ibm.icu.impl.duration.impl.DataRecord.*;\r
13 \r
14 \r
15 /**\r
16  * PeriodFormatterData provides locale-specific data used to format\r
17  * relative dates and times, and convenience api to access it.\r
18  *\r
19  * An instance of PeriodFormatterData is usually created by requesting\r
20  * data for a given locale from an PeriodFormatterDataService.\r
21  */\r
22 public class PeriodFormatterData {\r
23   final DataRecord dr;\r
24   String localeName;\r
25 \r
26   // debug\r
27   public static boolean trace = false;\r
28 \r
29   public PeriodFormatterData(String localeName, DataRecord dr) {\r
30     this.dr = dr;\r
31     this.localeName = localeName;\r
32     if(localeName == null) {\r
33         throw new NullPointerException("localename is null");\r
34     }\r
35 //    System.err.println("** localeName is " + localeName);\r
36     if (dr == null) {\r
37 //      Thread.dumpStack();\r
38       throw new NullPointerException("data record is null");\r
39     }\r
40   }\r
41 \r
42   // none - chinese (all forms the same)\r
43   // plural - english, special form for 1\r
44   // dual - special form for 1 and 2\r
45   // paucal - russian, special form for 1, for 2-4 and n > 20 && n % 10 == 2-4\r
46   // rpt_dual_few - slovenian, special form for 1, 2, 3-4 and n as above\r
47   // hebrew, dual plus singular form for years > 11\r
48   // arabic, dual, plus singular form for all terms > 10\r
49 \r
50   /**\r
51    * Return the pluralization format used by this locale.\r
52    * @return the pluralization format\r
53    */\r
54   public int pluralization() {\r
55     return dr.pl;\r
56   }\r
57 \r
58   /**\r
59    * Return true if zeros are allowed in the display.\r
60    * @return true if zeros should be allowed\r
61    */\r
62   public boolean allowZero() {\r
63     return dr.allowZero;\r
64   }\r
65 \r
66   public boolean weeksAloneOnly() {\r
67     return dr.weeksAloneOnly;\r
68   }\r
69 \r
70   public int useMilliseconds() {\r
71     return dr.useMilliseconds;\r
72   }\r
73 \r
74   /**\r
75    * Append the appropriate prefix to the string builder, depending on whether and\r
76    * how a limit and direction are to be displayed.\r
77    *\r
78    * @param tl how and whether to display the time limit\r
79    * @param td how and whether to display the time direction\r
80    * @param sb the string builder to which to append the text\r
81    * @return true if a following digit will require a digit prefix\r
82    */\r
83   public boolean appendPrefix(int tl, int td, StringBuffer sb) {\r
84     if (dr.scopeData != null) {\r
85       int ix = tl * 3 + td;\r
86       ScopeData sd = dr.scopeData[ix];\r
87       if (sd != null) {\r
88         String prefix = sd.prefix;\r
89         if (prefix != null) {\r
90           sb.append(prefix);\r
91           return sd.requiresDigitPrefix;\r
92         }\r
93       }\r
94     }\r
95     return false;\r
96   }\r
97 \r
98   /**\r
99    * Append the appropriate suffix to the string builder, depending on whether and\r
100    * how a limit and direction are to be displayed.\r
101    *\r
102    * @param tl how and whether to display the time limit\r
103    * @param td how and whether to display the time direction\r
104    * @param sb the string builder to which to append the text\r
105    */\r
106   public void appendSuffix(int tl, int td, StringBuffer sb) {\r
107     if (dr.scopeData != null) {\r
108       int ix = tl * 3 + td;\r
109       ScopeData sd = dr.scopeData[ix];\r
110       if (sd != null) {\r
111         String suffix = sd.suffix;\r
112         if (suffix != null) {\r
113           if (trace) {\r
114             System.out.println("appendSuffix '" + suffix + "'");\r
115           }\r
116           sb.append(suffix);\r
117         }\r
118       }\r
119     }\r
120   }\r
121 \r
122   /**\r
123    * Append the count and unit to the string builder.\r
124    *\r
125    * @param unit the unit to append\r
126    * @param count the count of units, * 1000\r
127    * @param cv the format to use for displaying the count\r
128    * @param uv the format to use for displaying the unit\r
129    * @param useCountSep if false, force no separator between count and unit\r
130    * @param useDigitPrefix if true, use the digit prefix\r
131    * @param multiple true if there are multiple units in this string\r
132    * @param last true if this is the last unit\r
133    * @param wasSkipped true if the unit(s) before this were skipped\r
134    * @param sb the string builder to which to append the text\r
135    * @return true if will require skip marker\r
136    */\r
137   @SuppressWarnings("fallthrough")\r
138   public boolean appendUnit(TimeUnit unit, int count, int cv, \r
139                             int uv, boolean useCountSep, \r
140                             boolean useDigitPrefix, boolean multiple, \r
141                             boolean last, boolean wasSkipped, \r
142                             StringBuffer sb) {\r
143     int px = unit.ordinal();\r
144 \r
145     boolean willRequireSkipMarker = false;\r
146     if (dr.requiresSkipMarker != null && dr.requiresSkipMarker[px] && \r
147         dr.skippedUnitMarker != null) {\r
148       if (!wasSkipped && last) {\r
149         sb.append(dr.skippedUnitMarker);\r
150       }\r
151       willRequireSkipMarker = true;\r
152     }\r
153 \r
154     if (uv != EUnitVariant.PLURALIZED) {\r
155       boolean useMedium = uv == EUnitVariant.MEDIUM; \r
156       String[] names = useMedium ? dr.mediumNames : dr.shortNames;\r
157       if (names == null || names[px] == null) {\r
158         names = useMedium ? dr.shortNames : dr.mediumNames;\r
159       }\r
160       if (names != null && names[px] != null) {\r
161         appendCount(unit, false, false, count, cv, useCountSep, \r
162                     names[px], last, sb); // omit suffix, ok?\r
163         return false; // omit skip marker\r
164       }\r
165     }\r
166 \r
167     // check cv\r
168     if (cv == ECountVariant.HALF_FRACTION && dr.halfSupport != null) {\r
169       switch (dr.halfSupport[px]) {\r
170         case EHalfSupport.YES: break;\r
171         case EHalfSupport.ONE_PLUS:\r
172           if (count > 1000) {\r
173             break;\r
174           }\r
175           // else fall through to decimal\r
176         case EHalfSupport.NO: {\r
177           count = (count / 500) * 500;  // round to 1/2\r
178           cv = ECountVariant.DECIMAL1; \r
179         } break;\r
180       }\r
181     }\r
182           \r
183     String name = null;\r
184     int form = computeForm(unit, count, cv, multiple && last);\r
185     if (form == FORM_SINGULAR_SPELLED) {\r
186       if (dr.singularNames == null) {\r
187         form = FORM_SINGULAR;\r
188         name = dr.pluralNames[px][form];\r
189       } else {\r
190         name = dr.singularNames[px];\r
191       }\r
192     } else if (form == FORM_SINGULAR_NO_OMIT) {\r
193       name = dr.pluralNames[px][FORM_SINGULAR];\r
194     } else if (form == FORM_HALF_SPELLED) {\r
195       name = dr.halfNames[px];\r
196     } else { \r
197       try {\r
198         name = dr.pluralNames[px][form];\r
199       } catch (NullPointerException e) {\r
200         System.out.println("Null Pointer in PeriodFormatterData["+localeName+"].au px: " + px + " form: " + form + " pn: " + dr.pluralNames);\r
201         throw e;\r
202       }\r
203     }\r
204     if (name == null) {\r
205       form = FORM_PLURAL;\r
206       name = dr.pluralNames[px][form];\r
207     }\r
208 \r
209     boolean omitCount =\r
210       (form == FORM_SINGULAR_SPELLED || form == FORM_HALF_SPELLED) ||\r
211       (dr.omitSingularCount && form == FORM_SINGULAR) ||\r
212       (dr.omitDualCount && form == FORM_DUAL);\r
213 \r
214     int suffixIndex = appendCount(unit, omitCount, useDigitPrefix, count, cv, \r
215                                   useCountSep, name, last, sb);\r
216     if (last && suffixIndex >= 0) {\r
217       String suffix = null;\r
218       if (dr.rqdSuffixes != null && suffixIndex < dr.rqdSuffixes.length) {\r
219         suffix = dr.rqdSuffixes[suffixIndex];\r
220       }\r
221       if (suffix == null && dr.optSuffixes != null && \r
222           suffixIndex < dr.optSuffixes.length) {\r
223         suffix = dr.optSuffixes[suffixIndex];\r
224       }\r
225       if (suffix != null) {\r
226         sb.append(suffix);\r
227       }\r
228     }\r
229     return willRequireSkipMarker;\r
230   }\r
231 \r
232   /**\r
233    * Append a count to the string builder.\r
234    *\r
235    * @param unit the unit\r
236    * @param count the count\r
237    * @param cv the format to use for displaying the count\r
238    * @param useSep whether to use the count separator, if available\r
239    * @param name the term name\r
240    * @param last true if this is the last unit to be formatted\r
241    * @param sb the string builder to which to append the text\r
242    * @return index to use if might have required or optional suffix, or -1 if none required\r
243    */\r
244   public int appendCount(TimeUnit unit, boolean omitCount, \r
245                          boolean useDigitPrefix, \r
246                          int count, int cv, boolean useSep, \r
247                          String name, boolean last, StringBuffer sb) {\r
248     if (cv == ECountVariant.HALF_FRACTION && dr.halves == null) {\r
249       cv = ECountVariant.INTEGER;\r
250     }\r
251 \r
252     if (!omitCount && useDigitPrefix && dr.digitPrefix != null) {\r
253       sb.append(dr.digitPrefix);\r
254     }\r
255 \r
256     int index = unit.ordinal();\r
257     switch (cv) {\r
258       case ECountVariant.INTEGER: {\r
259         if (!omitCount) {\r
260           appendInteger(count/1000, 1, 10, sb);\r
261         }\r
262       } break;\r
263 \r
264       case ECountVariant.INTEGER_CUSTOM: {\r
265         int val = count / 1000;\r
266         // only custom names we have for now\r
267         if (unit == TimeUnit.MINUTE && \r
268             (dr.fiveMinutes != null || dr.fifteenMinutes != null)) {\r
269           if (val != 0 && val % 5 == 0) {\r
270             if (dr.fifteenMinutes != null && (val == 15 || val == 45)) {\r
271               val = val == 15 ? 1 : 3;\r
272               if (!omitCount) appendInteger(val, 1, 10, sb);\r
273               name = dr.fifteenMinutes;\r
274               index = 8; // hack\r
275               break;\r
276             }\r
277             if (dr.fiveMinutes != null) {\r
278               val = val / 5;\r
279               if (!omitCount) appendInteger(val, 1, 10, sb);\r
280               name = dr.fiveMinutes;\r
281               index = 9; // hack\r
282               break;\r
283             }\r
284           }\r
285         }\r
286         if (!omitCount) appendInteger(val, 1, 10, sb);\r
287       } break;\r
288 \r
289       case ECountVariant.HALF_FRACTION: {\r
290         // 0, 1/2, 1, 1-1/2...\r
291         int v = count / 500;\r
292         if (v != 1) {\r
293           if (!omitCount) appendCountValue(count, 1, 0, sb);\r
294         }\r
295         if ((v & 0x1) == 1) {\r
296           // hack, using half name\r
297           if (v == 1 && dr.halfNames != null && dr.halfNames[index] != null) {\r
298             sb.append(name);\r
299             return last ? index : -1;\r
300           }\r
301 \r
302           int solox = v == 1 ? 0 : 1;\r
303           if (dr.genders != null && dr.halves.length > 2) {\r
304             if (dr.genders[index] == EGender.F) {\r
305               solox += 2;\r
306             }\r
307           }\r
308           int hp = dr.halfPlacements == null \r
309               ? EHalfPlacement.PREFIX\r
310               : dr.halfPlacements[solox & 0x1];\r
311           String half = dr.halves[solox];\r
312           String measure = dr.measures == null ? null : dr.measures[index];\r
313           switch (hp) {\r
314             case EHalfPlacement.PREFIX:\r
315               sb.append(half);\r
316               break;\r
317             case EHalfPlacement.AFTER_FIRST: {\r
318               if (measure != null) {\r
319                 sb.append(measure);\r
320                 sb.append(half);\r
321                 if (useSep && !omitCount) {\r
322                   sb.append(dr.countSep);\r
323                 } \r
324                 sb.append(name);\r
325               } else { // ignore sep completely\r
326                 sb.append(name);\r
327                 sb.append(half);\r
328                 return last ? index : -1; // might use suffix\r
329               }\r
330             } return -1; // exit early\r
331             case EHalfPlacement.LAST: {\r
332               if (measure != null) {\r
333                 sb.append(measure);\r
334               }\r
335               if (useSep && !omitCount) {\r
336                 sb.append(dr.countSep);\r
337               }\r
338               sb.append(name);\r
339               sb.append(half);\r
340             } return last ? index : -1; // might use suffix\r
341           }\r
342         }\r
343       } break;\r
344       default: {\r
345         int decimals = 1;\r
346         switch (cv) {\r
347           case ECountVariant.DECIMAL2: decimals = 2; break;\r
348           case ECountVariant.DECIMAL3: decimals = 3; break;\r
349           default: break;\r
350         }\r
351         if (!omitCount) appendCountValue(count, 1, decimals, sb);\r
352       } break;\r
353     }\r
354     if (!omitCount && useSep) {\r
355       sb.append(dr.countSep);\r
356     }\r
357     if (!omitCount && dr.measures != null && index < dr.measures.length) {\r
358       String measure = dr.measures[index];\r
359       if (measure != null) {\r
360         sb.append(measure);\r
361       }\r
362     }\r
363     sb.append(name);\r
364     return last ? index : -1;\r
365   }\r
366 \r
367   /**\r
368    * Append a count value to the builder.\r
369    *\r
370    * @param count the count\r
371    * @param integralDigits the number of integer digits to display\r
372    * @param decimalDigits the number of decimal digits to display, <= 3\r
373    * @param sb the string builder to which to append the text\r
374    */\r
375   public void appendCountValue(int count, int integralDigits, \r
376                                int decimalDigits, StringBuffer sb) {\r
377     int ival = count / 1000;\r
378     if (decimalDigits == 0) {\r
379       appendInteger(ival, integralDigits, 10, sb);\r
380       return;\r
381     }\r
382 \r
383     if (dr.requiresDigitSeparator && sb.length() > 0) {\r
384       sb.append(' ');\r
385     }\r
386     appendDigits(ival, integralDigits, 10, sb);\r
387     int dval = count % 1000;\r
388     if (decimalDigits == 1) {\r
389       dval /= 100;\r
390     } else if (decimalDigits == 2) {\r
391       dval /= 10;\r
392     }\r
393     sb.append(dr.decimalSep);\r
394     appendDigits(dval, decimalDigits, decimalDigits, sb);\r
395     if (dr.requiresDigitSeparator) {\r
396       sb.append(' ');\r
397     }\r
398   }\r
399 \r
400   public void appendInteger(int num, int mindigits, int maxdigits, \r
401                             StringBuffer sb) {\r
402     if (dr.numberNames != null && num < dr.numberNames.length) {\r
403       String name = dr.numberNames[num];\r
404       if (name != null) {\r
405         sb.append(name);\r
406         return;\r
407       }\r
408     }\r
409 \r
410     if (dr.requiresDigitSeparator && sb.length() > 0) {\r
411       sb.append(' ');\r
412     }\r
413     switch (dr.numberSystem) {\r
414       case ENumberSystem.DEFAULT: appendDigits(num, mindigits, maxdigits, sb); break;\r
415       case ENumberSystem.CHINESE_TRADITIONAL: sb.append(\r
416           Utils.chineseNumber(num, Utils.ChineseDigits.TRADITIONAL)); break;\r
417       case ENumberSystem.CHINESE_SIMPLIFIED: sb.append(\r
418           Utils.chineseNumber(num, Utils.ChineseDigits.SIMPLIFIED)); break;\r
419       case ENumberSystem.KOREAN: sb.append(\r
420           Utils.chineseNumber(num, Utils.ChineseDigits.KOREAN)); break;\r
421     }\r
422     if (dr.requiresDigitSeparator) {\r
423       sb.append(' ');\r
424     }\r
425   }\r
426 \r
427   /**\r
428    * Append digits to the string builder, using this.zero for '0' etc.\r
429    *\r
430    * @param num the integer to append\r
431    * @param mindigits the minimum number of digits to append\r
432    * @param maxdigits the maximum number of digits to append\r
433    * @param sb the string builder to which to append the text\r
434    */\r
435   public void appendDigits(long num, int mindigits, int maxdigits,  \r
436                            StringBuffer sb) {\r
437     char[] buf = new char[maxdigits];\r
438     int ix = maxdigits;\r
439     while (ix > 0 && num > 0) {\r
440       buf[--ix] = (char)(dr.zero + (num % 10));\r
441       num /= 10;\r
442     }\r
443     for (int e = maxdigits - mindigits; ix > e;) {\r
444       buf[--ix] = dr.zero;\r
445     }\r
446     sb.append(buf, ix, maxdigits - ix);\r
447   }\r
448 \r
449   /**\r
450    * Append a marker for skipped units internal to a string.\r
451    * @param sb the string builder to which to append the text\r
452    */\r
453   public void appendSkippedUnit(StringBuffer sb) {\r
454     if (dr.skippedUnitMarker != null) {\r
455       sb.append(dr.skippedUnitMarker);\r
456     }\r
457   }\r
458 \r
459   /**\r
460    * Append the appropriate separator between units\r
461    *\r
462    * @param unit the unit to which to append the separator\r
463    * @param afterFirst true if this is the first unit formatted\r
464    * @param beforeLast true if this is the next-to-last unit to be formatted\r
465    * @param sb the string builder to which to append the text\r
466    * @return true if a prefix will be required before a following unit\r
467    */\r
468   public boolean appendUnitSeparator(TimeUnit unit, boolean longSep, \r
469                                      boolean afterFirst, boolean beforeLast, \r
470                                      StringBuffer sb) {\r
471     // long seps\r
472     // false, false "...b', '...d"\r
473     // false, true  "...', and 'c"\r
474     // true, false - "a', '...c"\r
475     // true, true - "a' and 'b"\r
476     if ((longSep && dr.unitSep != null) || dr.shortUnitSep != null) {\r
477       if (longSep && dr.unitSep != null) {\r
478         int ix = (afterFirst ? 2 : 0) + (beforeLast ? 1 : 0);\r
479         sb.append(dr.unitSep[ix]);\r
480         return dr.unitSepRequiresDP != null && dr.unitSepRequiresDP[ix];\r
481       }\r
482       sb.append(dr.shortUnitSep); // todo: investigate whether DP is required\r
483     }\r
484     return false;\r
485   }\r
486 \r
487   private static final int \r
488     FORM_PLURAL = 0,\r
489     FORM_SINGULAR = 1,\r
490     FORM_DUAL = 2,\r
491     FORM_PAUCAL = 3,\r
492     FORM_SINGULAR_SPELLED = 4, // following are not in the pluralization list\r
493     FORM_SINGULAR_NO_OMIT = 5, // a hack\r
494     FORM_HALF_SPELLED = 6;\r
495 \r
496   private int computeForm(TimeUnit unit, int count, int cv, \r
497                           boolean lastOfMultiple) {\r
498     // first check if a particular form is forced by the countvariant.  if\r
499     // SO, just return that.  otherwise convert the count to an integer\r
500     // and use pluralization rules to determine which form to use.\r
501     // careful, can't assume any forms but plural exist.\r
502 \r
503     if (trace) {\r
504       System.err.println("pfd.cf unit: " + unit + " count: " + count + " cv: " + cv + " dr.pl: " + dr.pl);\r
505       Thread.dumpStack();\r
506     }\r
507     if (dr.pl == EPluralization.NONE) {\r
508       return FORM_PLURAL;\r
509     }\r
510     // otherwise, assume we have at least a singular and plural form\r
511 \r
512     int val = count/1000;\r
513 \r
514     switch (cv) {\r
515       case ECountVariant.INTEGER: \r
516       case ECountVariant.INTEGER_CUSTOM: {\r
517         // do more analysis based on floor of count\r
518       } break;\r
519       case ECountVariant.HALF_FRACTION: {\r
520         switch (dr.fractionHandling) {\r
521           case EFractionHandling.FPLURAL:\r
522             return FORM_PLURAL;\r
523 \r
524           case EFractionHandling.FSINGULAR_PLURAL_ANDAHALF:\r
525           case EFractionHandling.FSINGULAR_PLURAL: {\r
526             // if half-floor is 1/2, use singular\r
527             // else if half-floor is not integral, use plural\r
528             // else do more analysis\r
529             int v = count / 500;\r
530             if (v == 1) {\r
531               if (dr.halfNames != null && dr.halfNames[unit.ordinal()] != null) {\r
532                 return FORM_HALF_SPELLED;\r
533               }\r
534               return FORM_SINGULAR_NO_OMIT;\r
535             }\r
536             if ((v & 0x1) == 1) {\r
537               if (dr.pl == EPluralization.ARABIC && v > 21) { // hack\r
538                 return FORM_SINGULAR_NO_OMIT;\r
539               }\r
540               if (v == 3 && dr.pl == EPluralization.PLURAL &&\r
541                   dr.fractionHandling != EFractionHandling.FSINGULAR_PLURAL_ANDAHALF) {\r
542                 return FORM_PLURAL;\r
543               }\r
544             }\r
545             \r
546             // it will display like an integer, so do more analysis\r
547           } break;\r
548 \r
549           case EFractionHandling.FPAUCAL: {\r
550             int v = count / 500;\r
551             if (v == 1 || v == 3) {\r
552               return FORM_PAUCAL;\r
553             }\r
554             // else use integral form\r
555           } break;\r
556 \r
557           default:\r
558             throw new IllegalStateException();\r
559         }\r
560       } break;\r
561       default: { // for all decimals\r
562         switch (dr.decimalHandling) {\r
563           case EDecimalHandling.DPLURAL: break;\r
564           case EDecimalHandling.DSINGULAR: return FORM_SINGULAR_NO_OMIT;\r
565           case EDecimalHandling.DSINGULAR_SUBONE:\r
566             if (count < 1000) {\r
567               return FORM_SINGULAR_NO_OMIT;\r
568             }\r
569             break;\r
570           case EDecimalHandling.DPAUCAL:\r
571             if (dr.pl == EPluralization.PAUCAL) {\r
572               return FORM_PAUCAL;\r
573             }\r
574             break;\r
575           default:\r
576             break;\r
577         }\r
578         return FORM_PLURAL;\r
579       }\r
580     }\r
581 \r
582     // select among pluralization forms\r
583     if (trace && count == 0) {\r
584       System.err.println("EZeroHandling = " + dr.zeroHandling);\r
585     }\r
586     if (count == 0 && dr.zeroHandling == EZeroHandling.ZSINGULAR) {\r
587       return FORM_SINGULAR_SPELLED;\r
588     }\r
589 \r
590     int form = FORM_PLURAL;\r
591     switch(dr.pl) {\r
592       case EPluralization.NONE: break; // never get here\r
593       case EPluralization.PLURAL: {\r
594         if (val == 1) { \r
595           form = FORM_SINGULAR_SPELLED; // defaults to form_singular if no spelled forms\r
596         } \r
597       } break;\r
598       case EPluralization.DUAL: {\r
599         if (val == 2) {\r
600           form = FORM_DUAL; \r
601         } else if (val == 1) {\r
602           form = FORM_SINGULAR; \r
603         } \r
604       } break;\r
605       case EPluralization.PAUCAL: {\r
606         int v = val;\r
607         v = v % 100;\r
608         if (v > 20) {\r
609           v = v % 10;\r
610         }\r
611         if (v == 1) {\r
612           form = FORM_SINGULAR;\r
613         } else if (v > 1 && v < 5) {\r
614           form = FORM_PAUCAL;\r
615         }\r
616       } break;\r
617         /*\r
618       case EPluralization.RPT_DUAL_FEW: {\r
619         int v = val;\r
620         if (v > 20) {\r
621           v = v % 10;\r
622         }\r
623         if (v == 1) {\r
624           form = FORM_SINGULAR;\r
625         } else if (v == 2) {\r
626           form = FORM_DUAL;\r
627         } else if (v > 2 && v < 5) {\r
628           form = FORM_PAUCAL;\r
629         }\r
630       } break;\r
631         */\r
632       case EPluralization.HEBREW: {\r
633         if (val == 2) {\r
634           form = FORM_DUAL;\r
635         } else if (val == 1) {\r
636           if (lastOfMultiple) {\r
637             form = FORM_SINGULAR_SPELLED;\r
638           } else {\r
639             form = FORM_SINGULAR;\r
640           } \r
641         } else if (unit == TimeUnit.YEAR && val > 11) {\r
642           form = FORM_SINGULAR_NO_OMIT;\r
643         }\r
644       } break;\r
645       case EPluralization.ARABIC: {\r
646         if (val == 2) {\r
647           form = FORM_DUAL;\r
648         } else if (val == 1) {\r
649           form = FORM_SINGULAR;\r
650         } else if (val > 10) {\r
651           form = FORM_SINGULAR_NO_OMIT;\r
652         }\r
653       } break;\r
654       default: \r
655         System.err.println("dr.pl is " + dr.pl);\r
656         throw new IllegalStateException();\r
657     }\r
658 \r
659     return form;\r
660   }\r
661 }\r