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