ICU-13526 Refactoring MeasureFormat, CurrencyFormat, and TimeUnitFormat to more directly wrap NumberFormatter.

X-SVN-Rev: 40833
This commit is contained in:
Shane Carr 2018-02-03 03:40:09 +00:00
parent ccedf54223
commit bfab81c260
8 changed files with 386 additions and 950 deletions

View file

@ -64,6 +64,8 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
setToLong(number.longValue());
} else if (number instanceof Integer) {
setToInt(number.intValue());
} else if (number instanceof Float) {
setToDouble(number.doubleValue());
} else if (number instanceof Double) {
setToDouble(number.doubleValue());
} else if (number instanceof BigInteger) {

View file

@ -161,6 +161,12 @@ public class LongNameHandler implements MicroPropsGenerator {
this.parent = parent;
}
public static String getUnitDisplayName(ULocale locale, MeasureUnit unit, UnitWidth width) {
String[] measureData = new String[ARRAY_LENGTH];
getMeasureData(locale, unit, width, measureData);
return measureData[DNAM_INDEX];
}
public static LongNameHandler forCurrencyLongNames(
ULocale locale,
Currency currency,

View file

@ -17,7 +17,6 @@ import java.text.FieldPosition;
import java.text.ParsePosition;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.ULocale;
/**
@ -35,24 +34,8 @@ class CurrencyFormat extends MeasureFormat {
// Generated by serialver from JDK 1.4.1_01
static final long serialVersionUID = -931679363692504634L;
private NumberFormat fmt;
private transient final MeasureFormat mf;
public CurrencyFormat(ULocale locale) {
// Needed for getLocale(ULocale.VALID_LOCALE).
setLocale(locale, locale);
mf = MeasureFormat.getInstance(locale, FormatWidth.WIDE);
fmt = NumberFormat.getCurrencyInstance(locale.toLocale());
}
/**
* {@inheritDoc}
*/
@Override
public Object clone() {
CurrencyFormat result = (CurrencyFormat) super.clone();
result.fmt = (NumberFormat) fmt.clone();
return result;
super(locale, FormatWidth.DEFAULT_CURRENCY);
}
/**
@ -65,10 +48,7 @@ class CurrencyFormat extends MeasureFormat {
if (!(obj instanceof CurrencyAmount)) {
throw new IllegalArgumentException("Invalid type: " + obj.getClass().getName());
}
CurrencyAmount currency = (CurrencyAmount) obj;
fmt.setCurrency(currency.getCurrency());
return fmt.format(currency.getNumber(), toAppendTo, pos);
return super.format(obj, toAppendTo, pos);
}
/**
@ -78,49 +58,17 @@ class CurrencyFormat extends MeasureFormat {
*/
@Override
public CurrencyAmount parseObject(String source, ParsePosition pos) {
return fmt.parseCurrency(source, pos);
return getNumberFormat().parseCurrency(source, pos);
}
// boilerplate code to make CurrencyFormat otherwise follow the contract of
// MeasureFormat
/**
* {@inheritDoc}
*/
@Override
public StringBuilder formatMeasures(
StringBuilder appendTo,
FieldPosition fieldPosition,
Measure... measures) {
return mf.formatMeasures(appendTo, fieldPosition, measures);
}
/**
* {@inheritDoc}
*/
@Override
public MeasureFormat.FormatWidth getWidth() {
return mf.getWidth();
}
/**
* {@inheritDoc}
*/
@Override
public NumberFormat getNumberFormat() {
return mf.getNumberFormat();
}
// End boilerplate.
// Serialization
private Object writeReplace() throws ObjectStreamException {
return mf.toCurrencyProxy();
return toCurrencyProxy();
}
// Preserve backward serialize backward compatibility.
private Object readResolve() throws ObjectStreamException {
return new CurrencyFormat(fmt.getLocale(ULocale.ACTUAL_LOCALE));
return new CurrencyFormat(getLocale(ULocale.ACTUAL_LOCALE));
}
}

View file

@ -8,6 +8,7 @@
*/
package com.ibm.icu.text;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -19,6 +20,7 @@ import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.util.ICUUncheckedIOException;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
@ -36,7 +38,7 @@ final public class ListFormatter {
private final String middle;
private final String end;
private final ULocale locale;
/**
* Indicates the style of Listformatter
* @internal
@ -72,9 +74,9 @@ final public class ListFormatter {
*/
@Deprecated
DURATION_NARROW("unit-narrow");
private final String name;
Style(String name) {
this.name = name;
}
@ -86,7 +88,7 @@ final public class ListFormatter {
public String getName() {
return name;
}
}
/**
@ -153,7 +155,7 @@ final public class ListFormatter {
public static ListFormatter getInstance(Locale locale) {
return getInstance(ULocale.forLocale(locale), Style.STANDARD);
}
/**
* Create a list formatter that is appropriate for a locale and style.
*
@ -201,7 +203,7 @@ final public class ListFormatter {
public String format(Collection<?> items) {
return format(items, -1).toString();
}
// Formats a collection of objects and returns the formatted string plus the offset
// in the string where the index th element appears. index is zero based. If index is
// negative or greater than or equal to the size of items then this function returns -1 for
@ -224,7 +226,7 @@ final public class ListFormatter {
}
return builder.append(end, it.next(), index == count - 1);
}
/**
* Returns the pattern to use for a particular item count.
* @param count the item count.
@ -243,7 +245,7 @@ final public class ListFormatter {
}
return format(list);
}
/**
* Returns the locale of this object.
* @internal
@ -253,19 +255,19 @@ final public class ListFormatter {
public ULocale getLocale() {
return locale;
}
// Builds a formatted list
static class FormattedListBuilder {
private StringBuilder current;
private int offset;
// Start is the first object in the list; If recordOffset is true, records the offset of
// this first object.
public FormattedListBuilder(Object start, boolean recordOffset) {
this.current = new StringBuilder(start.toString());
this.offset = recordOffset ? 0 : -1;
}
// Appends additional object. pattern is a template indicating where the new object gets
// added in relation to the rest of the list. {0} represents the rest of the list; {1}
// represents the new object in pattern. next is the object to be added. If recordOffset
@ -288,16 +290,24 @@ final public class ListFormatter {
return this;
}
public void appendTo(Appendable appendable) {
try {
appendable.append(current);
} catch(IOException e) {
throw new ICUUncheckedIOException(e);
}
}
@Override
public String toString() {
return current.toString();
}
// Gets the last recorded offset or -1 if no offset recorded.
public int getOffset() {
return offset;
}
private boolean offsetRecorded() {
return offset >= 0;
}

View file

@ -9,7 +9,6 @@
package com.ibm.icu.text;
import java.io.ObjectStreamException;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.HashMap;
@ -23,42 +22,41 @@ import java.util.TreeMap;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.UResource;
import com.ibm.icu.util.Measure;
import com.ibm.icu.number.LocalizedNumberFormatter;
import com.ibm.icu.util.TimeUnit;
import com.ibm.icu.util.TimeUnitAmount;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
import com.ibm.icu.util.UResourceBundle;
/**
* Format or parse a TimeUnitAmount, using plural rules for the units where available.
*
* <P>
* Code Sample:
*
* <pre>
* // create a time unit instance.
* // only SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, and YEAR are supported
* TimeUnit timeUnit = TimeUnit.SECOND;
* // create time unit amount instance - a combination of Number and time unit
* TimeUnitAmount source = new TimeUnitAmount(2, timeUnit);
* // create time unit format instance
* TimeUnitFormat format = new TimeUnitFormat();
* // set the locale of time unit format
* format.setLocale(new ULocale("en"));
* // format a time unit amount
* String formatted = format.format(source);
* System.out.println(formatted);
* try {
* // parse a string into time unit amount
* TimeUnitAmount result = (TimeUnitAmount) format.parseObject(formatted);
* // result should equal to source
* } catch (ParseException e) {
* }
* // create a time unit instance.
* // only SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, and YEAR are supported
* TimeUnit timeUnit = TimeUnit.SECOND;
* // create time unit amount instance - a combination of Number and time unit
* TimeUnitAmount source = new TimeUnitAmount(2, timeUnit);
* // create time unit format instance
* TimeUnitFormat format = new TimeUnitFormat();
* // set the locale of time unit format
* format.setLocale(new ULocale("en"));
* // format a time unit amount
* String formatted = format.format(source);
* System.out.println(formatted);
* try {
* // parse a string into time unit amount
* TimeUnitAmount result = (TimeUnitAmount) format.parseObject(formatted);
* // result should equal to source
* } catch (ParseException e) {
* }
* </pre>
*
* <P>
*
* @see TimeUnitAmount
* @see MeasureFormat
* @author markdavis
@ -68,17 +66,15 @@ import com.ibm.icu.util.UResourceBundle;
public class TimeUnitFormat extends MeasureFormat {
/**
* Constant for full name style format. For example, the full name for "hour" in English is "hour" or
* "hours".
*
* Constant for full name style format.
* For example, the full name for "hour" in English is "hour" or "hours".
* @deprecated ICU 53 see {@link MeasureFormat.FormatWidth}
*/
@Deprecated
public static final int FULL_NAME = 0;
/**
* Constant for abbreviated name style format. For example, the abbreviated name for "hour" in
* English is "hr" or "hrs".
*
* Constant for abbreviated name style format.
* For example, the abbreviated name for "hour" in English is "hr" or "hrs".
* @deprecated ICU 53 see {@link MeasureFormat.FormatWidth}
*/
@Deprecated
@ -99,7 +95,7 @@ public class TimeUnitFormat extends MeasureFormat {
// is an empty shell. Every public method of the super class is overridden to
// delegate to this field. Each time this object mutates, it replaces this field with
// a new immutable instance.
private transient MeasureFormat mf;
// private transient MeasureFormat mf;
private transient Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
private transient PluralRules pluralRules;
@ -114,23 +110,18 @@ public class TimeUnitFormat extends MeasureFormat {
private static final String DEFAULT_PATTERN_FOR_YEAR = "{0} y";
/**
* Create empty format using full name style, for example, "hours". Use setLocale and/or setFormat to
* modify.
*
* Create empty format using full name style, for example, "hours".
* Use setLocale and/or setFormat to modify.
* @deprecated ICU 53 use {@link MeasureFormat} instead.
*/
@Deprecated
public TimeUnitFormat() {
mf = MeasureFormat.getInstance(ULocale.getDefault(), FormatWidth.WIDE);
isReady = false;
style = FULL_NAME;
this(ULocale.getDefault(), FULL_NAME);
}
/**
* Create TimeUnitFormat given a ULocale, and using full name style.
*
* @param locale
* locale of this time unit formatter.
* @param locale locale of this time unit formatter.
* @deprecated ICU 53 use {@link MeasureFormat} instead.
*/
@Deprecated
@ -140,9 +131,7 @@ public class TimeUnitFormat extends MeasureFormat {
/**
* Create TimeUnitFormat given a Locale, and using full name style.
*
* @param locale
* locale of this time unit formatter.
* @param locale locale of this time unit formatter.
* @deprecated ICU 53 use {@link MeasureFormat} instead.
*/
@Deprecated
@ -152,28 +141,20 @@ public class TimeUnitFormat extends MeasureFormat {
/**
* Create TimeUnitFormat given a ULocale and a formatting style.
*
* @param locale
* locale of this time unit formatter.
* @param style
* format style, either FULL_NAME or ABBREVIATED_NAME style.
* @throws IllegalArgumentException
* if the style is not FULL_NAME or ABBREVIATED_NAME style.
* @param locale locale of this time unit formatter.
* @param style format style, either FULL_NAME or ABBREVIATED_NAME style.
* @throws IllegalArgumentException if the style is not FULL_NAME or
* ABBREVIATED_NAME style.
* @deprecated ICU 53 use {@link MeasureFormat} instead.
*/
@Deprecated
public TimeUnitFormat(ULocale locale, int style) {
super(locale, style == FULL_NAME ? FormatWidth.WIDE : FormatWidth.SHORT);
format = super.getNumberFormat();
if (style < FULL_NAME || style >= TOTAL_STYLES) {
throw new IllegalArgumentException(
"style should be either FULL_NAME or ABBREVIATED_NAME style");
throw new IllegalArgumentException("style should be either FULL_NAME or ABBREVIATED_NAME style");
}
mf = MeasureFormat.getInstance(locale,
style == FULL_NAME ? FormatWidth.WIDE : FormatWidth.SHORT);
this.style = style;
// Needed for getLocale(ULocale.VALID_LOCALE)
setLocale(locale, locale);
this.locale = locale;
isReady = false;
}
@ -186,40 +167,29 @@ public class TimeUnitFormat extends MeasureFormat {
/**
* Create TimeUnitFormat given a Locale and a formatting style.
*
* @deprecated ICU 53 use {@link MeasureFormat} instead.
*/
@Deprecated
public TimeUnitFormat(Locale locale, int style) {
this(ULocale.forLocale(locale), style);
this(ULocale.forLocale(locale), style);
}
/**
* Set the locale used for formatting or parsing.
*
* @param locale
* locale of this time unit formatter.
* @param locale locale of this time unit formatter.
* @return this, for chaining.
* @deprecated ICU 53 see {@link MeasureFormat}.
*/
@Deprecated
public TimeUnitFormat setLocale(ULocale locale) {
if (locale != this.locale) {
mf = mf.withLocale(locale);
// Needed for getLocale(ULocale.VALID_LOCALE)
setLocale(locale, locale);
this.locale = locale;
isReady = false;
}
setLocale(locale, locale);
clearCache();
return this;
}
/**
* Set the locale used for formatting or parsing.
*
* @param locale
* locale of this time unit formatter.
* @param locale locale of this time unit formatter.
* @return this, for chaining.
* @deprecated ICU 53 see {@link MeasureFormat}.
*/
@ -231,9 +201,7 @@ public class TimeUnitFormat extends MeasureFormat {
/**
* Set the format used for formatting or parsing. Passing null is equivalent to passing
* {@link NumberFormat#getNumberInstance(ULocale)}.
*
* @param format
* the number formatter.
* @param format the number formatter.
* @return this, for chaining.
* @deprecated ICU 53 see {@link MeasureFormat}.
*/
@ -245,33 +213,28 @@ public class TimeUnitFormat extends MeasureFormat {
if (format == null) {
if (locale == null) {
isReady = false;
mf = mf.withLocale(ULocale.getDefault());
} else {
this.format = NumberFormat.getNumberInstance(locale);
mf = mf.withNumberFormat(this.format);
}
} else {
this.format = format;
mf = mf.withNumberFormat(this.format);
}
clearCache();
return this;
}
/**
* Format a TimeUnitAmount.
*
* @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
* @deprecated ICU 53 see {@link MeasureFormat}.
*/
@Override
@Deprecated
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return mf.format(obj, toAppendTo, pos);
public NumberFormat getNumberFormat() {
return format;
}
@Override
LocalizedNumberFormatter getNumberFormatter() {
return ((DecimalFormat)format).toNumberFormatter();
}
/**
* Parse a TimeUnitAmount.
*
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
* @deprecated ICU 53 see {@link MeasureFormat}.
*/
@ -313,11 +276,9 @@ public class TimeUnitFormat extends MeasureFormat {
if (tempObj instanceof Number) {
temp = (Number) tempObj;
} else {
// Since we now format the number ourselves, parseObject will likely give us
// back a String
// Since we now format the number ourselves, parseObject will likely give us back a String
// for
// the number. When this happens we must parse the formatted number
// ourselves.
// the number. When this happens we must parse the formatted number ourselves.
try {
temp = format.parse(tempObj.toString());
} catch (ParseException e) {
@ -337,9 +298,8 @@ public class TimeUnitFormat extends MeasureFormat {
}
}
/*
* After find the longest match, parse the number. Result number could be null for the pattern
* without number pattern. such as unit pattern in Arabic. When result number is null, use plural
* rule to set the number.
* After find the longest match, parse the number. Result number could be null for the pattern without number
* pattern. such as unit pattern in Arabic. When result number is null, use plural rule to set the number.
*/
if (resultNumber == null && longestParseDistance != 0) {
// set the number using plurrual count
@ -394,11 +354,8 @@ public class TimeUnitFormat extends MeasureFormat {
ULocale locale;
boolean beenHere;
TimeUnitFormatSetupSink(
Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
int style,
Set<String> pluralKeywords,
ULocale locale) {
TimeUnitFormatSetupSink(Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
int style, Set<String> pluralKeywords, ULocale locale) {
this.timeUnitToCountToPatterns = timeUnitToCountToPatterns;
this.style = style;
this.pluralKeywords = pluralKeywords;
@ -468,21 +425,16 @@ public class TimeUnitFormat extends MeasureFormat {
}
}
private void setup(
String resourceKey,
Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
int style,
private void setup(String resourceKey, Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns, int style,
Set<String> pluralKeywords) {
// fill timeUnitToCountToPatterns from resource file
try {
ICUResourceBundle resource = (ICUResourceBundle) UResourceBundle
.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
ICUResourceBundle resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(
ICUData.ICU_UNIT_BASE_NAME, locale);
TimeUnitFormatSetupSink sink = new TimeUnitFormatSetupSink(timeUnitToCountToPatterns,
style,
pluralKeywords,
locale);
TimeUnitFormatSetupSink sink = new TimeUnitFormatSetupSink(
timeUnitToCountToPatterns, style, pluralKeywords, locale);
resource.getAllItemsWithFallback(resourceKey, sink);
} catch (MissingResourceException e) {
}
@ -516,15 +468,9 @@ public class TimeUnitFormat extends MeasureFormat {
timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
}
for (String pluralCount : keywords) {
if (countToPatterns.get(pluralCount) == null
|| countToPatterns.get(pluralCount)[style] == null) {
if (countToPatterns.get(pluralCount) == null || countToPatterns.get(pluralCount)[style] == null) {
// look through parents
searchInTree(resourceKey,
style,
timeUnit,
pluralCount,
pluralCount,
countToPatterns);
searchInTree(resourceKey, style, timeUnit, pluralCount, pluralCount, countToPatterns);
}
}
}
@ -538,20 +484,15 @@ public class TimeUnitFormat extends MeasureFormat {
// if the pattern is not found even in root, fallback to
// using patterns of plural count "other",
// then, "other" is the searchPluralCount.
private void searchInTree(
String resourceKey,
int styl,
TimeUnit timeUnit,
String srcPluralCount,
String searchPluralCount,
Map<String, Object[]> countToPatterns) {
private void searchInTree(String resourceKey, int styl, TimeUnit timeUnit, String srcPluralCount,
String searchPluralCount, Map<String, Object[]> countToPatterns) {
ULocale parentLocale = locale;
String srcTimeUnitName = timeUnit.toString();
while (parentLocale != null) {
try {
// look for pattern for srcPluralCount in locale tree
ICUResourceBundle unitsRes = (ICUResourceBundle) UResourceBundle
.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, parentLocale);
ICUResourceBundle unitsRes = (ICUResourceBundle) UResourceBundle.getBundleInstance(
ICUData.ICU_UNIT_BASE_NAME, parentLocale);
unitsRes = unitsRes.getWithFallback(resourceKey);
ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(srcTimeUnitName);
String pattern = oneUnitRes.getStringWithFallback(searchPluralCount);
@ -611,39 +552,6 @@ public class TimeUnitFormat extends MeasureFormat {
// boilerplate code to make TimeUnitFormat otherwise follow the contract of
// MeasureFormat
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
@Override
public StringBuilder formatMeasures(
StringBuilder appendTo,
FieldPosition fieldPosition,
Measure... measures) {
return mf.formatMeasures(appendTo, fieldPosition, measures);
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
@Override
public MeasureFormat.FormatWidth getWidth() {
return mf.getWidth();
}
/**
* @internal
* @deprecated This API is ICU internal only.
*/
@Deprecated
@Override
public NumberFormat getNumberFormat() {
return mf.getNumberFormat();
}
/**
* @internal
* @deprecated This API is ICU internal only.
@ -660,7 +568,7 @@ public class TimeUnitFormat extends MeasureFormat {
// Serialization
private Object writeReplace() throws ObjectStreamException {
return mf.toTimeUnitProxy();
return super.toTimeUnitProxy();
}
// Preserve backward serialize backward compatibility.

View file

@ -1676,9 +1676,11 @@ public class MeasureUnitTest extends TestFmwk {
assertEquals("numeric currency", "$2.00", mf.format(USD_2));
mf = MeasureFormat.getInstance(ULocale.JAPAN, FormatWidth.WIDE);
assertEquals("Wide currency", "-1.00 \u7C73\u30C9\u30EB", mf.format(USD_NEG_1));
assertEquals("Wide currency", "1.00 \u7C73\u30C9\u30EB", mf.format(USD_1));
assertEquals("Wide currency", "2.00 \u7C73\u30C9\u30EB", mf.format(USD_2));
// Locale jp does NOT put a space between the number and the currency long name:
// https://unicode.org/cldr/trac/browser/tags/release-32-0-1/common/main/ja.xml?rev=13805#L7046
assertEquals("Wide currency", "-1.00\u7C73\u30C9\u30EB", mf.format(USD_NEG_1));
assertEquals("Wide currency", "1.00\u7C73\u30C9\u30EB", mf.format(USD_1));
assertEquals("Wide currency", "2.00\u7C73\u30C9\u30EB", mf.format(USD_2));
Measure CAD_1 = new Measure(1.0, Currency.getInstance("CAD"));
mf = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
@ -1912,7 +1914,7 @@ public class MeasureUnitTest extends TestFmwk {
new Measure(5.3, MeasureUnit.INCH)));
assertEquals("getLocale", ULocale.ENGLISH, mf.getLocale());
assertEquals("getNumberFormat", ULocale.ENGLISH, mf.getNumberFormat().getLocale(ULocale.VALID_LOCALE));
assertEquals("getWidth", MeasureFormat.FormatWidth.WIDE, mf.getWidth());
assertEquals("getWidth", MeasureFormat.FormatWidth.DEFAULT_CURRENCY, mf.getWidth());
}
@Test

View file

@ -10,6 +10,7 @@ package com.ibm.icu.dev.test.format;
import java.util.Arrays;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@ -72,6 +73,8 @@ public class PluralRangesTest extends TestFmwk {
}
}
// TODO: Re-enable this test when #12454 is fixed.
@Ignore("http://bugs.icu-project.org/trac/ticket/12454")
@Test
public void TestFormatting() {
Object[][] tests = {
@ -108,7 +111,9 @@ public class PluralRangesTest extends TestFmwk {
MeasureFormat mf = MeasureFormat.getInstance(locale, width);
Object actual;
try {
actual = mf.formatMeasureRange(new Measure(low, unit), new Measure(high, unit));
// TODO: Fix this when range formatting is added again.
// To let the code compile, the following line does list formatting.
actual = mf.formatMeasures(new Measure(low, unit), new Measure(high, unit));
} catch (Exception e) {
actual = e.getClass();
}