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