12ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/* GENERATED SOURCE. DO NOT MODIFY. */
22ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/*
32ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller **********************************************************************
498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert * Copyright (c) 2004-2016, International Business Machines
52ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Corporation and others.  All Rights Reserved.
62ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller **********************************************************************
72ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Author: Alan Liu
82ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Created: April 20, 2004
92ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Since: ICU 3.0
102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller **********************************************************************
112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpackage android.icu.text;
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.Externalizable;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.IOException;
162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.InvalidObjectException;
172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.ObjectInput;
182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.ObjectOutput;
192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.ObjectStreamException;
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.text.FieldPosition;
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.text.ParsePosition;
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Arrays;
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Collection;
242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Date;
252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.EnumMap;
262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashMap;
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Locale;
282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Map;
292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.MissingResourceException;
302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.concurrent.ConcurrentHashMap;
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.DontCareFieldPosition;
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.ICUData;
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.ICUResourceBundle;
352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.SimpleCache;
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.SimplePatternFormatter;
3798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubertimport android.icu.impl.StandardPlural;
3896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubertimport android.icu.impl.UResource;
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.math.BigDecimal;
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.PluralRules.Factory;
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Currency;
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.CurrencyAmount;
4396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubertimport android.icu.util.ICUException;
442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Measure;
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.MeasureUnit;
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.TimeZone;
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale;
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale.Category;
492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.UResourceBundle;
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller// If you update the examples in the doc, don't forget to update MesaureUnitTest.TestExamplesInDocs too.
522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/**
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * A formatter for Measure objects.
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>To format a Measure object, first create a formatter
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * object using a MeasureFormat factory method.  Then use that
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * object's format or formatMeasures methods.
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Here is sample code:
602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <pre>
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      MeasureFormat fmtFr = MeasureFormat.getInstance(
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              ULocale.FRENCH, FormatWidth.SHORT);
632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      Measure measure = new Measure(23, MeasureUnit.CELSIUS);
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 23 °C
662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      System.out.println(fmtFr.format(measure));
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      Measure measureF = new Measure(70, MeasureUnit.FAHRENHEIT);
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 70 °F
712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      System.out.println(fmtFr.format(measureF));
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      MeasureFormat fmtFrFull = MeasureFormat.getInstance(
742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              ULocale.FRENCH, FormatWidth.WIDE);
752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 70 pieds et 5,3 pouces
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      System.out.println(fmtFrFull.formatMeasures(
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(70, MeasureUnit.FOOT),
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(5.3, MeasureUnit.INCH)));
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 1 pied et 1 pouce
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      System.out.println(fmtFrFull.formatMeasures(
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(1, MeasureUnit.FOOT),
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(1, MeasureUnit.INCH)));
842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      MeasureFormat fmtFrNarrow = MeasureFormat.getInstance(
862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ULocale.FRENCH, FormatWidth.NARROW);
872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 1′ 1″
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      System.out.println(fmtFrNarrow.formatMeasures(
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(1, MeasureUnit.FOOT),
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(1, MeasureUnit.INCH)));
912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      MeasureFormat fmtEn = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      // Output: 1 inch, 2 feet
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *      fmtEn.formatMeasures(
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(1, MeasureUnit.INCH),
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *              new Measure(2, MeasureUnit.FOOT));
992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * </pre>
1002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * This class does not do conversions from one unit to another. It simply formats
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * whatever units it is given
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * This class is immutable and thread-safe so long as its deprecated subclass,
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * TimeUnitFormat, is never used. TimeUnitFormat is not thread-safe, and is
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * mutable. Although this class has existing subclasses, this class does not support new
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * sub-classes.
1082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * @see android.icu.text.UFormat
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * @author Alan Liu
1112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic class MeasureFormat extends UFormat {
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Generated by serialver from JDK 1.4.1_01
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static final long serialVersionUID = -7182021401701778240L;
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    private final transient MeasureFormatData cache;
11996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient ImmutableNumberFormat numberFormat;
1212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient FormatWidth formatWidth;
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // PluralRules is documented as being immutable which implies thread-safety.
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient PluralRules rules;
1262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient NumericFormatters numericFormatters;
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient ImmutableNumberFormat currencyFormat;
1302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final transient ImmutableNumberFormat integerFormat;
1322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final SimpleCache<ULocale, MeasureFormatData> localeMeasureFormatData
1342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    = new SimpleCache<ULocale, MeasureFormatData>();
1352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final SimpleCache<ULocale, NumericFormatters> localeToNumericDurationFormatters
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    = new SimpleCache<ULocale,NumericFormatters>();
1382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final Map<MeasureUnit, Integer> hmsTo012 =
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            new HashMap<MeasureUnit, Integer>();
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static {
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        hmsTo012.put(MeasureUnit.HOUR, 0);
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        hmsTo012.put(MeasureUnit.MINUTE, 1);
1452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        hmsTo012.put(MeasureUnit.SECOND, 2);
1462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // For serialization: sub-class types.
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final int MEASURE_FORMAT = 0;
1502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final int TIME_UNIT_FORMAT = 1;
1512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final int CURRENCY_FORMAT = 2;
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Formatting width enum.
1552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Be sure to update MeasureUnitTest.TestSerialFormatWidthEnum
1572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // when adding an enum value.
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public enum FormatWidth {
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Spell out everything.
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
16396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        WIDE(ListFormatter.Style.DURATION, NumberFormat.PLURALCURRENCYSTYLE),
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Abbreviate when possible.
1672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
16896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        SHORT(ListFormatter.Style.DURATION_SHORT, NumberFormat.ISOCURRENCYSTYLE),
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Brief. Use only a symbol for the unit when possible.
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
17396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        NARROW(ListFormatter.Style.DURATION_NARROW, NumberFormat.CURRENCYSTYLE),
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Identical to NARROW except when formatMeasures is called with
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * an hour and minute; minute and second; or hour, minute, and second Measures.
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * In these cases formatMeasures formats as 5:37:23 instead of 5h, 37m, 23s.
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
18096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        NUMERIC(ListFormatter.Style.DURATION_NARROW, NumberFormat.CURRENCYSTYLE);
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Be sure to update the toFormatWidth and fromFormatWidth() functions
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // when adding an enum value.
18496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        private static final int INDEX_COUNT = 3;  // NARROW.ordinal() + 1
1852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final ListFormatter.Style listFormatterStyle;
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final int currencyStyle;
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
18996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        private FormatWidth(ListFormatter.Style style, int currencyStyle) {
1902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.listFormatterStyle = style;
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.currencyStyle = currencyStyle;
1922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ListFormatter.Style getListFormatterStyle() {
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return listFormatterStyle;
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int getCurrencyStyle() {
1992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return currencyStyle;
2002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Create a format from the locale, formatWidth, and format.
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale the locale.
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param formatWidth hints how long formatted strings should be.
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return The new MeasureFormat object.
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getInstance(ULocale locale, FormatWidth formatWidth) {
2112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getInstance(locale, formatWidth, NumberFormat.getInstance(locale));
2122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2155a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * Create a format from the {@link java.util.Locale} and formatWidth.
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
2175a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * @param locale the {@link java.util.Locale}.
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param formatWidth hints how long formatted strings should be.
2192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return The new MeasureFormat object.
2202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getInstance(Locale locale, FormatWidth formatWidth) {
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getInstance(ULocale.forLocale(locale), formatWidth);
2232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Create a format from the locale, formatWidth, and format.
2272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
2282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale the locale.
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param formatWidth hints how long formatted strings should be.
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param format This is defensively copied.
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return The new MeasureFormat object.
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getInstance(ULocale locale, FormatWidth formatWidth, NumberFormat format) {
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        PluralRules rules = PluralRules.forLocale(locale);
2352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        NumericFormatters formatters = null;
2362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        MeasureFormatData data = localeMeasureFormatData.get(locale);
2372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (data == null) {
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            data = loadLocaleData(locale);
2392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            localeMeasureFormatData.put(locale, data);
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (formatWidth == FormatWidth.NUMERIC) {
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            formatters = localeToNumericDurationFormatters.get(locale);
2432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (formatters == null) {
2442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                formatters = loadNumericFormatters(locale);
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                localeToNumericDurationFormatters.put(locale, formatters);
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        NumberFormat intFormat = NumberFormat.getInstance(locale);
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        intFormat.setMaximumFractionDigits(0);
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        intFormat.setMinimumFractionDigits(0);
2512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        intFormat.setRoundingMode(BigDecimal.ROUND_DOWN);
2522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new MeasureFormat(
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                locale,
25496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                data,
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                formatWidth,
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                new ImmutableNumberFormat(format),
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                rules,
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                formatters,
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                new ImmutableNumberFormat(NumberFormat.getInstance(locale, formatWidth.getCurrencyStyle())),
26096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                new ImmutableNumberFormat(intFormat));
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2645a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * Create a format from the {@link java.util.Locale}, formatWidth, and format.
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
2665a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * @param locale the {@link java.util.Locale}.
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param formatWidth hints how long formatted strings should be.
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param format This is defensively copied.
2692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return The new MeasureFormat object.
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getInstance(Locale locale, FormatWidth formatWidth, NumberFormat format) {
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getInstance(ULocale.forLocale(locale), formatWidth, format);
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Able to format Collection&lt;? extends Measure&gt;, Measure[], and Measure
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * by delegating to formatMeasures.
2782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * If the pos argument identifies a NumberFormat field,
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * then its indices are set to the beginning and end of the first such field
2802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * encountered. MeasureFormat itself does not supply any fields.
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Calling a
2832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <code>formatMeasures</code> method is preferred over calling
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * this method as they give better performance.
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
286bfab1e7fec36dff93fb980c546ad64a565faf9fcPaul Duffin     * @param obj must be a Collection&lt;? extends Measure&gt;, Measure[], or Measure object.
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param toAppendTo Formatted string appended here.
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param pos Identifies a field in the formatted text.
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int prevLength = toAppendTo.length();
2942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FieldPosition fpos =
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                new FieldPosition(pos.getFieldAttribute(), pos.getField());
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (obj instanceof Collection) {
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Collection<?> coll = (Collection<?>) obj;
2982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Measure[] measures = new Measure[coll.size()];
2992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int idx = 0;
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (Object o : coll) {
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (!(o instanceof Measure)) {
3022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    throw new IllegalArgumentException(obj.toString());
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                measures[idx++] = (Measure) o;
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            toAppendTo.append(formatMeasures(new StringBuilder(), fpos, measures));
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if (obj instanceof Measure[]) {
3082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            toAppendTo.append(formatMeasures(new StringBuilder(), fpos, (Measure[]) obj));
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if (obj instanceof Measure){
3102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            toAppendTo.append(formatMeasure((Measure) obj, numberFormat, new StringBuilder(), fpos));
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException(obj.toString());
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            pos.setBeginIndex(fpos.getBeginIndex() + prevLength);
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            pos.setEndIndex(fpos.getEndIndex() + prevLength);
3172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return toAppendTo;
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Parses text from a string to produce a <code>Measure</code>.
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @throws UnsupportedOperationException Not supported.
325836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
3282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Measure parseObject(String source, ParsePosition pos) {
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        throw new UnsupportedOperationException();
3302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Format a sequence of measures. Uses the ListFormatter unit lists.
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * So, for example, one could format “3 feet, 2 inches”.
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Zero values are formatted (eg, “3 feet, 0 inches”). It is the caller’s
3362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * responsibility to have the appropriate values in appropriate order,
3372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and using the appropriate Number values. Typically the units should be
3382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * in descending order, with all but the last Measure having integer values
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * (eg, not “3.2 feet, 2 inches”).
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
3412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param measures a sequence of one or more measures.
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the formatted string.
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final String formatMeasures(Measure... measures) {
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return formatMeasures(
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                new StringBuilder(),
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DontCareFieldPosition.INSTANCE,
3482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                measures).toString();
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Format a range of measures, such as "3.4-5.1 meters". It is the caller’s
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * responsibility to have the appropriate values in appropriate order,
3542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and using the appropriate Number values.
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <br>Note: If the format doesn’t have enough decimals, or lowValue ≥ highValue,
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the result will be a degenerate range, like “5-5 meters”.
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <br>Currency Units are not yet supported.
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param lowValue low value in range
3602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param highValue high value in range
3612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the formatted string.
3622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
36393cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
364836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final String formatMeasureRange(Measure lowValue, Measure highValue) {
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        MeasureUnit unit = lowValue.getUnit();
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!unit.equals(highValue.getUnit())) {
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("Units must match: " + unit + " ≠ " + highValue.getUnit());
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Number lowNumber = lowValue.getNumber();
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Number highNumber = highValue.getNumber();
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final boolean isCurrency = unit instanceof Currency;
3752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        UFieldPosition lowFpos = new UFieldPosition();
3772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        UFieldPosition highFpos = new UFieldPosition();
3782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringBuffer lowFormatted = null;
3792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        StringBuffer highFormatted = null;
3802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (isCurrency) {
3822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Currency currency = (Currency) unit;
3832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int fracDigits = currency.getDefaultFractionDigits();
3842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int maxFrac = numberFormat.nf.getMaximumFractionDigits();
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int minFrac = numberFormat.nf.getMinimumFractionDigits();
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (fracDigits != maxFrac || fracDigits != minFrac) {
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DecimalFormat currentNumberFormat = (DecimalFormat) numberFormat.get();
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                currentNumberFormat.setMaximumFractionDigits(fracDigits);
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                currentNumberFormat.setMinimumFractionDigits(fracDigits);
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                lowFormatted = currentNumberFormat.format(lowNumber, new StringBuffer(), lowFpos);
3912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                highFormatted = currentNumberFormat.format(highNumber, new StringBuffer(), highFpos);
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (lowFormatted == null) {
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            lowFormatted = numberFormat.format(lowNumber, new StringBuffer(), lowFpos);
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            highFormatted = numberFormat.format(highNumber, new StringBuffer(), highFpos);
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final double lowDouble = lowNumber.doubleValue();
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String keywordLow = rules.select(new PluralRules.FixedDecimal(lowDouble,
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                lowFpos.getCountVisibleFractionDigits(), lowFpos.getFractionDigits()));
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final double highDouble = highNumber.doubleValue();
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String keywordHigh = rules.select(new PluralRules.FixedDecimal(highDouble,
4052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                highFpos.getCountVisibleFractionDigits(), highFpos.getFractionDigits()));
4062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final PluralRanges pluralRanges = Factory.getDefaultFactory().getPluralRanges(getLocale());
40898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        StandardPlural resolvedPlural = pluralRanges.get(
40998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                StandardPlural.fromString(keywordLow),
41098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                StandardPlural.fromString(keywordHigh));
4112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
41298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String rangeFormatter = getRangeFormat(getLocale(), formatWidth);
41398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String formattedNumber = SimplePatternFormatter.formatCompiledPattern(rangeFormatter, lowFormatted, highFormatted);
4142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (isCurrency) {
4162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Nasty hack
4172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            currencyFormat.format(1d); // have to call this for the side effect
4182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Currency currencyUnit = (Currency) unit;
4202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder result = new StringBuilder();
42198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            appendReplacingCurrency(currencyFormat.getPrefix(lowDouble >= 0), currencyUnit, resolvedPlural, result);
4222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.append(formattedNumber);
42398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            appendReplacingCurrency(currencyFormat.getSuffix(highDouble >= 0), currencyUnit, resolvedPlural, result);
4242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return result.toString();
4252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            StringBuffer buffer = new StringBuffer();
4262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            CurrencyAmount currencyLow = (CurrencyAmount) lowValue;
4272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            CurrencyAmount currencyHigh = (CurrencyAmount) highValue;
4282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            FieldPosition pos = new FieldPosition(NumberFormat.INTEGER_FIELD);
4292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            currencyFormat.format(currencyLow, buffer, pos);
4302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            int startOfInteger = pos.getBeginIndex();
4312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            StringBuffer buffer2 = new StringBuffer();
4322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            FieldPosition pos2 = new FieldPosition(0);
4332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            //            currencyFormat.format(currencyHigh, buffer2, pos2);
4342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
43598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            String formatter =
43698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    getPluralFormatter(lowValue.getUnit(), formatWidth, resolvedPlural.ordinal());
43798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            return SimplePatternFormatter.formatCompiledPattern(formatter, formattedNumber);
4382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
44198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private void appendReplacingCurrency(String affix, Currency unit, StandardPlural resolvedPlural, StringBuilder result) {
4422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String replacement = "¤";
4432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int pos = affix.indexOf(replacement);
4442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (pos < 0) {
4452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            replacement = "XXX";
4462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            pos = affix.indexOf(replacement);
4472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (pos < 0) {
4492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.append(affix);
4502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
4512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // for now, just assume single
4522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.append(affix.substring(0,pos));
4532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // we have a mismatch between the number style and the currency style, so remap
4542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int currentStyle = formatWidth.getCurrencyStyle();
4552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (currentStyle == NumberFormat.ISOCURRENCYSTYLE) {
4562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result.append(unit.getCurrencyCode());
4572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
4582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result.append(unit.getName(currencyFormat.nf.getLocale(ULocale.ACTUAL_LOCALE),
4592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        currentStyle == NumberFormat.CURRENCYSTYLE ? Currency.SYMBOL_NAME :  Currency.PLURAL_LONG_NAME,
46098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                                resolvedPlural.getKeyword(), null));
4612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.append(affix.substring(pos+replacement.length()));
4632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
4672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Formats a single measure per unit.
4682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
4692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * An example of such a formatted string is "3.5 meters per second."
4702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
4712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param measure  the measure object. In above example, 3.5 meters.
4722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param perUnit  the per unit. In above example, it is MeasureUnit.SECOND
4732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param appendTo formatted string appended here.
4742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param pos      The field position.
4752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return appendTo.
476836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
4772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
4782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public StringBuilder formatMeasurePerUnit(
4792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Measure measure,
4802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            MeasureUnit perUnit,
4812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder appendTo,
4822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            FieldPosition pos) {
4832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        MeasureUnit resolvedUnit = MeasureUnit.resolveUnitPerUnit(
4842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                measure.getUnit(), perUnit);
4852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (resolvedUnit != null) {
4862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Measure newMeasure = new Measure(measure.getNumber(), resolvedUnit);
4872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatMeasure(newMeasure, numberFormat, appendTo, pos);
4882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FieldPosition fpos = new FieldPosition(
4902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                pos.getFieldAttribute(), pos.getField());
4912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int offset = withPerUnitAndAppend(
4922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                formatMeasure(measure, numberFormat, new StringBuilder(), fpos),
4932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                perUnit,
4942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                appendTo);
4952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
4962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            pos.setBeginIndex(fpos.getBeginIndex() + offset);
4972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            pos.setEndIndex(fpos.getEndIndex() + offset);
4982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return appendTo;
5002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Formats a sequence of measures.
5042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
5052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * If the fieldPosition argument identifies a NumberFormat field,
5062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * then its indices are set to the beginning and end of the first such field
5072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * encountered. MeasureFormat itself does not supply any fields.
5082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
5092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param appendTo the formatted string appended here.
5102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param fieldPosition Identifies a field in the formatted text.
5112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param measures the measures to format.
5122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return appendTo.
5132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @see MeasureFormat#formatMeasures(Measure...)
5142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public StringBuilder formatMeasures(
5162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder appendTo, FieldPosition fieldPosition, Measure... measures) {
5172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // fast track for trivial cases
5182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (measures.length == 0) {
5192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return appendTo;
5202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (measures.length == 1) {
5222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatMeasure(measures[0], numberFormat, appendTo, fieldPosition);
5232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (formatWidth == FormatWidth.NUMERIC) {
5262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // If we have just hour, minute, or second follow the numeric
5272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // track.
5282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Number[] hms = toHMS(measures);
5292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (hms != null) {
5302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return formatNumeric(hms, appendTo);
5312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ListFormatter listFormatter = ListFormatter.getInstance(
5352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                getLocale(), formatWidth.getListFormatterStyle());
5362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (fieldPosition != DontCareFieldPosition.INSTANCE) {
5372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatMeasuresSlowTrack(listFormatter, appendTo, fieldPosition, measures);
5382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Fast track: No field position.
5402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String[] results = new String[measures.length];
5412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (int i = 0; i < measures.length; i++) {
5422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            results[i] = formatMeasure(
5432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    measures[i],
5442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    i == measures.length - 1 ? numberFormat : integerFormat);
5452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return appendTo.append(listFormatter.format((Object[]) results));
5472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Two MeasureFormats, a and b, are equal if and only if they have the same formatWidth,
5522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * locale, and equal number formats.
5532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
5552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final boolean equals(Object other) {
5562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (this == other) {
5572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!(other instanceof MeasureFormat)) {
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        MeasureFormat rhs = (MeasureFormat) other;
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // A very slow but safe implementation.
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getWidth() == rhs.getWidth()
5652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                && getLocale().equals(rhs.getLocale())
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                && getNumberFormat().equals(rhs.getNumberFormat());
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Override
5732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final int hashCode() {
5742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // A very slow but safe implementation.
5752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return (getLocale().hashCode() * 31
5762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                + getNumberFormat().hashCode()) * 31 + getWidth().hashCode();
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the format width this instance is using.
5812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public MeasureFormat.FormatWidth getWidth() {
5832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return formatWidth;
5842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the locale of this instance.
5882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final ULocale getLocale() {
5902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getLocale(ULocale.VALID_LOCALE);
5912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get a copy of the number format.
5952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public NumberFormat getNumberFormat() {
5972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return numberFormat.get();
5982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a formatter for CurrencyAmount objects in the given
6022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * locale.
6032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale desired locale
6042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return a formatter object
6052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getCurrencyFormat(ULocale locale) {
6072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new CurrencyFormat(locale);
6082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a formatter for CurrencyAmount objects in the given
6125a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * {@link java.util.Locale}.
6135a559d08b74c555d7f997b51acd311b7a8756d26Fredrik Roubert     * @param locale desired {@link java.util.Locale}
6142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return a formatter object
6152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getCurrencyFormat(Locale locale) {
6172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getCurrencyFormat(ULocale.forLocale(locale));
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a formatter for CurrencyAmount objects in the default
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <code>FORMAT</code> locale.
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return a formatter object
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @see Category#FORMAT
6252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static MeasureFormat getCurrencyFormat() {
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // This method changes the NumberFormat object as well to match the new locale.
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    MeasureFormat withLocale(ULocale locale) {
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return MeasureFormat.getInstance(locale, getWidth());
6332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    MeasureFormat withNumberFormat(NumberFormat format) {
6362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new MeasureFormat(
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                getLocale(),
63896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                this.cache,
6392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                this.formatWidth,
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                new ImmutableNumberFormat(format),
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                this.rules,
6422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                this.numericFormatters,
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                this.currencyFormat,
64496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                this.integerFormat);
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private MeasureFormat(
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ULocale locale,
64996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            MeasureFormatData data,
6502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            FormatWidth formatWidth,
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ImmutableNumberFormat format,
6522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            PluralRules rules,
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            NumericFormatters formatters,
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ImmutableNumberFormat currencyFormat,
65596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            ImmutableNumberFormat integerFormat) {
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        setLocale(locale, locale);
65796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        this.cache = data;
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.formatWidth = formatWidth;
6592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.numberFormat = format;
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.rules = rules;
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.numericFormatters = formatters;
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.currencyFormat = currencyFormat;
6632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.integerFormat = integerFormat;
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    MeasureFormat() {
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Make compiler happy by setting final fields to null.
66896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        this.cache = null;
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.formatWidth = null;
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.numberFormat = null;
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.rules = null;
6722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.numericFormatters = null;
6732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.currencyFormat = null;
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.integerFormat = null;
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static class NumericFormatters {
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private DateFormat hourMinute;
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private DateFormat minuteSecond;
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private DateFormat hourMinuteSecond;
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public NumericFormatters(
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DateFormat hourMinute,
6842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DateFormat minuteSecond,
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DateFormat hourMinuteSecond) {
6862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.hourMinute = hourMinute;
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.minuteSecond = minuteSecond;
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.hourMinuteSecond = hourMinuteSecond;
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public DateFormat getHourMinute() { return hourMinute; }
6922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public DateFormat getMinuteSecond() { return minuteSecond; }
6932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public DateFormat getHourMinuteSecond() { return hourMinuteSecond; }
6942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static NumericFormatters loadNumericFormatters(
6972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ULocale locale) {
6982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
6992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
7002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new NumericFormatters(
7012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                loadNumericDurationFormat(r, "hm"),
7022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                loadNumericDurationFormat(r, "ms"),
7032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                loadNumericDurationFormat(r, "hms"));
7042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
70796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Sink for enumerating all of the measurement unit display names.
70896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Contains inner sink classes, each one corresponding to a type of resource table.
70996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
71096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     *
71196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
71296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Only store a value if it is still missing, that is, it has not been overridden.
71396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     *
71496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * C++: Each inner sink class has a reference to the main outer sink.
71596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Java: Use non-static inner classes instead.
7162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
71796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    private static final class UnitDataSink extends UResource.TableSink {
71896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        /**
71996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * Sink for a table of display patterns. For example,
72096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * unitsShort/duration/hour contains other{"{0} hrs"}.
72196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         */
72296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        class UnitPatternSink extends UResource.TableSink {
72398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            String[] patterns;
72498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert
72598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            void setFormatterIfAbsent(int index, UResource.Value value, int minPlaceholders) {
72698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                if (patterns == null) {
72798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    EnumMap<FormatWidth, String[]> styleToPatterns =
72898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                            cacheData.unitToStyleToPatterns.get(unit);
72998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    if (styleToPatterns == null) {
73098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                        styleToPatterns =
73198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                                new EnumMap<FormatWidth, String[]>(FormatWidth.class);
73298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                        cacheData.unitToStyleToPatterns.put(unit, styleToPatterns);
73398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    } else {
73498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                        patterns = styleToPatterns.get(width);
73598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    }
73698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    if (patterns == null) {
73798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                        patterns = new String[MeasureFormatData.PATTERN_COUNT];
73898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                        styleToPatterns.put(width, patterns);
73998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    }
74098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                }
74198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                if (patterns[index] == null) {
74298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    patterns[index] = SimplePatternFormatter.compileToStringMinMaxPlaceholders(
74398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                            value.getString(), sb, minPlaceholders, 1);
74498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                }
74598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            }
74696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
74796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            @Override
74896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            public void put(UResource.Key key, UResource.Value value) {
74996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                if (key.contentEquals("dnam")) {
75096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // Skip the unit display name for now.
75196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (key.contentEquals("per")) {
75296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // For example, "{0}/h".
75398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    // TODO: Set minPlaceholders=1
75498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    // after http://unicode.org/cldr/trac/ticket/9129 is fixed.
75598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    setFormatterIfAbsent(MeasureFormatData.PER_UNIT_INDEX, value, 0);
75696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else {
75796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // The key must be one of the plural form strings. For example:
75896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // one{"{0} hr"}
75996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // other{"{0} hrs"}
76098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    setFormatterIfAbsent(StandardPlural.indexFromString(key), value, 0);
76196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                }
7622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
76496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitPatternSink patternSink = new UnitPatternSink();
76596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
76696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        /**
76796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * Sink for a table of per-unit tables. For example,
76896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * unitsShort/duration contains tables for duration-unit subtypes day & hour.
76996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         */
77096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        class UnitSubtypeSink extends UResource.TableSink {
77196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            @Override
77296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            public UResource.TableSink getOrCreateTableSink(UResource.Key key, int initialSize) {
77396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                // Should we ignore or reject unknown units?
77496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                unit = MeasureUnit.internalGetInstance(type, key.toString());  // never null
77598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                // Trigger a fresh lookup of the patterns for this unit+width.
77698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                patternSink.patterns = null;
77796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                return patternSink;
7782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
77996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
78096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitSubtypeSink subtypeSink = new UnitSubtypeSink();
78196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
78296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        /**
78396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * Sink for compound x-per-y display pattern. For example,
78496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * unitsShort/compound/per may be "{0}/{1}".
78596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         */
78696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        class UnitCompoundSink extends UResource.TableSink {
78796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            @Override
78896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            public void put(UResource.Key key, UResource.Value value) {
78996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                if (key.contentEquals("per")) {
79096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    cacheData.styleToPerPattern.put(width,
79198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                            SimplePatternFormatter.compileToStringMinMaxPlaceholders(
79298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                                    value.getString(), sb, 2, 2));
79396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                }
7942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
79596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
79696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitCompoundSink compoundSink = new UnitCompoundSink();
79796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
79896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        /**
79996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * Sink for a table of unit type tables. For example,
80096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * unitsShort contains tables for area & duration.
80196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * It also contains a table for the compound/per pattern.
80296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         */
80396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        class UnitTypeSink extends UResource.TableSink {
80496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            @Override
80596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            public UResource.TableSink getOrCreateTableSink(UResource.Key key, int initialSize) {
80696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                if (key.contentEquals("currency")) {
80796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    // Skip.
80896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (key.contentEquals("compound")) {
80996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    if (!cacheData.hasPerFormatter(width)) {
81096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                        return compoundSink;
8112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
81296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else {
81396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    type = key.toString();
81496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return subtypeSink;
81596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                }
81696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                return null;
81796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
81896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
81996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitTypeSink typeSink = new UnitTypeSink();
82096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
82196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitDataSink(MeasureFormatData outputData) {
82296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            cacheData = outputData;
82396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
82496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        @Override
82596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        public void put(UResource.Key key, UResource.Value value) {
82696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            // Handle aliases like
82796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            // units:alias{"/LOCALE/unitsShort"}
82896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            // which should only occur in the root bundle.
82996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (value.getType() != ICUResourceBundle.ALIAS) { return; }
83096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            FormatWidth sourceWidth = widthFromKey(key);
83196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (sourceWidth == null) {
83296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                // Alias from something we don't care about.
83396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                return;
83496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
83596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            FormatWidth targetWidth = widthFromAlias(value);
83696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (targetWidth == null) {
83796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                // We do not recognize what to fall back to.
83896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                throw new ICUException("Units data fallback from " + key +
83996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                        " to unknown " + value.getAliasString());
84096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
84196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            // Check that we do not fall back to another fallback.
84296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (cacheData.widthFallback[targetWidth.ordinal()] != null) {
84396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                throw new ICUException("Units data fallback from " + key +
84496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                        " to " + value.getAliasString() + " which falls back to something else");
84596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
84696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            cacheData.widthFallback[sourceWidth.ordinal()] = targetWidth;
84796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
84896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        @Override
84996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        public UResource.TableSink getOrCreateTableSink(UResource.Key key, int initialSize) {
85096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if ((width = widthFromKey(key)) != null) {
85196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                return typeSink;
85296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
85396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return null;
85496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
85596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
85696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        static FormatWidth widthFromKey(UResource.Key key) {
85796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (key.startsWith("units")) {
85896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                if (key.length() == 5) {
85996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.WIDE;
86096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (key.regionMatches(5, "Short")) {
86196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.SHORT;
86296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (key.regionMatches(5, "Narrow")) {
86396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.NARROW;
8642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
8652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
86696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return null;
8672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
86896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
86996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        static FormatWidth widthFromAlias(UResource.Value value) {
87096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            String s = value.getAliasString();
87196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            // For example: "/LOCALE/unitsShort"
87296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (s.startsWith("/LOCALE/units")) {
87396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                if (s.length() == 13) {
87496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.WIDE;
87596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (s.length() == 18 && s.endsWith("Short")) {
87696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.SHORT;
87796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                } else if (s.length() == 19 && s.endsWith("Narrow")) {
87896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                    return FormatWidth.NARROW;
87996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                }
88096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
88196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return null;
88296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
88396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
88496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        // Output data.
88596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        MeasureFormatData cacheData;
88696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
88796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        // Path to current data.
88896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        FormatWidth width;
88996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        String type;
89096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        MeasureUnit unit;
89198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert
89298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        // Temporary
89398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        StringBuilder sb = new StringBuilder();
8942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
89596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
89696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    /**
89796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Returns formatting data for all MeasureUnits except for currency ones.
89896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     */
89996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    private static MeasureFormatData loadLocaleData(ULocale locale) {
90096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        ICUResourceBundle resource =
90196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
90296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        MeasureFormatData cacheData = new MeasureFormatData();
90396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        UnitDataSink sink = new UnitDataSink(cacheData);
90496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        resource.getAllTableItemsWithFallback("", sink);
90596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        return cacheData;
90696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    }
90796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
90896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    private static final FormatWidth getRegularWidth(FormatWidth width) {
90996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        if (width == FormatWidth.NUMERIC) {
91096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return FormatWidth.NARROW;
9112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
91296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        return width;
91396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    }
91496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
91598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private String getFormatterOrNull(MeasureUnit unit, FormatWidth width, int index) {
91696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        width = getRegularWidth(width);
91798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        Map<FormatWidth, String[]> styleToPatterns = cache.unitToStyleToPatterns.get(unit);
91898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String[] patterns = styleToPatterns.get(width);
91998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        if (patterns != null && patterns[index] != null) {
92098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            return patterns[index];
9212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
92296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        FormatWidth fallbackWidth = cache.widthFallback[width.ordinal()];
92396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        if (fallbackWidth != null) {
92498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            patterns = styleToPatterns.get(fallbackWidth);
92598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            if (patterns != null && patterns[index] != null) {
92698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                return patterns[index];
9272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
9282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
92998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        return null;
9302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
93196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
93298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private String getFormatter(MeasureUnit unit, FormatWidth width, int index) {
93398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String pattern = getFormatterOrNull(unit, width, index);
93498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        if (pattern == null) {
93598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            throw new MissingResourceException(
93698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    "no formatting pattern for " + unit + ", width " + width + ", index " + index,
93798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                    null, null);
93896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
93998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        return pattern;
94098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    }
94198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert
94298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private String getPluralFormatter(MeasureUnit unit, FormatWidth width, int index) {
94398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        if (index != StandardPlural.OTHER_INDEX) {
94498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            String pattern = getFormatterOrNull(unit, width, index);
94598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            if (pattern != null) {
94698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                return pattern;
94796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
94896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
94998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        return getFormatter(unit, width, StandardPlural.OTHER_INDEX);
95096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    }
95196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
95298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private String getPerFormatter(FormatWidth width) {
95396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        width = getRegularWidth(width);
95498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String perPattern = cache.styleToPerPattern.get(width);
95596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        if (perPattern != null) {
95696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return perPattern;
95796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
95896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        FormatWidth fallbackWidth = cache.widthFallback[width.ordinal()];
95996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        if (fallbackWidth != null) {
96096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            perPattern = cache.styleToPerPattern.get(fallbackWidth);
96196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            if (perPattern != null) {
96296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert                return perPattern;
96396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            }
96496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
96596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        throw new MissingResourceException("no x-per-y pattern for width " + width, null, null);
96696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    }
96796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
9682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private int withPerUnitAndAppend(
9692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            CharSequence formatted, MeasureUnit perUnit, StringBuilder appendTo) {
9702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] offsets = new int[1];
97198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String perUnitPattern =
97298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                getFormatterOrNull(perUnit, formatWidth, MeasureFormatData.PER_UNIT_INDEX);
9732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (perUnitPattern != null) {
97498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            SimplePatternFormatter.formatAndAppend(perUnitPattern, appendTo, offsets, formatted);
9752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return offsets[0];
9762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
97798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String perPattern = getPerFormatter(formatWidth);
97898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String pattern = getPluralFormatter(perUnit, formatWidth, StandardPlural.ONE.ordinal());
97998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String perUnitString = SimplePatternFormatter.getTextWithNoPlaceholders(pattern).trim();
98098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        SimplePatternFormatter.formatAndAppend(
98198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                perPattern, appendTo, offsets, formatted, perUnitString);
9822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return offsets[0];
9832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String formatMeasure(Measure measure, ImmutableNumberFormat nf) {
9862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return formatMeasure(
9872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                measure, nf, new StringBuilder(),
9882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                DontCareFieldPosition.INSTANCE).toString();
9892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private StringBuilder formatMeasure(
9922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Measure measure,
9932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ImmutableNumberFormat nf,
9942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder appendTo,
9952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            FieldPosition fieldPosition) {
99698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        Number n = measure.getNumber();
99798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        MeasureUnit unit = measure.getUnit();
99898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        if (unit instanceof Currency) {
9992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return appendTo.append(
10002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    currencyFormat.format(
100198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                            new CurrencyAmount(n, (Currency) unit),
10022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            new StringBuffer(),
10032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            fieldPosition));
10042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
100698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        StringBuffer formattedNumber = new StringBuffer();
100798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        StandardPlural pluralForm = QuantityFormatter.selectPlural(
100898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                n, nf.nf, rules, formattedNumber, fieldPosition);
100998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String formatter = getPluralFormatter(unit, formatWidth, pluralForm.ordinal());
101098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        return QuantityFormatter.format(formatter, formattedNumber, appendTo, fieldPosition);
10112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
101298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert
101396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert    /**
101496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Instances contain all MeasureFormat specific data for a particular locale.
101596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * This data is cached. It is never copied, but is shared via shared pointers.
101696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     *
101796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * Note: We might change the cache data to have
101896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * an array[WIDTH_INDEX_COUNT] or EnumMap<FormatWidth, ...> of
101996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * complete sets of unit & per patterns,
102096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     * to correspond to the resource data and its aliases.
102196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert     */
10222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final class MeasureFormatData {
102398d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        static final int PER_UNIT_INDEX = StandardPlural.COUNT;
102498d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        static final int PATTERN_COUNT = PER_UNIT_INDEX + 1;
102598d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert
102696a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        boolean hasPerFormatter(FormatWidth width) {
102796a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert            return styleToPerPattern.containsKey(width);
102896a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        }
102996a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert
103096a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        /**
103196a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * Redirection data from root-bundle, top-level sideways aliases.
103296a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * - null: initial value, just fall back to root
103396a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         * - FormatWidth.WIDE/SHORT/NARROW: sideways alias for missing data
103496a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert         */
103596a989def40de002d53d58a6bc121e0c110bd6c0Fredrik Roubert        final FormatWidth widthFallback[] = new FormatWidth[FormatWidth.INDEX_COUNT];
103698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
103798d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        final Map<MeasureUnit, EnumMap<FormatWidth, String[]>> unitToStyleToPatterns =
103898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                new HashMap<MeasureUnit, EnumMap<FormatWidth, String[]>>();
103998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        final EnumMap<FormatWidth, String> styleToPerPattern =
104098d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert                new EnumMap<FormatWidth, String>(FormatWidth.class);;
10412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
10422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Wrapper around NumberFormat that provides immutability and thread-safety.
10442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final class ImmutableNumberFormat {
10452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private NumberFormat nf;
10462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public ImmutableNumberFormat(NumberFormat nf) {
10482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.nf = (NumberFormat) nf.clone();
10492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public synchronized NumberFormat get() {
10522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return (NumberFormat) nf.clone();
10532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public synchronized StringBuffer format(
10562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                Number n, StringBuffer buffer, FieldPosition pos) {
10572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return nf.format(n, buffer, pos);
10582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public synchronized StringBuffer format(
10612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                CurrencyAmount n, StringBuffer buffer, FieldPosition pos) {
10622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return nf.format(n, buffer, pos);
10632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        @SuppressWarnings("unused")
10662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public synchronized String format(Number number) {
10672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return nf.format(number);
10682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getPrefix(boolean positive) {
10712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return positive ? ((DecimalFormat)nf).getPositivePrefix() : ((DecimalFormat)nf).getNegativePrefix();
10722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getSuffix(boolean positive) {
10742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return positive ? ((DecimalFormat)nf).getPositiveSuffix() : ((DecimalFormat)nf).getPositiveSuffix();
10752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
10772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static final class PatternData {
10792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final String prefix;
10802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final String suffix;
10812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public PatternData(String pattern) {
10822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int pos = pattern.indexOf("{0}");
10832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (pos < 0) {
10842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                prefix = pattern;
10852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                suffix = null;
10862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
10872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                prefix = pattern.substring(0,pos);
10882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                suffix = pattern.substring(pos+3);
10892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
10902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String toString() {
10922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return prefix + "; " + suffix;
10932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
10962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    Object toTimeUnitProxy() {
10982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new MeasureProxy(getLocale(), formatWidth, numberFormat.get(), TIME_UNIT_FORMAT);
10992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    Object toCurrencyProxy() {
11022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new MeasureProxy(getLocale(), formatWidth, numberFormat.get(), CURRENCY_FORMAT);
11032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private StringBuilder formatMeasuresSlowTrack(
11062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ListFormatter listFormatter,
11072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder appendTo,
11082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            FieldPosition fieldPosition,
11092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Measure... measures) {
11102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String[] results = new String[measures.length];
11112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Zero out our field position so that we can tell when we find our field.
11132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FieldPosition fpos = new FieldPosition(
11142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                fieldPosition.getFieldAttribute(), fieldPosition.getField());
11152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int fieldPositionFoundIndex = -1;
11172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (int i = 0; i < measures.length; ++i) {
11182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ImmutableNumberFormat nf = (i == measures.length - 1 ? numberFormat : integerFormat);
11192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (fieldPositionFoundIndex == -1) {
11202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                results[i] = formatMeasure(measures[i], nf, new StringBuilder(), fpos).toString();
11212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
11222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    fieldPositionFoundIndex = i;
11232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
11242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
11252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                results[i] = formatMeasure(measures[i], nf);
11262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ListFormatter.FormattedListBuilder builder =
11292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                listFormatter.format(Arrays.asList(results), fieldPositionFoundIndex);
11302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Fix up FieldPosition indexes if our field is found.
11322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (builder.getOffset() != -1) {
11332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fieldPosition.setBeginIndex(fpos.getBeginIndex() + builder.getOffset() + appendTo.length());
11342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fieldPosition.setEndIndex(fpos.getEndIndex() + builder.getOffset() + appendTo.length());
11352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return appendTo.append(builder.toString());
11372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // type is one of "hm", "ms" or "hms"
11402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static DateFormat loadNumericDurationFormat(
11412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ICUResourceBundle r, String type) {
11422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        r = r.getWithFallback(String.format("durationUnits/%s", type));
11432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // We replace 'h' with 'H' because 'h' does not make sense in the context of durations.
11442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        DateFormat result = new SimpleDateFormat(r.getString().replace("h", "H"));
11452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        result.setTimeZone(TimeZone.GMT_ZONE);
11462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
11472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Returns hours in [0]; minutes in [1]; seconds in [2] out of measures array. If
11502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // unsuccessful, e.g measures has other measurements besides hours, minutes, seconds;
11512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // hours, minutes, seconds are out of order; or have negative values, returns null.
11522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // If hours, minutes, or seconds is missing from measures the corresponding element in
11532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // returned array will be null.
11542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static Number[] toHMS(Measure[] measures) {
11552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Number[] result = new Number[3];
11562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int lastIdx = -1;
11572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (Measure m : measures) {
11582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (m.getNumber().doubleValue() < 0.0) {
11592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return null;
11602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Integer idxObj = hmsTo012.get(m.getUnit());
11622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (idxObj == null) {
11632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return null;
11642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int idx = idxObj.intValue();
11662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (idx <= lastIdx) {
11672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // hour before minute before second
11682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return null;
11692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            lastIdx = idx;
11712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result[idx] = m.getNumber();
11722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
11742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Formats numeric time duration as 5:00:47 or 3:54. In the process, it replaces any null
11772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // values in hms with 0.
11782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private StringBuilder formatNumeric(Number[] hms, StringBuilder appendable) {
11792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // find the start and end of non-nil values in hms array. We have to know if we
11812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // have hour-minute; minute-second; or hour-minute-second.
11822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int startIndex = -1;
11832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int endIndex = -1;
11842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (int i = 0; i < hms.length; i++) {
11852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (hms[i] != null) {
11862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                endIndex = i;
11872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (startIndex == -1) {
11882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    startIndex = endIndex;
11892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
11902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
11912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Replace nil value with 0.
11922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                hms[i] = Integer.valueOf(0);
11932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // convert hours, minutes, seconds into milliseconds.
11962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        long millis = (long) (((Math.floor(hms[0].doubleValue()) * 60.0
11972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                + Math.floor(hms[1].doubleValue())) * 60.0
11982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                + Math.floor(hms[2].doubleValue())) * 1000.0);
11992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Date d = new Date(millis);
12002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // if hour-minute-second
12012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (startIndex == 0 && endIndex == 2) {
12022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatNumeric(
12032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    d,
12042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    numericFormatters.getHourMinuteSecond(),
12052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    DateFormat.Field.SECOND,
12062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    hms[endIndex],
12072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    appendable);
12082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
12092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // if minute-second
12102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (startIndex == 1 && endIndex == 2) {
12112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatNumeric(
12122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    d,
12132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    numericFormatters.getMinuteSecond(),
12142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    DateFormat.Field.SECOND,
12152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    hms[endIndex],
12162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    appendable);
12172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
12182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // if hour-minute
12192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (startIndex == 0 && endIndex == 1) {
12202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return formatNumeric(
12212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    d,
12222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    numericFormatters.getHourMinute(),
12232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    DateFormat.Field.MINUTE,
12242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    hms[endIndex],
12252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    appendable);
12262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
12272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        throw new IllegalStateException();
12282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
12292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Formats a duration as 5:00:37 or 23:59.
12312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // duration is a particular duration after epoch.
12322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // formatter is a hour-minute-second, hour-minute, or minute-second formatter.
12332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // smallestField denotes what the smallest field is in duration: either
12342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // hour, minute, or second.
12352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // smallestAmount is the value of that smallest field. for 5:00:37.3,
12362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // smallestAmount is 37.3. This smallest field is formatted with this object's
12372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // NumberFormat instead of formatter.
12382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // appendTo is where the formatted string is appended.
12392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private StringBuilder formatNumeric(
12402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Date duration,
12412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            DateFormat formatter,
12422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            DateFormat.Field smallestField,
12432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Number smallestAmount,
12442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder appendTo) {
12452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Format the smallest amount ahead of time.
12462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String smallestAmountFormatted;
12472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Format the smallest amount using this object's number format, but keep track
12492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // of the integer portion of this formatted amount. We have to replace just the
12502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // integer part with the corresponding value from formatting the date. Otherwise
12512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // when formatting 0 minutes 9 seconds, we may get "00:9" instead of "00:09"
12522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FieldPosition intFieldPosition = new FieldPosition(NumberFormat.INTEGER_FIELD);
12532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        smallestAmountFormatted = numberFormat.format(
12542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                smallestAmount, new StringBuffer(), intFieldPosition).toString();
12552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Give up if there is no integer field.
12562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (intFieldPosition.getBeginIndex() == 0 && intFieldPosition.getEndIndex() == 0) {
12572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalStateException();
12582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
12592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Format our duration as a date, but keep track of where the smallest field is
12602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // so that we can use it to replace the integer portion of the smallest value.
12612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FieldPosition smallestFieldPosition = new FieldPosition(smallestField);
12622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String draft = formatter.format(
12632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                duration, new StringBuffer(), smallestFieldPosition).toString();
12642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // If we find the smallest field
12662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (smallestFieldPosition.getBeginIndex() != 0
12672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                || smallestFieldPosition.getEndIndex() != 0) {
12682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // add everything up to the start of the smallest field in duration.
12692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(draft, 0, smallestFieldPosition.getBeginIndex());
12702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // add everything in the smallest field up to the integer portion
12722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(smallestAmountFormatted, 0, intFieldPosition.getBeginIndex());
12732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Add the smallest field in formatted duration in lieu of the integer portion
12752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // of smallest field
12762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(
12772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    draft,
12782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    smallestFieldPosition.getBeginIndex(),
12792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    smallestFieldPosition.getEndIndex());
12802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Add the rest of the smallest field
12822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(
12832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    smallestAmountFormatted,
12842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    intFieldPosition.getEndIndex(),
12852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    smallestAmountFormatted.length());
12862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(draft, smallestFieldPosition.getEndIndex(), draft.length());
12872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
12882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // As fallback, just use the formatted duration.
12892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            appendTo.append(draft);
12902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
12912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return appendTo;
12922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
12932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private Object writeReplace() throws ObjectStreamException {
12952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new MeasureProxy(
12962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                getLocale(), formatWidth, numberFormat.get(), MEASURE_FORMAT);
12972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
12982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
12992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static class MeasureProxy implements Externalizable {
13002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private static final long serialVersionUID = -6033308329886716770L;
13012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private ULocale locale;
13032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private FormatWidth formatWidth;
13042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private NumberFormat numberFormat;
13052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private int subClass;
13062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private HashMap<Object, Object> keyValues;
13072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public MeasureProxy(
13092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ULocale locale,
13102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                FormatWidth width,
13112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                NumberFormat numberFormat,
13122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int subClass) {
13132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.locale = locale;
13142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.formatWidth = width;
13152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.numberFormat = numberFormat;
13162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.subClass = subClass;
13172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.keyValues = new HashMap<Object, Object>();
13182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Must have public constructor, to enable Externalizable
13212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public MeasureProxy() {
13222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void writeExternal(ObjectOutput out) throws IOException {
13252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeByte(0); // version
13262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeUTF(locale.toLanguageTag());
13272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeByte(formatWidth.ordinal());
13282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeObject(numberFormat);
13292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeByte(subClass);
13302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            out.writeObject(keyValues);
13312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        @SuppressWarnings("unchecked")
13342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
13352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            in.readByte(); // version.
13362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            locale = ULocale.forLanguageTag(in.readUTF());
13372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            formatWidth = fromFormatWidthOrdinal(in.readByte() & 0xFF);
13382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            numberFormat = (NumberFormat) in.readObject();
13392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (numberFormat == null) {
13402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new InvalidObjectException("Missing number format.");
13412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
13422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            subClass = in.readByte() & 0xFF;
13432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // This cast is safe because the serialized form of hashtable can have
13452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // any object as the key and any object as the value.
13462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            keyValues = (HashMap<Object, Object>) in.readObject();
13472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (keyValues == null) {
13482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new InvalidObjectException("Missing optional values map.");
13492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
13502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private TimeUnitFormat createTimeUnitFormat() throws InvalidObjectException {
13532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int style;
13542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (formatWidth == FormatWidth.WIDE) {
13552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                style = TimeUnitFormat.FULL_NAME;
13562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else if (formatWidth == FormatWidth.SHORT) {
13572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                style = TimeUnitFormat.ABBREVIATED_NAME;
13582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
13592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new InvalidObjectException("Bad width: " + formatWidth);
13602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
13612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            TimeUnitFormat result = new TimeUnitFormat(locale, style);
13622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.setNumberFormat(numberFormat);
13632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return result;
13642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private Object readResolve() throws ObjectStreamException {
13672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            switch (subClass) {
13682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            case MEASURE_FORMAT:
13692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return MeasureFormat.getInstance(locale, formatWidth, numberFormat);
13702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            case TIME_UNIT_FORMAT:
13712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return createTimeUnitFormat();
13722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            case CURRENCY_FORMAT:
13732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return new CurrencyFormat(locale);
13742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            default:
13752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new InvalidObjectException("Unknown subclass: " + subClass);
13762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
13772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
13792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static FormatWidth fromFormatWidthOrdinal(int ordinal) {
13812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FormatWidth[] values = FormatWidth.values();
13822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (ordinal < 0 || ordinal >= values.length) {
13832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return FormatWidth.SHORT;
13842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
13852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return values[ordinal];
13862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
13872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
138898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    private static final Map<ULocale, String> localeIdToRangeFormat =
138998d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            new ConcurrentHashMap<ULocale, String>();
13902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
13912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
139298d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert     * Return a formatter (compiled SimplePatternFormatter pattern) for a range, such as "{0}–{1}".
13932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param forLocale locale to get the format for
13942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param width the format width
13952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return range formatter, such as "{0}–{1}"
13962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
139793cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
1398836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
13992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
14002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
140198d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert    public static String getRangeFormat(ULocale forLocale, FormatWidth width) {
14022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // TODO fix Hack for French
14032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (forLocale.getLanguage().equals("fr")) {
14042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return getRangeFormat(ULocale.ROOT, width);
14052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
140698d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert        String result = localeIdToRangeFormat.get(forLocale);
14072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (result == null) {
14082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.
14092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    getBundleInstance(ICUData.ICU_BASE_NAME, forLocale);
14102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ULocale realLocale = rb.getULocale();
14112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!forLocale.equals(realLocale)) { // if the child would inherit, then add a cache entry for it.
14122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = localeIdToRangeFormat.get(forLocale);
14132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (result != null) {
14142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    localeIdToRangeFormat.put(forLocale, result);
14152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    return result;
14162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
14172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
14182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // At this point, both the forLocale and the realLocale don't have an item
14192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // So we have to make one.
14202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            NumberingSystem ns = NumberingSystem.getInstance(forLocale);
14212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
14222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String resultString = null;
14232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            try {
14242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                resultString = rb.getStringWithFallback("NumberElements/" + ns.getName() + "/miscPatterns/range");
14252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } catch ( MissingResourceException ex ) {
14262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                resultString = rb.getStringWithFallback("NumberElements/latn/patterns/range");
14272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
142898d264ec1d3841aef62c0e0293c929c23c08c5c5Fredrik Roubert            result = SimplePatternFormatter.compileToStringMinMaxPlaceholders(resultString, new StringBuilder(), 2, 2);
14292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            localeIdToRangeFormat.put(forLocale, result);
14302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!forLocale.equals(realLocale)) {
14312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                localeIdToRangeFormat.put(realLocale, result);
14322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
14332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
14342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
14352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
14362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}
1437