Home | History | Annotate | Download | only in text
      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) 2008-2014, Google, International Business Machines
      7  * Corporation and others. All Rights Reserved.
      8  **************************************************************************
      9  */
     10 package android.icu.text;
     11 
     12 import java.io.ObjectStreamException;
     13 import java.text.FieldPosition;
     14 import java.text.ParseException;
     15 import java.text.ParsePosition;
     16 import java.util.HashMap;
     17 import java.util.Locale;
     18 import java.util.Map;
     19 import java.util.Map.Entry;
     20 import java.util.MissingResourceException;
     21 import java.util.Set;
     22 import java.util.TreeMap;
     23 
     24 import android.icu.impl.ICUData;
     25 import android.icu.impl.ICUResourceBundle;
     26 import android.icu.impl.UResource;
     27 import android.icu.util.Measure;
     28 import android.icu.util.TimeUnit;
     29 import android.icu.util.TimeUnitAmount;
     30 import android.icu.util.ULocale;
     31 import android.icu.util.ULocale.Category;
     32 import android.icu.util.UResourceBundle;
     33 
     34 
     35 /**
     36  * Format or parse a TimeUnitAmount, using plural rules for the units where available.
     37  *
     38  * <P>
     39  * Code Sample:
     40  * <pre>
     41  *   // create a time unit instance.
     42  *   // only SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, and YEAR are supported
     43  *   TimeUnit timeUnit = TimeUnit.SECOND;
     44  *   // create time unit amount instance - a combination of Number and time unit
     45  *   TimeUnitAmount source = new TimeUnitAmount(2, timeUnit);
     46  *   // create time unit format instance
     47  *   TimeUnitFormat format = new TimeUnitFormat();
     48  *   // set the locale of time unit format
     49  *   format.setLocale(new ULocale("en"));
     50  *   // format a time unit amount
     51  *   String formatted = format.format(source);
     52  *   System.out.println(formatted);
     53  *   try {
     54  *       // parse a string into time unit amount
     55  *       TimeUnitAmount result = (TimeUnitAmount) format.parseObject(formatted);
     56  *       // result should equal to source
     57  *   } catch (ParseException e) {
     58  *   }
     59  * </pre>
     60  *
     61  * <P>
     62  * @see TimeUnitAmount
     63  * @see MeasureFormat
     64  * @author markdavis
     65  * @deprecated ICU 53 use {@link MeasureFormat} instead.
     66  * @hide Only a subset of ICU is exposed in Android
     67  */
     68 @Deprecated
     69 public class TimeUnitFormat extends MeasureFormat {
     70 
     71     /**
     72      * Constant for full name style format.
     73      * For example, the full name for "hour" in English is "hour" or "hours".
     74      * @deprecated ICU 53 see {@link MeasureFormat.FormatWidth}
     75      */
     76     @Deprecated
     77     public static final int FULL_NAME = 0;
     78     /**
     79      * Constant for abbreviated name style format.
     80      * For example, the abbreviated name for "hour" in English is "hr" or "hrs".
     81      * @deprecated ICU 53 see {@link MeasureFormat.FormatWidth}
     82      */
     83     @Deprecated
     84     public static final int ABBREVIATED_NAME = 1;
     85 
     86     private static final int TOTAL_STYLES = 2;
     87 
     88     private static final long serialVersionUID = -3707773153184971529L;
     89 
     90     // These fields are supposed to be the same as the fields in mf. They
     91     // are here for serialization backward compatibility and to support parsing.
     92     private NumberFormat format;
     93     private ULocale locale;
     94     private int style;
     95 
     96     // We use this field in lieu of the super class because the super class
     97     // is immutable while this class is mutable. The contents of the super class
     98     // is an empty shell. Every public method of the super class is overridden to
     99     // delegate to this field. Each time this object mutates, it replaces this field with
    100     // a new immutable instance.
    101     private transient MeasureFormat mf;
    102 
    103     private transient Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
    104     private transient PluralRules pluralRules;
    105     private transient boolean isReady;
    106 
    107     private static final String DEFAULT_PATTERN_FOR_SECOND = "{0} s";
    108     private static final String DEFAULT_PATTERN_FOR_MINUTE = "{0} min";
    109     private static final String DEFAULT_PATTERN_FOR_HOUR = "{0} h";
    110     private static final String DEFAULT_PATTERN_FOR_DAY = "{0} d";
    111     private static final String DEFAULT_PATTERN_FOR_WEEK = "{0} w";
    112     private static final String DEFAULT_PATTERN_FOR_MONTH = "{0} m";
    113     private static final String DEFAULT_PATTERN_FOR_YEAR = "{0} y";
    114 
    115     /**
    116      * Create empty format using full name style, for example, "hours".
    117      * Use setLocale and/or setFormat to modify.
    118      * @deprecated ICU 53 use {@link MeasureFormat} instead.
    119      */
    120     @Deprecated
    121     public TimeUnitFormat() {
    122         mf = MeasureFormat.getInstance(ULocale.getDefault(), FormatWidth.WIDE);
    123         isReady = false;
    124         style = FULL_NAME;
    125     }
    126 
    127     /**
    128      * Create TimeUnitFormat given a ULocale, and using full name style.
    129      * @param locale   locale of this time unit formatter.
    130      * @deprecated ICU 53 use {@link MeasureFormat} instead.
    131      */
    132     @Deprecated
    133     public TimeUnitFormat(ULocale locale) {
    134         this(locale, FULL_NAME);
    135     }
    136 
    137     /**
    138      * Create TimeUnitFormat given a Locale, and using full name style.
    139      * @param locale   locale of this time unit formatter.
    140      * @deprecated ICU 53 use {@link MeasureFormat} instead.
    141      */
    142     @Deprecated
    143     public TimeUnitFormat(Locale locale) {
    144         this(locale, FULL_NAME);
    145     }
    146 
    147     /**
    148      * Create TimeUnitFormat given a ULocale and a formatting style.
    149      * @param locale   locale of this time unit formatter.
    150      * @param style    format style, either FULL_NAME or ABBREVIATED_NAME style.
    151      * @throws IllegalArgumentException if the style is not FULL_NAME or
    152      *                                  ABBREVIATED_NAME style.
    153      * @deprecated ICU 53 use {@link MeasureFormat} instead.
    154      */
    155     @Deprecated
    156     public TimeUnitFormat(ULocale locale, int style) {
    157         if (style < FULL_NAME || style >= TOTAL_STYLES) {
    158             throw new IllegalArgumentException("style should be either FULL_NAME or ABBREVIATED_NAME style");
    159         }
    160         mf = MeasureFormat.getInstance(
    161                 locale, style == FULL_NAME ? FormatWidth.WIDE : FormatWidth.SHORT);
    162         this.style = style;
    163 
    164         // Needed for getLocale(ULocale.VALID_LOCALE)
    165         setLocale(locale, locale);
    166         this.locale = locale;
    167         isReady = false;
    168     }
    169 
    170     private TimeUnitFormat(ULocale locale, int style, NumberFormat numberFormat) {
    171         this(locale, style);
    172         if (numberFormat != null) {
    173             setNumberFormat((NumberFormat) numberFormat.clone());
    174         }
    175     }
    176 
    177     /**
    178      * Create TimeUnitFormat given a Locale and a formatting style.
    179      * @deprecated ICU 53 use {@link MeasureFormat} instead.
    180      */
    181     @Deprecated
    182     public TimeUnitFormat(Locale locale, int style) {
    183         this(ULocale.forLocale(locale),  style);
    184     }
    185 
    186     /**
    187      * Set the locale used for formatting or parsing.
    188      * @param locale   locale of this time unit formatter.
    189      * @return this, for chaining.
    190      * @deprecated ICU 53 see {@link MeasureFormat}.
    191      */
    192     @Deprecated
    193     public TimeUnitFormat setLocale(ULocale locale) {
    194         if (locale != this.locale){
    195             mf = mf.withLocale(locale);
    196 
    197             // Needed for getLocale(ULocale.VALID_LOCALE)
    198             setLocale(locale, locale);
    199             this.locale = locale;
    200             isReady = false;
    201         }
    202         return this;
    203     }
    204 
    205     /**
    206      * Set the locale used for formatting or parsing.
    207      * @param locale   locale of this time unit formatter.
    208      * @return this, for chaining.
    209      * @deprecated ICU 53 see {@link MeasureFormat}.
    210      */
    211     @Deprecated
    212     public TimeUnitFormat setLocale(Locale locale) {
    213         return setLocale(ULocale.forLocale(locale));
    214     }
    215 
    216     /**
    217      * Set the format used for formatting or parsing. Passing null is equivalent to passing
    218      * {@link NumberFormat#getNumberInstance(ULocale)}.
    219      * @param format   the number formatter.
    220      * @return this, for chaining.
    221      * @deprecated ICU 53 see {@link MeasureFormat}.
    222      */
    223     @Deprecated
    224     public TimeUnitFormat setNumberFormat(NumberFormat format) {
    225         if (format == this.format) {
    226             return this;
    227         }
    228         if (format == null) {
    229             if (locale == null) {
    230                 isReady = false;
    231                 mf = mf.withLocale(ULocale.getDefault());
    232             } else {
    233                 this.format = NumberFormat.getNumberInstance(locale);
    234                 mf = mf.withNumberFormat(this.format);
    235             }
    236         } else {
    237             this.format = format;
    238             mf = mf.withNumberFormat(this.format);
    239         }
    240         return this;
    241     }
    242 
    243 
    244     /**
    245      * Format a TimeUnitAmount.
    246      * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
    247      * @deprecated ICU 53 see {@link MeasureFormat}.
    248      */
    249     @Deprecated
    250     public StringBuffer format(Object obj, StringBuffer toAppendTo,
    251             FieldPosition pos) {
    252         return mf.format(obj, toAppendTo, pos);
    253     }
    254 
    255     /**
    256      * Parse a TimeUnitAmount.
    257      * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
    258      * @deprecated ICU 53 see {@link MeasureFormat}.
    259      */
    260     @Deprecated
    261     @Override
    262     public TimeUnitAmount parseObject(String source, ParsePosition pos) {
    263         if (!isReady) {
    264             setup();
    265         }
    266         Number resultNumber = null;
    267         TimeUnit resultTimeUnit = null;
    268         int oldPos = pos.getIndex();
    269         int newPos = -1;
    270         int longestParseDistance = 0;
    271         String countOfLongestMatch = null;
    272         // we don't worry too much about speed on parsing, but this can be optimized later if needed.
    273         // Parse by iterating through all available patterns
    274         // and looking for the longest match.
    275         for (TimeUnit timeUnit : timeUnitToCountToPatterns.keySet()) {
    276             Map<String, Object[]> countToPattern = timeUnitToCountToPatterns.get(timeUnit);
    277             for (Entry<String, Object[]> patternEntry : countToPattern.entrySet()) {
    278                 String count = patternEntry.getKey();
    279                 for (int styl = FULL_NAME; styl < TOTAL_STYLES; ++styl) {
    280                     MessageFormat pattern = (MessageFormat) (patternEntry.getValue())[styl];
    281                     pos.setErrorIndex(-1);
    282                     pos.setIndex(oldPos);
    283                     // see if we can parse
    284                     Object parsed = pattern.parseObject(source, pos);
    285                     if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
    286                         // nothing parsed
    287                         continue;
    288                     }
    289                     Number temp = null;
    290                     if (((Object[]) parsed).length != 0) {
    291                         // pattern with Number as beginning,
    292                         // such as "{0} d".
    293                         // check to make sure that the timeUnit is consistent
    294                         Object tempObj = ((Object[]) parsed)[0];
    295                         if (tempObj instanceof Number) {
    296                             temp = (Number) tempObj;
    297                         } else {
    298                             // Since we now format the number ourselves, parseObject will likely give us back a String
    299                             // for
    300                             // the number. When this happens we must parse the formatted number ourselves.
    301                             try {
    302                                 temp = format.parse(tempObj.toString());
    303                             } catch (ParseException e) {
    304                                 continue;
    305                             }
    306                         }
    307                     }
    308                     int parseDistance = pos.getIndex() - oldPos;
    309                     if (parseDistance > longestParseDistance) {
    310                         resultNumber = temp;
    311                         resultTimeUnit = timeUnit;
    312                         newPos = pos.getIndex();
    313                         longestParseDistance = parseDistance;
    314                         countOfLongestMatch = count;
    315                     }
    316                 }
    317             }
    318         }
    319         /*
    320          * After find the longest match, parse the number. Result number could be null for the pattern without number
    321          * pattern. such as unit pattern in Arabic. When result number is null, use plural rule to set the number.
    322          */
    323         if (resultNumber == null && longestParseDistance != 0) {
    324             // set the number using plurrual count
    325             if (countOfLongestMatch.equals("zero")) {
    326                 resultNumber = Integer.valueOf(0);
    327             } else if (countOfLongestMatch.equals("one")) {
    328                 resultNumber = Integer.valueOf(1);
    329             } else if (countOfLongestMatch.equals("two")) {
    330                 resultNumber = Integer.valueOf(2);
    331             } else {
    332                 // should not happen.
    333                 // TODO: how to handle?
    334                 resultNumber = Integer.valueOf(3);
    335             }
    336         }
    337         if (longestParseDistance == 0) {
    338             pos.setIndex(oldPos);
    339             pos.setErrorIndex(0);
    340             return null;
    341         } else {
    342             pos.setIndex(newPos);
    343             pos.setErrorIndex(-1);
    344             return new TimeUnitAmount(resultNumber, resultTimeUnit);
    345         }
    346     }
    347 
    348     private void setup() {
    349         if (locale == null) {
    350             if (format != null) {
    351                 locale = format.getLocale(null);
    352             } else {
    353                 locale = ULocale.getDefault(Category.FORMAT);
    354             }
    355             // Needed for getLocale(ULocale.VALID_LOCALE)
    356             setLocale(locale, locale);
    357         }
    358         if (format == null) {
    359             format = NumberFormat.getNumberInstance(locale);
    360         }
    361         pluralRules = PluralRules.forLocale(locale);
    362         timeUnitToCountToPatterns = new HashMap<TimeUnit, Map<String, Object[]>>();
    363         Set<String> pluralKeywords = pluralRules.getKeywords();
    364         setup("units/duration", timeUnitToCountToPatterns, FULL_NAME, pluralKeywords);
    365         setup("unitsShort/duration", timeUnitToCountToPatterns, ABBREVIATED_NAME, pluralKeywords);
    366         isReady = true;
    367     }
    368 
    369     private static final class TimeUnitFormatSetupSink extends UResource.Sink {
    370         Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
    371         int style;
    372         Set<String> pluralKeywords;
    373         ULocale locale;
    374         boolean beenHere;
    375 
    376         TimeUnitFormatSetupSink(Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
    377                 int style, Set<String> pluralKeywords, ULocale locale) {
    378             this.timeUnitToCountToPatterns = timeUnitToCountToPatterns;
    379             this.style = style;
    380             this.pluralKeywords = pluralKeywords;
    381             this.locale = locale;
    382             this.beenHere = false;
    383         }
    384 
    385         @Override
    386         public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
    387             // Skip all put() calls except the first one -- discard all fallback data.
    388             if (beenHere) {
    389                 return;
    390             } else {
    391                 beenHere = true;
    392             }
    393 
    394             UResource.Table units = value.getTable();
    395             for (int i = 0; units.getKeyAndValue(i, key, value); ++i) {
    396                 String timeUnitName = key.toString();
    397                 TimeUnit timeUnit = null;
    398 
    399                 if (timeUnitName.equals("year")) {
    400                     timeUnit = TimeUnit.YEAR;
    401                 } else if (timeUnitName.equals("month")) {
    402                     timeUnit = TimeUnit.MONTH;
    403                 } else if (timeUnitName.equals("day")) {
    404                     timeUnit = TimeUnit.DAY;
    405                 } else if (timeUnitName.equals("hour")) {
    406                     timeUnit = TimeUnit.HOUR;
    407                 } else if (timeUnitName.equals("minute")) {
    408                     timeUnit = TimeUnit.MINUTE;
    409                 } else if (timeUnitName.equals("second")) {
    410                     timeUnit = TimeUnit.SECOND;
    411                 } else if (timeUnitName.equals("week")) {
    412                     timeUnit = TimeUnit.WEEK;
    413                 } else {
    414                     continue;
    415                 }
    416 
    417                 Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
    418                 if (countToPatterns == null) {
    419                     countToPatterns = new TreeMap<String, Object[]>();
    420                     timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
    421                 }
    422 
    423                 UResource.Table countsToPatternTable = value.getTable();
    424                 for (int j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
    425                     String pluralCount = key.toString();
    426                     if (!pluralKeywords.contains(pluralCount))
    427                         continue;
    428                     // save both full name and abbreviated name in one table
    429                     // is good space-wise, but it degrades performance,
    430                     // since it needs to check whether the needed space
    431                     // is already allocated or not.
    432                     Object[] pair = countToPatterns.get(pluralCount);
    433                     if (pair == null) {
    434                         pair = new Object[2];
    435                         countToPatterns.put(pluralCount, pair);
    436                     }
    437                     if (pair[style] == null) {
    438                         String pattern = value.getString();
    439                         final MessageFormat messageFormat = new MessageFormat(pattern, locale);
    440                         pair[style] = messageFormat;
    441                     }
    442                 }
    443             }
    444         }
    445     }
    446 
    447     private void setup(String resourceKey, Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns, int style,
    448             Set<String> pluralKeywords) {
    449         // fill timeUnitToCountToPatterns from resource file
    450         try {
    451 
    452             ICUResourceBundle resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(
    453                     ICUData.ICU_UNIT_BASE_NAME, locale);
    454 
    455             TimeUnitFormatSetupSink sink = new TimeUnitFormatSetupSink(
    456                     timeUnitToCountToPatterns, style, pluralKeywords, locale);
    457             resource.getAllItemsWithFallback(resourceKey, sink);
    458         } catch (MissingResourceException e) {
    459         }
    460         // there should be patterns for each plural rule in each time unit.
    461         // For each time unit,
    462         // for each plural rule, following is unit pattern fall-back rule:
    463         // ( for example: "one" hour )
    464         // look for its unit pattern in its locale tree.
    465         // if pattern is not found in its own locale, such as de_DE,
    466         // look for the pattern in its parent, such as de,
    467         // keep looking till found or till root.
    468         // if the pattern is not found in root either,
    469         // fallback to plural count "other",
    470         // look for the pattern of "other" in the locale tree:
    471         // "de_DE" to "de" to "root".
    472         // If not found, fall back to value of
    473         // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
    474         //
    475         // Following is consistency check to create pattern for each
    476         // plural rule in each time unit using above fall-back rule.
    477         //
    478         final TimeUnit[] timeUnits = TimeUnit.values();
    479         Set<String> keywords = pluralRules.getKeywords();
    480         for (int i = 0; i < timeUnits.length; ++i) {
    481             // for each time unit,
    482             // get all the patterns for each plural rule in this locale.
    483             final TimeUnit timeUnit = timeUnits[i];
    484             Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
    485             if (countToPatterns == null) {
    486                 countToPatterns = new TreeMap<String, Object[]>();
    487                 timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
    488             }
    489             for (String pluralCount : keywords) {
    490                 if (countToPatterns.get(pluralCount) == null || countToPatterns.get(pluralCount)[style] == null) {
    491                     // look through parents
    492                     searchInTree(resourceKey, style, timeUnit, pluralCount, pluralCount, countToPatterns);
    493                 }
    494             }
    495         }
    496     }
    497 
    498     // srcPluralCount is the original plural count on which the pattern is
    499     // searched for.
    500     // searchPluralCount is the fallback plural count.
    501     // For example, to search for pattern for ""one" hour",
    502     // "one" is the srcPluralCount,
    503     // if the pattern is not found even in root, fallback to
    504     // using patterns of plural count "other",
    505     // then, "other" is the searchPluralCount.
    506     private void searchInTree(String resourceKey, int styl, TimeUnit timeUnit, String srcPluralCount,
    507             String searchPluralCount, Map<String, Object[]> countToPatterns) {
    508         ULocale parentLocale = locale;
    509         String srcTimeUnitName = timeUnit.toString();
    510         while (parentLocale != null) {
    511             try {
    512                 // look for pattern for srcPluralCount in locale tree
    513                 ICUResourceBundle unitsRes = (ICUResourceBundle) UResourceBundle.getBundleInstance(
    514                         ICUData.ICU_UNIT_BASE_NAME, parentLocale);
    515                 unitsRes = unitsRes.getWithFallback(resourceKey);
    516                 ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(srcTimeUnitName);
    517                 String pattern = oneUnitRes.getStringWithFallback(searchPluralCount);
    518                 final MessageFormat messageFormat = new MessageFormat(pattern, locale);
    519                 Object[] pair = countToPatterns.get(srcPluralCount);
    520                 if (pair == null) {
    521                     pair = new Object[2];
    522                     countToPatterns.put(srcPluralCount, pair);
    523                 }
    524                 pair[styl] = messageFormat;
    525                 return;
    526             } catch (MissingResourceException e) {
    527             }
    528             parentLocale = parentLocale.getFallback();
    529         }
    530         // if no unitsShort resource was found even after fallback to root locale
    531         // then search the units resource fallback from the current level to root
    532         if (parentLocale == null && resourceKey.equals("unitsShort")) {
    533             searchInTree("units", styl, timeUnit, srcPluralCount, searchPluralCount, countToPatterns);
    534             if (countToPatterns.get(srcPluralCount) != null
    535                     && countToPatterns.get(srcPluralCount)[styl] != null) {
    536                 return;
    537             }
    538         }
    539         // if not found the pattern for this plural count at all,
    540         // fall-back to plural count "other"
    541         if (searchPluralCount.equals("other")) {
    542             // set default fall back the same as the resource in root
    543             MessageFormat messageFormat = null;
    544             if (timeUnit == TimeUnit.SECOND) {
    545                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_SECOND, locale);
    546             } else if (timeUnit == TimeUnit.MINUTE) {
    547                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MINUTE, locale);
    548             } else if (timeUnit == TimeUnit.HOUR) {
    549                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_HOUR, locale);
    550             } else if (timeUnit == TimeUnit.WEEK) {
    551                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_WEEK, locale);
    552             } else if (timeUnit == TimeUnit.DAY) {
    553                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_DAY, locale);
    554             } else if (timeUnit == TimeUnit.MONTH) {
    555                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MONTH, locale);
    556             } else if (timeUnit == TimeUnit.YEAR) {
    557                 messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_YEAR, locale);
    558             }
    559             Object[] pair = countToPatterns.get(srcPluralCount);
    560             if (pair == null) {
    561                 pair = new Object[2];
    562                 countToPatterns.put(srcPluralCount, pair);
    563             }
    564             pair[styl] = messageFormat;
    565         } else {
    566             // fall back to rule "other", and search in parents
    567             searchInTree(resourceKey, styl, timeUnit, srcPluralCount, "other", countToPatterns);
    568         }
    569     }
    570 
    571     // boilerplate code to make TimeUnitFormat otherwise follow the contract of
    572     // MeasureFormat
    573 
    574 
    575     /**
    576      * @deprecated This API is ICU internal only.
    577      * @hide draft / provisional / internal are hidden on Android
    578      */
    579     @Deprecated
    580     @Override
    581     public StringBuilder formatMeasures(
    582             StringBuilder appendTo, FieldPosition fieldPosition, Measure... measures) {
    583         return mf.formatMeasures(appendTo, fieldPosition, measures);
    584     }
    585 
    586     /**
    587      * @deprecated This API is ICU internal only.
    588      * @hide draft / provisional / internal are hidden on Android
    589      */
    590     @Deprecated
    591     @Override
    592     public MeasureFormat.FormatWidth getWidth() {
    593         return mf.getWidth();
    594     }
    595 
    596     /**
    597      * @deprecated This API is ICU internal only.
    598      * @hide draft / provisional / internal are hidden on Android
    599      */
    600     @Deprecated
    601     @Override
    602     public NumberFormat getNumberFormat() {
    603         return mf.getNumberFormat();
    604     }
    605 
    606     /**
    607      * @deprecated This API is ICU internal only.
    608      * @hide draft / provisional / internal are hidden on Android
    609      */
    610     @Deprecated
    611     @Override
    612     public Object clone() {
    613         TimeUnitFormat result = (TimeUnitFormat) super.clone();
    614         result.format = (NumberFormat) format.clone();
    615         return result;
    616     }
    617     // End boilerplate.
    618 
    619     // Serialization
    620 
    621     private Object writeReplace() throws ObjectStreamException {
    622         return mf.toTimeUnitProxy();
    623     }
    624 
    625     // Preserve backward serialize backward compatibility.
    626     private Object readResolve() throws ObjectStreamException {
    627         return new TimeUnitFormat(locale, style, format);
    628     }
    629 }
    630