diff --git a/.gitattributes b/.gitattributes index 9f033a9abf4..159c81d2153 100644 --- a/.gitattributes +++ b/.gitattributes @@ -245,6 +245,8 @@ icu4j/main/classes/core/.project -text icu4j/main/classes/core/.settings/org.eclipse.core.resources.prefs -text icu4j/main/classes/core/.settings/org.eclipse.jdt.core.prefs -text icu4j/main/classes/core/manifest.stub -text +icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormat.java -text +icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormatData.java -text icu4j/main/classes/currdata/.externalToolBuilders/copy-data-currdata.launch -text icu4j/main/classes/currdata/.settings/org.eclipse.core.resources.prefs -text icu4j/main/classes/currdata/.settings/org.eclipse.jdt.core.prefs -text @@ -304,6 +306,7 @@ icu4j/main/tests/core/manifest.stub -text icu4j/main/tests/core/src/com/ibm/icu/dev/data/rbbi/english.dict -text icu4j/main/tests/core/src/com/ibm/icu/dev/data/resources/testmessages.properties -text icu4j/main/tests/core/src/com/ibm/icu/dev/data/thai6.ucs -text +icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/CompactDecimalFormatTest.java -text icu4j/main/tests/core/src/com/ibm/icu/dev/test/serializable/data/ICU_3.6/com.ibm.icu.impl.OlsonTimeZone.dat -text icu4j/main/tests/core/src/com/ibm/icu/dev/test/serializable/data/ICU_3.6/com.ibm.icu.impl.TimeZoneAdapter.dat -text icu4j/main/tests/core/src/com/ibm/icu/dev/test/serializable/data/ICU_3.6/com.ibm.icu.math.BigDecimal.dat -text diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormat.java new file mode 100644 index 00000000000..887a5050bb9 --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormat.java @@ -0,0 +1,240 @@ +/* + ******************************************************************************* + * Copyright (C) 1996-2012, Google, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +package com.ibm.icu.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.ibm.icu.util.ULocale; + +/** + * The CompactDecimalFormat produces abbreviated numbers, suitable for display in environments will limited real estate. + * For example, 'Hits: 1.2B' instead of 'Hits: 1,200,000,000'. The format will be appropriate for the given language, + * such as "1,2 Mrd." for German. + *

+ * For numbers under 1000 trillion (under 10^15, such as 123,456,789,012,345), the result will be short for supported + * languages. However, the result may sometimes exceed 7 characters, such as when there are combining marks or thin + * characters. In such cases, the visual width in fonts should still be short. + *

+ * By default, there are 2 significant digits. After creation, if more than three significant digits are set (with + * setMaximumSignificantDigits), or if a fixed number of digits are set (with setMaximumIntegerDigits or + * setMaximumFractionDigits), then result may be wider. + *

+ * At this time, negative numbers and parsing are not supported, and will produce an UnsupportedOperationException. + * Resetting the pattern prefixes or suffixes is not supported; the method calls are ignored. + *

+ * Note that important methods, like setting the number of decimals, will be moved up from DecimalFormat to + * NumberFormat. + * + * @author markdavis + * @internal + */ +public class CompactDecimalFormat extends DecimalFormat { + private static final long serialVersionUID = 4716293295276629682L; + + private static final int MINIMUM_ARRAY_LENGTH = 15; + private static final int POSITIVE_PREFIX = 0, POSITIVE_SUFFIX = 1, AFFIX_SIZE = 2; + + private final String[] prefix; + private final String[] suffix; + private final long[] divisor; + private final String[] currencyAffixes; + + /** + * The normal mechanism is to use NumberFormat.getCompactDecimalInstance(). This is intended only for testing. + * + * @param locale + * the desired locale + * @param style + * the compact style + * @internal + */ + public CompactDecimalFormat(ULocale locale, CompactStyle style) { + DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(locale); + Data data; + while (true) { + data = localeToData.get(locale); + if (data != null) { + break; + } + locale = locale.equals(zhTW) ? ULocale.TRADITIONAL_CHINESE : locale.getFallback(); + } + this.prefix = data.prefixes; + this.suffix = data.suffixes; + this.divisor = data.divisors; + applyPattern(format.toPattern()); + setDecimalFormatSymbols(format.getDecimalFormatSymbols()); + setMaximumSignificantDigits(2); // default significant digits + setSignificantDigitsUsed(true); + setGroupingUsed(false); + + DecimalFormat currencyFormat = (DecimalFormat) NumberFormat.getCurrencyInstance(locale); + currencyAffixes = new String[AFFIX_SIZE]; + currencyAffixes[CompactDecimalFormat.POSITIVE_PREFIX] = currencyFormat.getPositivePrefix(); + currencyAffixes[CompactDecimalFormat.POSITIVE_SUFFIX] = currencyFormat.getPositiveSuffix(); + // TODO fix to get right symbol for the count + } + + /** + * Create a short number "from scratch". Intended for internal use. The prefix, suffix, and divisor arrays are + * parallel, and provide the information for each power of 10. When formatting a value, the correct power of 10 is + * found, then the value is divided by the divisor, and the prefix and suffix are set (using + * setPositivePrefix/Suffix). + * + * @param pattern + * A number format pattern. Note that the prefix and suffix are discarded, and the decimals are + * overridden by default. + * @param formatSymbols + * Decimal format symbols, typically from a locale. + * @param prefix + * An array of prefix values, one for each power of 10 from 0 to 14 + * @param suffix + * An array of prefix values, one for each power of 10 from 0 to 14 + * @param divisor + * An array of prefix values, one for each power of 10 from 0 to 14 + * @param debugCreationErrors + * A collection of strings for debugging. If null on input, then any errors found will be added to that + * collection instead of throwing exceptions. + * @internal + */ + public CompactDecimalFormat(String pattern, DecimalFormatSymbols formatSymbols, String[] prefix, String[] suffix, + long[] divisor, Collection debugCreationErrors, CompactStyle style, String[] currencyAffixes) { + if (prefix.length < MINIMUM_ARRAY_LENGTH) { + recordError(debugCreationErrors, "Must have at least " + MINIMUM_ARRAY_LENGTH + " prefix items."); + } + if (prefix.length != suffix.length || prefix.length != divisor.length) { + recordError(debugCreationErrors, "Prefix, suffix, and divisor arrays must have the same length."); + } + long oldDivisor = 0; + Map seen = new HashMap(); + for (int i = 0; i < prefix.length; ++i) { + if (prefix[i] == null || suffix[i] == null) { + recordError(debugCreationErrors, "Prefix or suffix is null for " + i); + } + + // divisor must be a power of 10, and must be less than or equal to 10^i + int log = (int) Math.log10(divisor[i]); + if (log > i) { + recordError(debugCreationErrors, "Divisor[" + i + "] must be less than or equal to 10^" + i + + ", but is: " + divisor[i]); + } + long roundTrip = (long) Math.pow(10.0d, log); + if (roundTrip != divisor[i]) { + recordError(debugCreationErrors, "Divisor[" + i + "] must be a power of 10, but is: " + divisor[i]); + } + + // we can't have two different indexes with the same display + String key = prefix[i] + "\uFFFF" + suffix[i] + "\uFFFF" + (i - log); + Integer old = seen.get(key); + if (old != null) { + recordError(debugCreationErrors, "Collision between values for " + i + " and " + old + + " for [prefix/suffix/index-log(divisor)" + key.replace('\uFFFF', ';')); + } else { + seen.put(key, i); + } + if (divisor[i] < oldDivisor) { + recordError(debugCreationErrors, "Bad divisor, the divisor for 10E" + i + "(" + divisor[i] + + ") is less than the divisor for the divisor for 10E" + (i - 1) + "(" + oldDivisor + ")"); + } + oldDivisor = divisor[i]; + } + + this.prefix = prefix.clone(); + this.suffix = suffix.clone(); + this.divisor = divisor.clone(); + applyPattern(pattern); + setDecimalFormatSymbols(formatSymbols); + setMaximumSignificantDigits(2); // default significant digits + setSignificantDigitsUsed(true); + setGroupingUsed(false); + this.currencyAffixes = currencyAffixes.clone(); + } + + @Override + public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { + if (number < 0.0d) { + throw new UnsupportedOperationException("CompactDecimalFormat doesn't handle negative numbers yet."); + } + int integerCount = (int) Math.log10(number); + int base = integerCount > 14 ? 14 : integerCount; + number = number / divisor[base]; + setPositivePrefix(prefix[base]); + setPositiveSuffix(suffix[base]); + setCurrency(null); + return super.format(number, toAppendTo, pos); + } + + @Override + public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { + return format((double) number, toAppendTo, pos); + } + + @Override + public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) { + return format(number.doubleValue(), toAppendTo, pos); + } + + @Override + public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) { + return format(number.doubleValue(), toAppendTo, pos); + } + + @Override + public StringBuffer format(com.ibm.icu.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) { + return format(number.doubleValue(), toAppendTo, pos); + } + + /** + * Parsing is currently unsupported, and throws an UnsupportedOperationException. + */ + @Override + public Number parse(String text, ParsePosition parsePosition) { + throw new UnsupportedOperationException(); + } + + /* INTERNALS */ + + private static ULocale zhTW = new ULocale("zh_TW"); + + private void recordError(Collection creationErrors, String errorMessage) { + if (creationErrors == null) { + throw new IllegalArgumentException(errorMessage); + } + creationErrors.add(errorMessage); + } + + /** JUST FOR DEVELOPMENT */ + // For use with the hard-coded data + static class Data { + public Data(long[] divisors, String[] prefixes, String[] suffixes) { + this.divisors = divisors; + this.prefixes = prefixes; + this.suffixes = suffixes; + } + + long[] divisors; + String[] prefixes; + String[] suffixes; + } + + static Map localeToData = new HashMap(); + + static void add(String locale, long[] ls, String[] prefixes, String[] suffixes) { + localeToData.put(new ULocale(locale), new Data(ls, prefixes, suffixes)); + } + + static { + CompactDecimalFormatData.load(); + } + +} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormatData.java b/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormatData.java new file mode 100644 index 00000000000..a71e9842074 --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/CompactDecimalFormatData.java @@ -0,0 +1,234 @@ +/* + ******************************************************************************* + * Copyright (C) 1996-2012, Google, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +package com.ibm.icu.text; + +/** + * Only until we access the data from CLDR. + */ +class CompactDecimalFormatData { + static void load() { + CompactDecimalFormat.add("af", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mil", " mil", " mil", " mjd", " mjd", " mjd", " bil", " bil", " bil"}); + CompactDecimalFormat.add("am", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "ኪባ", " ሜትር", " ሜትር", " ሜትር", "G", "G", "G", "T", "T", "T"}); + CompactDecimalFormat.add("ar", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("bg", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " Х", " Х", " Х", " М", " М", " М", " Б", " Б", " Б", " Т", " Т", " Т"}); + CompactDecimalFormat.add("bn", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("ca", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " M", " M", " M", " M", "k M", "k M", " B", " B", " B"}); + CompactDecimalFormat.add("cs", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " tis.", " tis.", " tis.", " mil.", " mil.", " mil.", " mld.", " mld.", " mld.", " bil.", " bil.", " bil."}); + CompactDecimalFormat.add("da", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mio", " mio", " mio", " mia", " mia", " mia", " bill", " bill", " bill"}); + CompactDecimalFormat.add("de", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " Mio", " Mio", " Mio", " Mrd", " Mrd", " Mrd", " B", " B", " B"}); + CompactDecimalFormat.add("el", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " χιλ.", " χιλ.", " χιλ.", " εκ.", " εκ.", " εκ.", " δις", " δις", " δις", " τρις", " τρις", " τρις"}); + CompactDecimalFormat.add("en", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("es", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "k", "k", " M", " M", " M", " M", "k M", "k M", " B", " B", " B"}); + CompactDecimalFormat.add("et", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " tuh", " tuh", " tuh", " mln", " mln", " mln", " mld", " mld", " mld", " trl", " trl", " trl"}); + CompactDecimalFormat.add("eu", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " M", " M", " M", " M", "k M", "k M", " B", " B", " B"}); + CompactDecimalFormat.add("fa", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("fi", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " t", " t", " t", " ml", " ml", " ml", " mr", " mr", " mr", " b", " b", " b"}); + CompactDecimalFormat.add("fil", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("fr", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " M", " M", " M", " Md", " Mds", " Mds", " Bn", " Bn", " Bn"}); + CompactDecimalFormat.add("gl", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "k", "k", " M", " M", " M", " M", "k M", "k M", " B", " B", " B"}); + CompactDecimalFormat.add("gu", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("he", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("hi", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("hr", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mil.", " mil.", " mil.", " mlr", " mlr", " mlr", " bil.", " bil.", " bil."}); + CompactDecimalFormat.add("hu", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " e", " e", " e", " m", " m", " m", " mrd", " mrd", " mrd", " b", " b", " b"}); + CompactDecimalFormat.add("id", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", " rb", " rb", " jt", " jt", " jt", " M", " M", " M", " T", " T", " T"}); + CompactDecimalFormat.add("is", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " þ.", " þ.", " þ.", " m.", " m.", " m.", " ma.", " ma.", " ma.", " tr.", " tr.", " tr."}); + CompactDecimalFormat.add("it", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " M", " M", " M", " Md", " Md", " Md", " B", " B", " B"}); + CompactDecimalFormat.add("ja", + new long[]{1L, 1L, 1L, 1L, 10000L, 10000L, 10000L, 10000L, 100000000L, 100000000L, 100000000L, 100000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "万", "万", "万", "万", "億", "億", "億", "億", "兆", "兆", "兆"}); + CompactDecimalFormat.add("kn", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("ko", + new long[]{1L, 1L, 1L, 1L, 10000L, 10000L, 10000L, 10000L, 100000000L, 100000000L, 100000000L, 100000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "만", "만", "만", "만", "억", "억", "억", "억", "조", "조", "조"}); + CompactDecimalFormat.add("lt", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " K", " K", " K", " M", " M", " M", " G", " G", " G", " T", " T", " T"}); + CompactDecimalFormat.add("lv", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " K", " K", " K", " M", " M", " M", " G", " G", " G", " T", " T", " T"}); + CompactDecimalFormat.add("ml", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("mr", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("ms", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("nb", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mill", " mill", " mill", " mrd", " mrd", " mrd", " bill", " bill", " bill"}); + CompactDecimalFormat.add("nl", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mln", " mln", " mln", " mld", " mld", " mld", " bilj", " bilj", " bilj"}); + CompactDecimalFormat.add("pl", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", " tys.", " tys.", " mln", " mln", " mln", " mld", " mld", " mld", " bln", " bln", " bln"}); + CompactDecimalFormat.add("pt", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " mil", " mil", " mil", " mi", " mi", " mi", " bi", " bi", " bi", " tri", " tri", " tri"}); + CompactDecimalFormat.add("ro", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " mii", " mii", " mii", " mil.", " mil.", " mil.", " mld.", " mld.", " mld.", " bln.", " bln.", " bln."}); + CompactDecimalFormat.add("ru", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " тыс.", " тыс.", " тыс.", " млн", " млн", " млн", " млрд", " млрд", " млрд", " трлн", " трлн", " трлн"}); + CompactDecimalFormat.add("sk", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " tis.", " tis.", " tis.", " mil.", " mil.", " mil.", " mld.", " mld.", " mld.", " bil.", " bil.", " bil."}); + CompactDecimalFormat.add("sl", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " tis.", " tis.", " tis.", " mio.", " mio.", " mio.", " mrd.", " mrd.", " mrd.", " bil.", " bil.", " bil."}); + CompactDecimalFormat.add("sr", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", " хиљ", " хиљ", " мил", " мил", " мил", " млрд", " млрд", " млрд", " бил", " бил", " бил"}); + CompactDecimalFormat.add("sv", + new long[]{1L, 1L, 1L, 1L, 1L, 1L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "", "", " mjn", " mjn", " mjn", " mjd", " mjd", " mjd", " trl", " trl", " trl"}); + CompactDecimalFormat.add("sw", + new long[]{1L, 1L, 1L, 1000L, 1000L, 100000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "elfu ", "elfu ", "laki", "M", "M", "M", "B", "B", "B", "T", "T", "T"}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}); + CompactDecimalFormat.add("ta", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("te", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("th", + new long[]{1L, 1L, 1L, 1000L, 10000L, 100000L, 1000000L, 1000000L, 1000000L, 1000000000L, 10000000000L, 100000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " พัน", "หมื่น", "แสน", " ล้าน", " ล้าน", " ล.", " พ.ล.", " ม.ล.", " ส.ล.", " ล.ล.", " ล.ล", " ล.ล."}); + CompactDecimalFormat.add("tr", + new long[]{1L, 1L, 1L, 1L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", " B", " B", " Mn", " Mn", " Mn", " Mr", " Mr", " Mr", " T", " T", " T"}); + CompactDecimalFormat.add("uk", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " тис", " тис", " тис", " млн", " млн", " млн", " млрд", " млрд", " млрд", " трлн", " трлн", " трлн"}); + CompactDecimalFormat.add("ur", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "K", "K", "K", "M", "M", "M", "B", "B", "B", "T", "T", "T"}); + CompactDecimalFormat.add("vi", + new long[]{1L, 1L, 1L, 1000L, 1000L, 1000L, 1000000L, 1000000L, 1000000L, 1000000000L, 1000000000L, 1000000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", " N", " N", " N", " Tr", " Tr", " Tr", " Tỷ", " Tỷ", " Tỷ", " NT", " NT", " NT"}); + CompactDecimalFormat.add("zh", + new long[]{1L, 1L, 1L, 1L, 10000L, 10000L, 10000L, 10000L, 100000000L, 100000000L, 100000000L, 100000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "万", "万", "万", "万", "亿", "亿", "亿", "亿", "兆", "兆", "兆"}); + CompactDecimalFormat.add("zh_Hant", + new long[]{1L, 1L, 1L, 1L, 10000L, 10000L, 10000L, 10000L, 100000000L, 100000000L, 100000000L, 100000000L, 1000000000000L, 1000000000000L, 1000000000000L}, + new String[]{"", "", "", "", "", "", "", "", "", "", "", "", "", "", ""}, + new String[]{"", "", "", "", "萬", "萬", "萬", "萬", "億", "億", "億", "億", "兆", "兆", "兆"}); +}} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java index e2c870e17d1..15817d9aaea 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java @@ -713,6 +713,46 @@ public abstract class NumberFormat extends UFormat { return getInstance(inLocale, SCIENTIFICSTYLE); } + /** + * Style parameter for CompactDecimalFormat. + * @internal + */ + public enum CompactStyle { + /** + * Short version, like "1.2T" + */ + SHORT, + /** + * Longer version, like "1.2 trillion", if available. May return same result as SHORT if not. + */ + LONG + } + + /** + * Create a CompactDecimalFormat appropriate for a locale (Mockup for what would be in NumberFormat). The result may + * be affected by the number system in the locale, such as ar-u-nu-latn. + * + * @param locale the desired locale + * @param style the compact style + * @internal + */ + public static final CompactDecimalFormat getCompactDecimalInstance(ULocale locale, CompactStyle style) { + return new CompactDecimalFormat(locale, style); + } + + /** + * Create a CompactDecimalFormat appropriate for a locale (Mockup for what would be in NumberFormat). The result may + * be affected by the number system in the locale, such as ar-u-nu-latn. + * + * @param locale the desired locale + * @param style the compact style + * @internal + */ + public static final CompactDecimalFormat getCompactDecimalInstance(Locale locale, CompactStyle style) { + return new CompactDecimalFormat(ULocale.forLocale(locale), style); + } + + // ===== Factory stuff ===== /** * A NumberFormatFactory is used to register new number formats. The factory @@ -1796,4 +1836,4 @@ public abstract class NumberFormat extends UFormat { throw new InvalidObjectException("An invalid object."); } } -} +} \ No newline at end of file diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/CompactDecimalFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/CompactDecimalFormatTest.java new file mode 100644 index 00000000000..5f4102a9d42 --- /dev/null +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/CompactDecimalFormatTest.java @@ -0,0 +1,71 @@ +/* + ******************************************************************************* + * Copyright (C) 1996-2012, Google, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +package com.ibm.icu.dev.test.format; + +import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.text.CompactDecimalFormat; +import com.ibm.icu.text.NumberFormat; +import com.ibm.icu.text.NumberFormat.CompactStyle; +import com.ibm.icu.util.ULocale; + +public class CompactDecimalFormatTest extends TestFmwk { + + public static void main(String[] args) { + new CompactDecimalFormatTest().run(args); + } + + Object[][] EnglishTestData = { + // default is 2 digits of accuracy + {1234, "1.2K"}, + {12345, "12K"}, + {123456, "120K"}, + {1234567, "1.2M"}, + {12345678, "12M"}, + {123456789, "120M"}, + {1234567890, "1.2B"}, + {12345678901f, "12B"}, + {123456789012f, "120B"}, + {1234567890123f, "1.2T"}, + {12345678901234f, "12T"}, + {123456789012345f, "120T"}, + }; + + Object[][] JapaneseTestData = { + {1234, "1200"}, + {12345, "1.2万"}, + {123456, "12万"}, + {1234567, "120万"}, + {12345678, "1200万"}, + {123456789, "1.2億"}, + {1234567890, "12億"}, + {12345678901f, "120億"}, + {123456789012f, "1200億"}, + {1234567890123f, "1.2兆"}, + {12345678901234f, "12兆"}, + {123456789012345f, "120兆"}, + }; + + public void TestEnglish() { + checkLocale(ULocale.ENGLISH, EnglishTestData); + } + + public void TestJapanese() { + checkLocale(ULocale.JAPANESE, JapaneseTestData); + } + + public void TestJapaneseGermany() { + // check fallback. + checkLocale(ULocale.forLanguageTag("ja-DE"), JapaneseTestData); + } + + public void checkLocale(ULocale locale, Object[][] testData) { + CompactDecimalFormat cdf = NumberFormat.getCompactDecimalInstance(locale, CompactStyle.SHORT); + for (Object[] row : testData) { + assertEquals(locale + " (" + locale.getDisplayName(locale) + ")", row[1], cdf.format((Number) row[0])); + } + } +} diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java index 8f88327e515..05cdb347675 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/Relation.java @@ -1,6 +1,6 @@ /* ********************************************************************** - * Copyright (c) 2002-2011, International Business Machines + * Copyright (c) 2002-2012, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * Author: Mark Davis @@ -199,9 +199,12 @@ public class Relation implements Freezable { // TODO: add , Map values() { - Set result = newSet(); - for (K key : data.keySet()) { - result.addAll(data.get(key)); + return values(new LinkedHashSet()); + } + + public > C values(C result) { + for (Entry> keyValue : data.entrySet()) { + result.addAll(keyValue.getValue()); } return result; }