ICU-10600 add plural ranges and unit formatting
X-SVN-Rev: 35997
This commit is contained in:
parent
719e780320
commit
adc2570f18
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2013, International Business Machines Corporation and *
|
||||
* Copyright (C) 2008-2014, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -10,13 +10,18 @@ import java.text.ParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.ibm.icu.text.NumberingSystem;
|
||||
import com.ibm.icu.text.PluralRanges;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
@ -29,6 +34,8 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
||||
private Map<String, String> localeIdToCardinalRulesId;
|
||||
private Map<String, String> localeIdToOrdinalRulesId;
|
||||
private Map<String, ULocale> rulesIdToEquivalentULocale;
|
||||
private static Map<String, PluralRanges> localeIdToPluralRanges;
|
||||
|
||||
|
||||
/**
|
||||
* Access through singleton.
|
||||
@ -140,7 +147,7 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
||||
tempLocaleIdToOrdinalRulesId = Collections.emptyMap();
|
||||
tempRulesIdToEquivalentULocale = Collections.emptyMap();
|
||||
}
|
||||
|
||||
|
||||
synchronized(this) {
|
||||
if (localeIdToCardinalRulesId == null) {
|
||||
localeIdToCardinalRulesId = tempLocaleIdToCardinalRulesId;
|
||||
@ -253,4 +260,245 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
||||
public boolean hasOverride(ULocale locale) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final PluralRanges UNKNOWN_RANGE = new PluralRanges().freeze();
|
||||
|
||||
public PluralRanges getPluralRanges(ULocale locale) {
|
||||
// TODO markdavis Fix the bad fallback, here and elsewhere in this file.
|
||||
String localeId = ULocale.canonicalize(locale.getBaseName());
|
||||
PluralRanges result;
|
||||
while (null == (result = localeIdToPluralRanges.get(localeId))) {
|
||||
int ix = localeId.lastIndexOf("_");
|
||||
if (ix == -1) {
|
||||
result = UNKNOWN_RANGE;
|
||||
break;
|
||||
}
|
||||
localeId = localeId.substring(0, ix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isPluralRangesAvailable(ULocale locale) {
|
||||
return getPluralRanges(locale) == UNKNOWN_RANGE;
|
||||
}
|
||||
|
||||
// TODO markdavis FIX HARD-CODED HACK once we have data from CLDR in the bundles
|
||||
static {
|
||||
String[][] pluralRangeData = {
|
||||
{"locales", "id ja km ko lo ms my th vi zh"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "am bn fr gu hi hy kn mr pa zu"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "fa"},
|
||||
{"one", "one", "other"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ka"},
|
||||
{"one", "other", "one"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "az de el gl hu it kk ky ml mn ne nl pt sq sw ta te tr ug uz"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "af bg ca en es et eu fi nb sv ur"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "da fil is"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "si"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "mk"},
|
||||
{"one", "one", "other"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "lv"},
|
||||
{"zero", "zero", "other"},
|
||||
{"zero", "one", "one"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "zero", "other"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "zero", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ro"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "few"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "hr sr bs"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "one"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "sl"},
|
||||
{"one", "one", "few"},
|
||||
{"one", "two", "two"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "one", "few"},
|
||||
{"two", "two", "two"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "one", "few"},
|
||||
{"few", "two", "two"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "one", "few"},
|
||||
{"other", "two", "two"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "he"},
|
||||
{"one", "two", "other"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "many", "other"},
|
||||
{"two", "other", "other"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "many"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "two", "other"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "cs pl sk"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "one", "one"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "lt ru uk"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "one"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "one", "one"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "cy"},
|
||||
{"zero", "one", "one"},
|
||||
{"zero", "two", "two"},
|
||||
{"zero", "few", "few"},
|
||||
{"zero", "many", "many"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "two", "two"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "many", "many"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "two", "two"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ar"},
|
||||
{"zero", "one", "zero"},
|
||||
{"zero", "two", "zero"},
|
||||
{"zero", "few", "few"},
|
||||
{"zero", "many", "many"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "two", "other"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "many", "many"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "two", "other"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
};
|
||||
PluralRanges pr = null;
|
||||
String[] locales = null;
|
||||
HashMap<String, PluralRanges> tempLocaleIdToPluralRanges = new HashMap<String, PluralRanges>();
|
||||
for (String[] row : pluralRangeData) {
|
||||
if (row[0].equals("locales")) {
|
||||
if (pr != null) {
|
||||
pr.freeze();
|
||||
for (String locale : locales) {
|
||||
tempLocaleIdToPluralRanges.put(locale, pr);
|
||||
}
|
||||
}
|
||||
locales = row[1].split(" ");
|
||||
pr = new PluralRanges();
|
||||
} else {
|
||||
pr.add(
|
||||
StandardPluralCategories.valueOf(row[0]),
|
||||
StandardPluralCategories.valueOf(row[1]),
|
||||
StandardPluralCategories.valueOf(row[2]));
|
||||
}
|
||||
}
|
||||
// do last one
|
||||
for (String locale : locales) {
|
||||
tempLocaleIdToPluralRanges.put(locale, pr);
|
||||
}
|
||||
// now make whole thing immutable
|
||||
localeIdToPluralRanges = Collections.unmodifiableMap(tempLocaleIdToPluralRanges);
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (c) 2004-2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
* Author: Alan Liu
|
||||
* Created: April 20, 2004
|
||||
* Since: ICU 3.0
|
||||
**********************************************************************
|
||||
*/
|
||||
**********************************************************************
|
||||
* Copyright (c) 2004-2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
* Author: Alan Liu
|
||||
* Created: April 20, 2004
|
||||
* Since: ICU 3.0
|
||||
**********************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.Externalizable;
|
||||
@ -26,12 +26,14 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.ibm.icu.impl.DontCareFieldPosition;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.SimplePatternFormatter;
|
||||
import com.ibm.icu.math.BigDecimal;
|
||||
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.CurrencyAmount;
|
||||
import com.ibm.icu.util.Measure;
|
||||
@ -104,47 +106,47 @@ import com.ibm.icu.util.UResourceBundle;
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
public class MeasureFormat extends UFormat {
|
||||
|
||||
|
||||
|
||||
// Generated by serialver from JDK 1.4.1_01
|
||||
static final long serialVersionUID = -7182021401701778240L;
|
||||
|
||||
|
||||
private final transient ImmutableNumberFormat numberFormat;
|
||||
|
||||
|
||||
private final transient FormatWidth formatWidth;
|
||||
|
||||
|
||||
// PluralRules is documented as being immutable which implies thread-safety.
|
||||
private final transient PluralRules rules;
|
||||
|
||||
|
||||
// Measure unit -> format width -> plural form -> pattern ("{0} meters")
|
||||
private final transient Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat;
|
||||
|
||||
|
||||
private final transient NumericFormatters numericFormatters;
|
||||
|
||||
|
||||
private final transient ImmutableNumberFormat currencyFormat;
|
||||
|
||||
|
||||
private final transient ImmutableNumberFormat integerFormat;
|
||||
|
||||
private static final SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>> localeToUnitToStyleToCountToFormat
|
||||
= new SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>>();
|
||||
|
||||
= new SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>>();
|
||||
|
||||
private static final SimpleCache<ULocale, NumericFormatters> localeToNumericDurationFormatters
|
||||
= new SimpleCache<ULocale,NumericFormatters>();
|
||||
|
||||
= new SimpleCache<ULocale,NumericFormatters>();
|
||||
|
||||
private static final Map<MeasureUnit, Integer> hmsTo012 =
|
||||
new HashMap<MeasureUnit, Integer>();
|
||||
|
||||
|
||||
static {
|
||||
hmsTo012.put(MeasureUnit.HOUR, 0);
|
||||
hmsTo012.put(MeasureUnit.MINUTE, 1);
|
||||
hmsTo012.put(MeasureUnit.SECOND, 2);
|
||||
}
|
||||
|
||||
|
||||
// For serialization: sub-class types.
|
||||
private static final int MEASURE_FORMAT = 0;
|
||||
private static final int TIME_UNIT_FORMAT = 1;
|
||||
private static final int CURRENCY_FORMAT = 2;
|
||||
|
||||
|
||||
/**
|
||||
* Formatting width enum.
|
||||
*
|
||||
@ -154,7 +156,7 @@ public class MeasureFormat extends UFormat {
|
||||
// Be sure to update MeasureUnitTest.TestSerialFormatWidthEnum
|
||||
// when adding an enum value.
|
||||
public enum FormatWidth {
|
||||
|
||||
|
||||
/**
|
||||
* Spell out everything.
|
||||
*
|
||||
@ -162,7 +164,7 @@ public class MeasureFormat extends UFormat {
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
WIDE("units", ListFormatter.Style.DURATION, NumberFormat.PLURALCURRENCYSTYLE),
|
||||
|
||||
|
||||
/**
|
||||
* Abbreviate when possible.
|
||||
*
|
||||
@ -170,7 +172,7 @@ public class MeasureFormat extends UFormat {
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT("unitsShort", ListFormatter.Style.DURATION_SHORT, NumberFormat.ISOCURRENCYSTYLE),
|
||||
|
||||
|
||||
/**
|
||||
* Brief. Use only a symbol for the unit when possible.
|
||||
*
|
||||
@ -178,7 +180,7 @@ public class MeasureFormat extends UFormat {
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
NARROW("unitsNarrow", ListFormatter.Style.DURATION_NARROW, NumberFormat.CURRENCYSTYLE),
|
||||
|
||||
|
||||
/**
|
||||
* Identical to NARROW except when formatMeasures is called with
|
||||
* an hour and minute; minute and second; or hour, minute, and second Measures.
|
||||
@ -188,29 +190,29 @@ public class MeasureFormat extends UFormat {
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
NUMERIC("unitsNarrow", ListFormatter.Style.DURATION_NARROW, NumberFormat.CURRENCYSTYLE);
|
||||
|
||||
|
||||
// Be sure to update the toFormatWidth and fromFormatWidth() functions
|
||||
// when adding an enum value.
|
||||
|
||||
|
||||
final String resourceKey;
|
||||
private final ListFormatter.Style listFormatterStyle;
|
||||
private final int currencyStyle;
|
||||
|
||||
|
||||
private FormatWidth(String resourceKey, ListFormatter.Style style, int currencyStyle) {
|
||||
this.resourceKey = resourceKey;
|
||||
this.listFormatterStyle = style;
|
||||
this.currencyStyle = currencyStyle;
|
||||
}
|
||||
|
||||
|
||||
ListFormatter.Style getListFormatterStyle() {
|
||||
return listFormatterStyle;
|
||||
}
|
||||
|
||||
|
||||
int getCurrencyStyle() {
|
||||
return currencyStyle;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a format from the locale, formatWidth, and format.
|
||||
*
|
||||
@ -223,7 +225,7 @@ public class MeasureFormat extends UFormat {
|
||||
public static MeasureFormat getInstance(ULocale locale, FormatWidth formatWidth) {
|
||||
return getInstance(locale, formatWidth, NumberFormat.getInstance(locale));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a format from the JDK locale, formatWidth, and format.
|
||||
*
|
||||
@ -276,9 +278,9 @@ public class MeasureFormat extends UFormat {
|
||||
formatters,
|
||||
new ImmutableNumberFormat(
|
||||
NumberFormat.getInstance(locale, formatWidth.getCurrencyStyle())),
|
||||
new ImmutableNumberFormat(intFormat));
|
||||
new ImmutableNumberFormat(intFormat));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a format from the JDK locale, formatWidth, and format.
|
||||
*
|
||||
@ -341,7 +343,7 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return toAppendTo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses text from a string to produce a <code>Measure</code>.
|
||||
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
|
||||
@ -353,7 +355,7 @@ public class MeasureFormat extends UFormat {
|
||||
public Measure parseObject(String source, ParsePosition pos) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format a sequence of measures. Uses the ListFormatter unit lists.
|
||||
* So, for example, one could format “3 feet, 2 inches”.
|
||||
@ -374,7 +376,48 @@ public class MeasureFormat extends UFormat {
|
||||
DontCareFieldPosition.INSTANCE,
|
||||
measures).toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format a range of measures, such as "3.4-5.1 meters". It is the caller’s
|
||||
* responsibility to have the appropriate values in appropriate order,
|
||||
* and using the appropriate Number values. Typically the units should be
|
||||
* in ascending order.
|
||||
*
|
||||
* @param lowValue low value in range
|
||||
* @param highValue high value in range
|
||||
* @return the formatted string.
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final String formatMeasureRange(Measure lowValue, Measure highValue) {
|
||||
MeasureUnit unit = lowValue.getUnit();
|
||||
if (!unit.equals(highValue.getUnit())) {
|
||||
throw new IllegalArgumentException("Units must match: " + unit + " ≠ " + highValue.getUnit());
|
||||
}
|
||||
Number lowNumber = lowValue.getNumber();
|
||||
Number highNumber = highValue.getNumber();
|
||||
|
||||
UFieldPosition fpos = new UFieldPosition();
|
||||
StringBuffer lowFormatted = numberFormat.format(lowNumber, new StringBuffer(), fpos);
|
||||
String keywordLow = rules.select(new PluralRules.FixedDecimal(lowNumber.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
|
||||
|
||||
StringBuffer highFormatted = numberFormat.format(highNumber, new StringBuffer(), fpos);
|
||||
String keywordHigh = rules.select(new PluralRules.FixedDecimal(highNumber.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
|
||||
|
||||
StandardPluralCategories resolvedCategory = PluralRules.getRange(
|
||||
getLocale(),
|
||||
StandardPluralCategories.valueOf(keywordLow),
|
||||
StandardPluralCategories.valueOf(keywordHigh));
|
||||
|
||||
Map<FormatWidth, QuantityFormatter> styleToCountToFormat = unitToStyleToCountToFormat.get(lowValue.getUnit());
|
||||
QuantityFormatter countToFormat = styleToCountToFormat.get(formatWidth);
|
||||
SimplePatternFormatter formatter = countToFormat.getByVariant(resolvedCategory.toString());
|
||||
SimplePatternFormatter rangeFormatter = getRangeFormat(getLocale(), formatWidth);
|
||||
String formattedNumber = rangeFormatter.format(lowFormatted, highFormatted);
|
||||
return formatter.format(formattedNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a sequence of measures.
|
||||
*
|
||||
@ -399,7 +442,7 @@ public class MeasureFormat extends UFormat {
|
||||
if (measures.length == 1) {
|
||||
return formatMeasure(measures[0], numberFormat, appendTo, fieldPosition);
|
||||
}
|
||||
|
||||
|
||||
if (formatWidth == FormatWidth.NUMERIC) {
|
||||
// If we have just hour, minute, or second follow the numeric
|
||||
// track.
|
||||
@ -408,7 +451,7 @@ public class MeasureFormat extends UFormat {
|
||||
return formatNumeric(hms, appendTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ListFormatter listFormatter = ListFormatter.getInstance(
|
||||
getLocale(), formatWidth.getListFormatterStyle());
|
||||
if (fieldPosition != DontCareFieldPosition.INSTANCE) {
|
||||
@ -422,9 +465,9 @@ public class MeasureFormat extends UFormat {
|
||||
i == measures.length - 1 ? numberFormat : integerFormat);
|
||||
}
|
||||
return appendTo.append(listFormatter.format((Object[]) results));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Two MeasureFormats, a and b, are equal if and only if they have the same formatWidth,
|
||||
* locale, and equal number formats.
|
||||
@ -445,7 +488,7 @@ public class MeasureFormat extends UFormat {
|
||||
&& getLocale().equals(rhs.getLocale())
|
||||
&& getNumberFormat().equals(rhs.getNumberFormat());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @draft ICU 53
|
||||
@ -457,7 +500,7 @@ public class MeasureFormat extends UFormat {
|
||||
return (getLocale().hashCode() * 31
|
||||
+ getNumberFormat().hashCode()) * 31 + getWidth().hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the format width this instance is using.
|
||||
* @draft ICU 53
|
||||
@ -466,7 +509,7 @@ public class MeasureFormat extends UFormat {
|
||||
public MeasureFormat.FormatWidth getWidth() {
|
||||
return formatWidth;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the locale of this instance.
|
||||
* @draft ICU 53
|
||||
@ -475,7 +518,7 @@ public class MeasureFormat extends UFormat {
|
||||
public final ULocale getLocale() {
|
||||
return getLocale(ULocale.VALID_LOCALE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy of the number format.
|
||||
* @draft ICU 53
|
||||
@ -518,7 +561,7 @@ public class MeasureFormat extends UFormat {
|
||||
public static MeasureFormat getCurrencyFormat() {
|
||||
return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
|
||||
}
|
||||
|
||||
|
||||
// This method changes the NumberFormat object as well to match the new locale.
|
||||
MeasureFormat withLocale(ULocale locale) {
|
||||
return MeasureFormat.getInstance(locale, getWidth());
|
||||
@ -535,7 +578,7 @@ public class MeasureFormat extends UFormat {
|
||||
this.currencyFormat,
|
||||
this.integerFormat);
|
||||
}
|
||||
|
||||
|
||||
private MeasureFormat(
|
||||
ULocale locale,
|
||||
FormatWidth formatWidth,
|
||||
@ -554,7 +597,7 @@ public class MeasureFormat extends UFormat {
|
||||
this.currencyFormat = currencyFormat;
|
||||
this.integerFormat = integerFormat;
|
||||
}
|
||||
|
||||
|
||||
MeasureFormat() {
|
||||
// Make compiler happy by setting final fields to null.
|
||||
this.formatWidth = null;
|
||||
@ -565,12 +608,12 @@ public class MeasureFormat extends UFormat {
|
||||
this.currencyFormat = null;
|
||||
this.integerFormat = null;
|
||||
}
|
||||
|
||||
|
||||
static class NumericFormatters {
|
||||
private DateFormat hourMinute;
|
||||
private DateFormat minuteSecond;
|
||||
private DateFormat hourMinuteSecond;
|
||||
|
||||
|
||||
public NumericFormatters(
|
||||
DateFormat hourMinute,
|
||||
DateFormat minuteSecond,
|
||||
@ -579,12 +622,12 @@ public class MeasureFormat extends UFormat {
|
||||
this.minuteSecond = minuteSecond;
|
||||
this.hourMinuteSecond = hourMinuteSecond;
|
||||
}
|
||||
|
||||
|
||||
public DateFormat getHourMinute() { return hourMinute; }
|
||||
public DateFormat getMinuteSecond() { return minuteSecond; }
|
||||
public DateFormat getHourMinuteSecond() { return hourMinuteSecond; }
|
||||
}
|
||||
|
||||
|
||||
private static NumericFormatters loadNumericFormatters(
|
||||
ULocale locale) {
|
||||
ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
|
||||
@ -594,7 +637,7 @@ public class MeasureFormat extends UFormat {
|
||||
loadNumericDurationFormat(r, "ms"),
|
||||
loadNumericDurationFormat(r, "hms"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns formatting data for all MeasureUnits except for currency ones.
|
||||
*/
|
||||
@ -602,7 +645,7 @@ public class MeasureFormat extends UFormat {
|
||||
ULocale locale, PluralRules rules) {
|
||||
QuantityFormatter.Builder builder = new QuantityFormatter.Builder();
|
||||
Map<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>> unitToStyleToCountToFormat
|
||||
= new HashMap<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>();
|
||||
= new HashMap<MeasureUnit, EnumMap<FormatWidth, QuantityFormatter>>();
|
||||
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable()) {
|
||||
// Currency data cannot be found here. Skip.
|
||||
@ -654,13 +697,13 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return unitToStyleToCountToFormat;
|
||||
}
|
||||
|
||||
|
||||
private String formatMeasure(Measure measure, ImmutableNumberFormat nf) {
|
||||
return formatMeasure(
|
||||
measure, nf, new StringBuilder(),
|
||||
DontCareFieldPosition.INSTANCE).toString();
|
||||
}
|
||||
|
||||
|
||||
private StringBuilder formatMeasure(
|
||||
Measure measure,
|
||||
ImmutableNumberFormat nf,
|
||||
@ -669,10 +712,10 @@ public class MeasureFormat extends UFormat {
|
||||
if (measure.getUnit() instanceof Currency) {
|
||||
return appendTo.append(
|
||||
currencyFormat.format(
|
||||
new CurrencyAmount(measure.getNumber(), (Currency) measure.getUnit()),
|
||||
new StringBuffer(),
|
||||
fieldPosition));
|
||||
|
||||
new CurrencyAmount(measure.getNumber(), (Currency) measure.getUnit()),
|
||||
new StringBuffer(),
|
||||
fieldPosition));
|
||||
|
||||
}
|
||||
Number n = measure.getNumber();
|
||||
MeasureUnit unit = measure.getUnit();
|
||||
@ -694,24 +737,24 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
// Wrapper around NumberFormat that provides immutability and thread-safety.
|
||||
private static final class ImmutableNumberFormat {
|
||||
private NumberFormat nf;
|
||||
|
||||
|
||||
public ImmutableNumberFormat(NumberFormat nf) {
|
||||
this.nf = (NumberFormat) nf.clone();
|
||||
}
|
||||
|
||||
|
||||
public synchronized NumberFormat get() {
|
||||
return (NumberFormat) nf.clone();
|
||||
}
|
||||
|
||||
|
||||
public synchronized StringBuffer format(
|
||||
Number n, StringBuffer buffer, FieldPosition pos) {
|
||||
return nf.format(n, buffer, pos);
|
||||
}
|
||||
|
||||
|
||||
public synchronized StringBuffer format(
|
||||
CurrencyAmount n, StringBuffer buffer, FieldPosition pos) {
|
||||
return nf.format(n, buffer, pos);
|
||||
@ -722,7 +765,7 @@ public class MeasureFormat extends UFormat {
|
||||
return nf.format(number);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static final class PatternData {
|
||||
final String prefix;
|
||||
final String suffix;
|
||||
@ -741,26 +784,26 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Object toTimeUnitProxy() {
|
||||
return new MeasureProxy(getLocale(), formatWidth, numberFormat.get(), TIME_UNIT_FORMAT);
|
||||
}
|
||||
|
||||
|
||||
Object toCurrencyProxy() {
|
||||
return new MeasureProxy(getLocale(), formatWidth, numberFormat.get(), CURRENCY_FORMAT);
|
||||
}
|
||||
|
||||
|
||||
private StringBuilder formatMeasuresSlowTrack(
|
||||
ListFormatter listFormatter,
|
||||
StringBuilder appendTo,
|
||||
FieldPosition fieldPosition,
|
||||
Measure... measures) {
|
||||
String[] results = new String[measures.length];
|
||||
|
||||
|
||||
// Zero out our field position so that we can tell when we find our field.
|
||||
FieldPosition fpos = new FieldPosition(
|
||||
fieldPosition.getFieldAttribute(), fieldPosition.getField());
|
||||
|
||||
|
||||
int fieldPositionFoundIndex = -1;
|
||||
for (int i = 0; i < measures.length; ++i) {
|
||||
ImmutableNumberFormat nf = (i == measures.length - 1 ? numberFormat : integerFormat);
|
||||
@ -775,7 +818,7 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
ListFormatter.FormattedListBuilder builder =
|
||||
listFormatter.format(Arrays.asList(results), fieldPositionFoundIndex);
|
||||
|
||||
|
||||
// Fix up FieldPosition indexes if our field is found.
|
||||
if (builder.getOffset() != -1) {
|
||||
fieldPosition.setBeginIndex(fpos.getBeginIndex() + builder.getOffset() + appendTo.length());
|
||||
@ -783,7 +826,7 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return appendTo.append(builder.toString());
|
||||
}
|
||||
|
||||
|
||||
// type is one of "hm", "ms" or "hms"
|
||||
private static DateFormat loadNumericDurationFormat(
|
||||
ICUResourceBundle r, String type) {
|
||||
@ -793,7 +836,7 @@ public class MeasureFormat extends UFormat {
|
||||
result.setTimeZone(TimeZone.GMT_ZONE);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Returns hours in [0]; minutes in [1]; seconds in [2] out of measures array. If
|
||||
// unsuccessful, e.g measures has other measurements besides hours, minutes, seconds;
|
||||
// hours, minutes, seconds are out of order; or have negative values, returns null.
|
||||
@ -820,11 +863,11 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Formats numeric time duration as 5:00:47 or 3:54. In the process, it replaces any null
|
||||
// values in hms with 0.
|
||||
private StringBuilder formatNumeric(Number[] hms, StringBuilder appendable) {
|
||||
|
||||
|
||||
// find the start and end of non-nil values in hms array. We have to know if we
|
||||
// have hour-minute; minute-second; or hour-minute-second.
|
||||
int startIndex = -1;
|
||||
@ -874,7 +917,7 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
|
||||
// Formats a duration as 5:00:37 or 23:59.
|
||||
// duration is a particular duration after epoch.
|
||||
// formatter is a hour-minute-second, hour-minute, or minute-second formatter.
|
||||
@ -892,7 +935,7 @@ public class MeasureFormat extends UFormat {
|
||||
StringBuilder appendTo) {
|
||||
// Format the smallest amount ahead of time.
|
||||
String smallestAmountFormatted;
|
||||
|
||||
|
||||
// Format the smallest amount using this object's number format, but keep track
|
||||
// of the integer portion of this formatted amount. We have to replace just the
|
||||
// integer part with the corresponding value from formatting the date. Otherwise
|
||||
@ -909,23 +952,23 @@ public class MeasureFormat extends UFormat {
|
||||
FieldPosition smallestFieldPosition = new FieldPosition(smallestField);
|
||||
String draft = formatter.format(
|
||||
duration, new StringBuffer(), smallestFieldPosition).toString();
|
||||
|
||||
|
||||
// If we find the smallest field
|
||||
if (smallestFieldPosition.getBeginIndex() != 0
|
||||
|| smallestFieldPosition.getEndIndex() != 0) {
|
||||
// add everything up to the start of the smallest field in duration.
|
||||
appendTo.append(draft, 0, smallestFieldPosition.getBeginIndex());
|
||||
|
||||
|
||||
// add everything in the smallest field up to the integer portion
|
||||
appendTo.append(smallestAmountFormatted, 0, intFieldPosition.getBeginIndex());
|
||||
|
||||
|
||||
// Add the smallest field in formatted duration in lieu of the integer portion
|
||||
// of smallest field
|
||||
appendTo.append(
|
||||
draft,
|
||||
smallestFieldPosition.getBeginIndex(),
|
||||
smallestFieldPosition.getEndIndex());
|
||||
|
||||
|
||||
// Add the rest of the smallest field
|
||||
appendTo.append(
|
||||
smallestAmountFormatted,
|
||||
@ -938,15 +981,15 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new MeasureProxy(
|
||||
getLocale(), formatWidth, numberFormat.get(), MEASURE_FORMAT);
|
||||
}
|
||||
|
||||
|
||||
static class MeasureProxy implements Externalizable {
|
||||
private static final long serialVersionUID = -6033308329886716770L;
|
||||
|
||||
|
||||
private ULocale locale;
|
||||
private FormatWidth formatWidth;
|
||||
private NumberFormat numberFormat;
|
||||
@ -988,7 +1031,7 @@ public class MeasureFormat extends UFormat {
|
||||
throw new InvalidObjectException("Missing number format.");
|
||||
}
|
||||
subClass = in.readByte() & 0xFF;
|
||||
|
||||
|
||||
// This cast is safe because the serialized form of hashtable can have
|
||||
// any object as the key and any object as the value.
|
||||
keyValues = (HashMap<Object, Object>) in.readObject();
|
||||
@ -996,7 +1039,7 @@ public class MeasureFormat extends UFormat {
|
||||
throw new InvalidObjectException("Missing optional values map.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private TimeUnitFormat createTimeUnitFormat() throws InvalidObjectException {
|
||||
int style;
|
||||
if (formatWidth == FormatWidth.WIDE) {
|
||||
@ -1024,7 +1067,7 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static FormatWidth fromFormatWidthOrdinal(int ordinal) {
|
||||
FormatWidth[] values = FormatWidth.values();
|
||||
if (ordinal < 0 || ordinal >= values.length) {
|
||||
@ -1032,4 +1075,53 @@ public class MeasureFormat extends UFormat {
|
||||
}
|
||||
return values[ordinal];
|
||||
}
|
||||
|
||||
static final Map<ULocale, SimplePatternFormatter> localeIdToRangeFormat
|
||||
= new ConcurrentHashMap<ULocale, SimplePatternFormatter>();
|
||||
|
||||
/**
|
||||
* Return a simple pattern formatter for a range, such as "{0}–{1}".
|
||||
* @param forLocale locale to get the format for
|
||||
* @param width the format width
|
||||
* @return range formatter, such as "{0}–{1}"
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
public SimplePatternFormatter getRangeFormat(ULocale forLocale, FormatWidth width) {
|
||||
// TODO fix Hack for French
|
||||
if (width != FormatWidth.WIDE && forLocale.getLanguage().equals("fr")) {
|
||||
return getRangeFormat(ULocale.ROOT, width);
|
||||
}
|
||||
SimplePatternFormatter result = localeIdToRangeFormat.get(forLocale);
|
||||
if (result == null) {
|
||||
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.
|
||||
getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, forLocale);
|
||||
ULocale realLocale = rb.getULocale();
|
||||
if (!forLocale.equals(realLocale)) { // if the child would inherit, then add a cache entry for it.
|
||||
result = localeIdToRangeFormat.get(forLocale);
|
||||
if (result != null) {
|
||||
localeIdToRangeFormat.put(forLocale, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// At this point, both the forLocale and the realLocale don't have an item
|
||||
// So we have to make one.
|
||||
NumberingSystem ns = NumberingSystem.getInstance(forLocale);
|
||||
|
||||
String resultString = null;
|
||||
try {
|
||||
resultString = rb.getStringWithFallback("NumberElements/" + ns.getName() + "/miscPatterns/range");
|
||||
} catch ( MissingResourceException ex ) {
|
||||
resultString = rb.getStringWithFallback("NumberElements/latn/patterns/range");
|
||||
}
|
||||
result = SimplePatternFormatter.compile(resultString);
|
||||
localeIdToRangeFormat.put(forLocale, result);
|
||||
if (!forLocale.equals(realLocale)) {
|
||||
localeIdToRangeFormat.put(realLocale, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
307
icu4j/main/classes/core/src/com/ibm/icu/text/PluralRanges.java
Normal file
307
icu4j/main/classes/core/src/com/ibm/icu/text/PluralRanges.java
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2014, Google, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
|
||||
import com.ibm.icu.util.Freezable;
|
||||
import com.ibm.icu.util.Output;
|
||||
|
||||
/**
|
||||
* Utility class for returning the plural category for a range of numbers, such as 1–5, so that appropriate messages can
|
||||
* be chosen. The rules for determining this value vary widely across locales.
|
||||
*
|
||||
* @author markdavis
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class PluralRanges implements Freezable<PluralRanges>, Comparable<PluralRanges> {
|
||||
|
||||
private volatile boolean isFrozen;
|
||||
private Matrix matrix = new Matrix();
|
||||
private boolean[] explicit = new boolean[StandardPluralCategories.COUNT];
|
||||
|
||||
/**
|
||||
* Internal class for mapping from two StandardPluralCategories values to another.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public static final class Matrix implements Comparable<Matrix>, Cloneable {
|
||||
private byte[] data = new byte[StandardPluralCategories.COUNT * StandardPluralCategories.COUNT];
|
||||
{
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
data[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for setting.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public void set(StandardPluralCategories start, StandardPluralCategories end, StandardPluralCategories result) {
|
||||
data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()] = result == null ? (byte) -1
|
||||
: (byte) result.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for setting; throws exception if already set.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public void setIfNew(StandardPluralCategories start, StandardPluralCategories end,
|
||||
StandardPluralCategories result) {
|
||||
byte old = data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()];
|
||||
if (old >= 0) {
|
||||
throw new IllegalArgumentException("Previously set value for <" + start + ", " + end + ", "
|
||||
+ StandardPluralCategories.VALUES.get(old) + ">");
|
||||
}
|
||||
data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()] = result == null ? (byte) -1
|
||||
: (byte) result.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for getting.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public StandardPluralCategories get(StandardPluralCategories start, StandardPluralCategories end) {
|
||||
byte result = data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()];
|
||||
return result < 0 ? null : StandardPluralCategories.VALUES.get(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to see if <*,end> values are all the same.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public StandardPluralCategories endSame(StandardPluralCategories end) {
|
||||
StandardPluralCategories first = null;
|
||||
for (StandardPluralCategories start : StandardPluralCategories.VALUES) {
|
||||
StandardPluralCategories item = get(start, end);
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (first == null) {
|
||||
first = item;
|
||||
continue;
|
||||
}
|
||||
if (first != item) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to see if <start,*> values are all the same.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public StandardPluralCategories startSame(StandardPluralCategories start,
|
||||
EnumSet<StandardPluralCategories> endDone, Output<Boolean> emit) {
|
||||
emit.value = false;
|
||||
StandardPluralCategories first = null;
|
||||
for (StandardPluralCategories end : StandardPluralCategories.VALUES) {
|
||||
StandardPluralCategories item = get(start, end);
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (first == null) {
|
||||
first = item;
|
||||
continue;
|
||||
}
|
||||
if (first != item) {
|
||||
return null;
|
||||
}
|
||||
// only emit if we didn't cover with the 'end' values
|
||||
if (!endDone.contains(end)) {
|
||||
emit.value = true;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
result = result * 37 + data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Matrix)) {
|
||||
return false;
|
||||
}
|
||||
return 0 == compareTo((Matrix) other);
|
||||
}
|
||||
|
||||
public int compareTo(Matrix o) {
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
int diff = data[i] - o.data[i];
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix clone() {
|
||||
Matrix result = new Matrix();
|
||||
result.data = data.clone();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (StandardPluralCategories i : StandardPluralCategories.values()) {
|
||||
for (StandardPluralCategories j : StandardPluralCategories.values()) {
|
||||
StandardPluralCategories x = get(i, j);
|
||||
if (x != null) {
|
||||
result.append(i + " & " + j + " → " + x + ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for building. If the start or end are null, it means everything of that type.
|
||||
*
|
||||
* @param rangeStart
|
||||
* plural category for the start of the range
|
||||
* @param rangeEnd
|
||||
* plural category for the end of the range
|
||||
* @param result
|
||||
* the resulting plural category
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public void add(StandardPluralCategories rangeStart, StandardPluralCategories rangeEnd,
|
||||
StandardPluralCategories result) {
|
||||
if (isFrozen) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
explicit[result.ordinal()] = true;
|
||||
if (rangeStart == null) {
|
||||
for (StandardPluralCategories rs : StandardPluralCategories.values()) {
|
||||
if (rangeEnd == null) {
|
||||
for (StandardPluralCategories re : StandardPluralCategories.values()) {
|
||||
matrix.setIfNew(rs, re, result);
|
||||
}
|
||||
} else {
|
||||
explicit[rangeEnd.ordinal()] = true;
|
||||
matrix.setIfNew(rs, rangeEnd, result);
|
||||
}
|
||||
}
|
||||
} else if (rangeEnd == null) {
|
||||
explicit[rangeStart.ordinal()] = true;
|
||||
for (StandardPluralCategories re : StandardPluralCategories.values()) {
|
||||
matrix.setIfNew(rangeStart, re, result);
|
||||
}
|
||||
} else {
|
||||
explicit[rangeStart.ordinal()] = true;
|
||||
explicit[rangeEnd.ordinal()] = true;
|
||||
matrix.setIfNew(rangeStart, rangeEnd, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate plural category for a range from start to end. If there is no available data, then
|
||||
* 'other' is returned.
|
||||
*
|
||||
* @param start
|
||||
* plural category for the start of the range
|
||||
* @param end
|
||||
* plural category for the end of the range
|
||||
* @return the resulting plural category, or 'end' if there is no data.
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public StandardPluralCategories get(StandardPluralCategories start, StandardPluralCategories end) {
|
||||
StandardPluralCategories result = matrix.get(start, end);
|
||||
return result == null ? end : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate plural category for a range from start to end. If the combination does not explicitly
|
||||
* occur in the data, returns null.
|
||||
*
|
||||
* @param start
|
||||
* plural category for the start of the range
|
||||
* @param end
|
||||
* plural category for the end of the range
|
||||
* @return the resulting plural category
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public boolean isExplicit(StandardPluralCategories start, StandardPluralCategories end) {
|
||||
return matrix.get(start, end) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to determines whether the StandardPluralCategories was explicitly used in any add statement.
|
||||
*
|
||||
* @param count
|
||||
* plural category to test
|
||||
* @return true if set
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public boolean isExplicitlySet(StandardPluralCategories count) {
|
||||
return explicit[count.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof PluralRanges ? matrix.equals((PluralRanges) other) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return matrix.hashCode();
|
||||
}
|
||||
|
||||
public int compareTo(PluralRanges that) {
|
||||
return matrix.compareTo(that.matrix);
|
||||
}
|
||||
|
||||
public boolean isFrozen() {
|
||||
return isFrozen;
|
||||
}
|
||||
|
||||
public PluralRanges freeze() {
|
||||
isFrozen = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluralRanges cloneAsThawed() {
|
||||
PluralRanges result = new PluralRanges();
|
||||
result.explicit = explicit.clone();
|
||||
result.matrix = matrix.clone();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return matrix.toString();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -1819,6 +1820,20 @@ public class PluralRules implements Serializable {
|
||||
*/
|
||||
@Deprecated
|
||||
other;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final List<StandardPluralCategories> VALUES
|
||||
= Collections.unmodifiableList(Arrays.asList(values()));
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int COUNT = values().length;
|
||||
|
||||
static StandardPluralCategories forString(String s) {
|
||||
StandardPluralCategories a;
|
||||
try {
|
||||
@ -2431,4 +2446,41 @@ public class PluralRules implements Serializable {
|
||||
public boolean computeLimited(String keyword, SampleType sampleType) {
|
||||
return rules.computeLimited(keyword, sampleType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plural category for a range, such as 3.1-4.2.
|
||||
* @param locale locale for the range
|
||||
* @param startPluralCategory the plural category of the low end of the range.
|
||||
* @param endPluralCategory the plural category of the high end of the range.
|
||||
* @return the plural category of the range
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
static public StandardPluralCategories getRange(ULocale locale,
|
||||
StandardPluralCategories startPluralCategory,
|
||||
StandardPluralCategories endPluralCategory) {
|
||||
final PluralRanges pluralRanges = Factory.getDefaultFactory().getPluralRanges(locale);
|
||||
return pluralRanges.get(startPluralCategory, endPluralCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plural category for a range, such as 3.1-4.2.
|
||||
* @param locale locale for the range
|
||||
* @param startPluralCategory the plural category of the low end of the range.
|
||||
* @param endPluralCategory the plural category of the high end of the range.
|
||||
* @return the plural category of the range
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
static public String getRange(ULocale locale,
|
||||
String startPluralCategory,
|
||||
String endPluralCategory) {
|
||||
final PluralRanges pluralRanges = Factory.getDefaultFactory().getPluralRanges(locale);
|
||||
return pluralRanges.get(
|
||||
StandardPluralCategories.valueOf(startPluralCategory),
|
||||
StandardPluralCategories.valueOf(endPluralCategory))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2014, 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.MeasureFormat;
|
||||
import com.ibm.icu.text.MeasureFormat.FormatWidth;
|
||||
import com.ibm.icu.text.PluralRanges;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.StandardPluralCategories;
|
||||
import com.ibm.icu.util.Measure;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* @author markdavis
|
||||
*
|
||||
*/
|
||||
public class PluralRangesTest extends TestFmwk {
|
||||
public static void main(String[] args) {
|
||||
new PluralRangesTest().run(args);
|
||||
}
|
||||
|
||||
public void TestLocaleData() {
|
||||
String[][] tests = {
|
||||
{"de", "other", "one", "one"},
|
||||
{"xxx", "few", "few", "few" },
|
||||
{"de", "one", "other", "other"},
|
||||
{"de", "other", "one", "one"},
|
||||
{"de", "other", "other", "other"},
|
||||
{"ro", "one", "few", "few"},
|
||||
{"ro", "one", "other", "other"},
|
||||
{"ro", "few", "one", "few"},
|
||||
};
|
||||
for (String[] test : tests) {
|
||||
final ULocale locale = new ULocale(test[0]);
|
||||
final StandardPluralCategories start = StandardPluralCategories.valueOf(test[1]);
|
||||
final StandardPluralCategories end = StandardPluralCategories.valueOf(test[2]);
|
||||
final StandardPluralCategories expected = StandardPluralCategories.valueOf(test[3]);
|
||||
|
||||
StandardPluralCategories actual = PluralRules.getRange(locale, start, end);
|
||||
assertEquals("Deriving range category", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public void TestFormatting() {
|
||||
Object[][] tests = {
|
||||
{0.0, 1.0, ULocale.FRANCE, FormatWidth.WIDE, MeasureUnit.FAHRENHEIT, "de 0 à 1 degré Fahrenheit"},
|
||||
{1.0, 2.0, ULocale.FRANCE, FormatWidth.WIDE, MeasureUnit.FAHRENHEIT, "de 1 à 2 degrés Fahrenheit"},
|
||||
{3.1, 4.25, ULocale.FRANCE, FormatWidth.SHORT, MeasureUnit.FAHRENHEIT, "3,1–4,25 °F"},
|
||||
{3.1, 4.25, ULocale.ENGLISH, FormatWidth.SHORT, MeasureUnit.FAHRENHEIT, "3.1–4.25°F"},
|
||||
{3.1, 4.25, ULocale.CHINESE, FormatWidth.WIDE, MeasureUnit.INCH, "3.1-4.25英寸"},
|
||||
{0.0, 1.0, new ULocale("xx"), FormatWidth.WIDE, MeasureUnit.INCH, "0–1 inches"},
|
||||
};
|
||||
for (Object[] test : tests) {
|
||||
double low = (Double) test[0];
|
||||
double high = (Double) test[1];
|
||||
final ULocale locale = (ULocale) test[2];
|
||||
final FormatWidth width = (FormatWidth) test[3];
|
||||
final MeasureUnit unit = (MeasureUnit) test[4];
|
||||
final String expected = (String) test[5];
|
||||
|
||||
MeasureFormat mf = MeasureFormat.getInstance(locale, width);
|
||||
String actual = mf.formatMeasureRange(new Measure(low, unit), new Measure(high, unit));
|
||||
assertEquals("Formatting unit", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public void TestBasic() {
|
||||
PluralRanges a = new PluralRanges();
|
||||
a.add(StandardPluralCategories.one, StandardPluralCategories.other, StandardPluralCategories.one);
|
||||
StandardPluralCategories actual = a.get(StandardPluralCategories.one, StandardPluralCategories.other);
|
||||
assertEquals("range", StandardPluralCategories.one, actual);
|
||||
a.freeze();
|
||||
try {
|
||||
a.add(StandardPluralCategories.one, StandardPluralCategories.one, StandardPluralCategories.one);
|
||||
errln("Failed to cause exception on frozen instance");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,7 @@ public class TestAll extends TestGroup {
|
||||
"IntlTestDecimalFormatAPIC",
|
||||
"IntlTestDecimalFormatSymbols",
|
||||
"IntlTestDecimalFormatSymbolsC",
|
||||
"PluralRangesTest",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user