ICU-10268 Add the units along the lines of what was discussed. Needs more work, however.
X-SVN-Rev: 34099
This commit is contained in:
parent
ff5564232d
commit
6037d39183
@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (c) 2004-2012, International Business Machines
|
||||
* Copyright (c) 2004-2013, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
* Author: Alan Liu
|
||||
@ -10,6 +10,7 @@
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
@ -28,7 +29,7 @@ import com.ibm.icu.util.ULocale;
|
||||
* @see com.ibm.icu.text.DecimalFormat
|
||||
* @author Alan Liu
|
||||
*/
|
||||
class CurrencyFormat extends MeasureFormat {
|
||||
class CurrencyFormat extends MeasureFormat implements Serializable {
|
||||
// Generated by serialver from JDK 1.4.1_01
|
||||
static final long serialVersionUID = -931679363692504634L;
|
||||
|
||||
|
@ -0,0 +1,553 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013, Google Inc, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.util.FormatWidth;
|
||||
import com.ibm.icu.util.Measure;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
import com.ibm.icu.util.ULocale.Category;
|
||||
|
||||
/**
|
||||
* Mutable class for formatting GeneralMeasures, or sequences of them.
|
||||
* @author markdavis
|
||||
*/
|
||||
public class GeneralMeasureFormat extends MeasureFormat {
|
||||
|
||||
// Cache the data for units so we don't have to look it up each time.
|
||||
// For each format, we'll store a pointer into the EnumMap for quick access.
|
||||
// TODO use the data to allow parsing.
|
||||
static final transient Map<ULocale,ParseData> localeToParseData = new HashMap<ULocale,ParseData>();
|
||||
static final transient Map<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>> localeToUnitToStyleToCountToFormat
|
||||
= new HashMap<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>>();
|
||||
static final transient Index<MeasureUnit> index = new Index<MeasureUnit>();
|
||||
|
||||
static final class PatternData {
|
||||
final String prefix;
|
||||
final String suffix;
|
||||
public PatternData(String pattern) {
|
||||
int pos = pattern.indexOf("{0}");
|
||||
if (pos < 0) {
|
||||
prefix = pattern;
|
||||
suffix = null;
|
||||
} else {
|
||||
prefix = pattern.substring(0,pos);
|
||||
suffix = pattern.substring(pos+3);
|
||||
}
|
||||
}
|
||||
public String toString() {
|
||||
return prefix + "; " + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
private final ULocale locale;
|
||||
private final FormatWidth length;
|
||||
private final NumberFormat numberFormat;
|
||||
|
||||
private final transient PluralRules rules;
|
||||
private final transient Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat; // invariant once built
|
||||
private transient ParseData parseData; // set as needed
|
||||
|
||||
|
||||
private static final long serialVersionUID = 7922671801770278517L;
|
||||
|
||||
/**
|
||||
* @param styleToCountToFormat2
|
||||
* @param rules2
|
||||
* @param rules2
|
||||
*/
|
||||
protected GeneralMeasureFormat(ULocale locale, FormatWidth style,
|
||||
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat,
|
||||
NumberFormat numberFormat) {
|
||||
this.locale = locale;
|
||||
this.length = style;
|
||||
this.unitToStyleToCountToFormat = unitToStyleToCountToFormat;
|
||||
rules = PluralRules.forLocale(locale);
|
||||
this.numberFormat = numberFormat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a format from the locale and length
|
||||
* @param locale locale of this time unit formatter.
|
||||
* @param length the desired length
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static GeneralMeasureFormat getInstance(ULocale locale, FormatWidth length) {
|
||||
return getInstance(locale, length, NumberFormat.getInstance(locale));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a format from the locale and length
|
||||
* @param locale locale of this time unit formatter.
|
||||
* @param length the desired length
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static GeneralMeasureFormat getInstance(ULocale locale, FormatWidth length,
|
||||
NumberFormat decimalFormat) {
|
||||
synchronized (localeToUnitToStyleToCountToFormat) {
|
||||
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat
|
||||
= localeToUnitToStyleToCountToFormat.get(locale);
|
||||
if (unitToStyleToCountToFormat == null) {
|
||||
unitToStyleToCountToFormat = cacheLocaleData(locale);
|
||||
}
|
||||
// System.out.println(styleToCountToFormat);
|
||||
return new GeneralMeasureFormat(locale, length, unitToStyleToCountToFormat, decimalFormat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatter for CurrencyAmount objects in the given
|
||||
* locale.
|
||||
* @param locale desired locale
|
||||
* @return a formatter object
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
public static MeasureFormat getCurrencyFormat(ULocale locale) {
|
||||
return new CurrencyFormat(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a formatter for CurrencyAmount objects in the default
|
||||
* <code>FORMAT</code> locale.
|
||||
* @return a formatter object
|
||||
* @see Category#FORMAT
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
public static MeasureFormat getCurrencyFormat() {
|
||||
return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the locale of the format.
|
||||
*/
|
||||
public ULocale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the desired length for the format
|
||||
*/
|
||||
public FormatWidth getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
private static Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> cacheLocaleData(ULocale locale) {
|
||||
PluralRules rules = PluralRules.forLocale(locale);
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat;
|
||||
localeToUnitToStyleToCountToFormat.put(locale, unitToStyleToCountToFormat
|
||||
= new HashMap<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>());
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable()) {
|
||||
EnumMap<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
|
||||
if (styleToCountToFormat == null) {
|
||||
unitToStyleToCountToFormat.put(unit, styleToCountToFormat = new EnumMap<FormatWidth, Map<String, PatternData>>(FormatWidth.class));
|
||||
}
|
||||
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
|
||||
for (FormatWidth styleItem : FormatWidth.values()) {
|
||||
try {
|
||||
ICUResourceBundle unitTypeRes = resource.getWithFallback(styleItem.resourceKey);
|
||||
ICUResourceBundle unitsRes = unitTypeRes.getWithFallback(unit.getType());
|
||||
ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(unit.getCode());
|
||||
Map<String, PatternData> countToFormat = styleToCountToFormat.get(styleItem);
|
||||
if (countToFormat == null) {
|
||||
styleToCountToFormat.put(styleItem, countToFormat = new HashMap<String, PatternData>());
|
||||
}
|
||||
for (String keyword : keywords) {
|
||||
UResourceBundle countBundle;
|
||||
try {
|
||||
countBundle = oneUnitRes.get(keyword);
|
||||
} catch (MissingResourceException e) {
|
||||
continue;
|
||||
}
|
||||
String pattern = countBundle.getString();
|
||||
// System.out.println(styleItem.resourceKey + "/"
|
||||
// + unit.getType() + "/"
|
||||
// + unit.getCode() + "/"
|
||||
// + keyword + "=" + pattern);
|
||||
PatternData format = new PatternData(pattern);
|
||||
countToFormat.put(keyword, format);
|
||||
// System.out.println(styleToCountToFormat);
|
||||
}
|
||||
// fill in 'other' for any missing values
|
||||
PatternData other = countToFormat.get("other");
|
||||
for (String keyword : keywords) {
|
||||
if (!countToFormat.containsKey(keyword)) {
|
||||
countToFormat.put(keyword, other);
|
||||
}
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// now fill in the holes
|
||||
fillin:
|
||||
if (styleToCountToFormat.size() != FormatWidth.values().length) {
|
||||
Map<String, PatternData> fallback = styleToCountToFormat.get(FormatWidth.SHORT);
|
||||
if (fallback == null) {
|
||||
fallback = styleToCountToFormat.get(FormatWidth.WIDE);
|
||||
}
|
||||
if (fallback == null) {
|
||||
break fillin; // TODO use root
|
||||
}
|
||||
for (FormatWidth styleItem : FormatWidth.values()) {
|
||||
Map<String, PatternData> countToFormat = styleToCountToFormat.get(styleItem);
|
||||
if (countToFormat == null) {
|
||||
styleToCountToFormat.put(styleItem, countToFormat = new HashMap<String, PatternData>());
|
||||
for (Entry<String, PatternData> entry : fallback.entrySet()) {
|
||||
countToFormat.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return unitToStyleToCountToFormat;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public StringBuffer format(Object arg0, StringBuffer arg1, FieldPosition arg2) {
|
||||
if (arg0 instanceof Collection) {
|
||||
Collection<Measure> coll = (Collection<Measure>) arg0;
|
||||
return format(arg1, arg2, coll.toArray(new Measure[coll.size()]));
|
||||
} else if (arg0 instanceof Measure[]) {
|
||||
return format(arg1, arg2, (Measure[]) arg0);
|
||||
} else {
|
||||
return format((Measure) arg0, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a general measure (type-safe).
|
||||
* @param measure the measure to format
|
||||
* @param stringBuffer as in {@link #format(Object, StringBuffer, FieldPosition)}
|
||||
* @param fieldPosition as in {@link #format(Object, StringBuffer, FieldPosition)}
|
||||
* @return passed-in buffer with appended text.
|
||||
*/
|
||||
public StringBuffer format(Measure measure, StringBuffer stringBuffer, FieldPosition fieldPosition) {
|
||||
Number n = measure.getNumber();
|
||||
MeasureUnit unit = measure.getUnit();
|
||||
UFieldPosition pos = new UFieldPosition(fieldPosition.getFieldAttribute(), fieldPosition.getField());
|
||||
StringBuffer formattedNumber = numberFormat.format(n, new StringBuffer(), pos);
|
||||
String keyword = rules.select(new PluralRules.FixedDecimal(n.doubleValue(), pos.getCountVisibleFractionDigits(), pos.getFractionDigits()));
|
||||
|
||||
Map<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
|
||||
Map<String, PatternData> countToFormat = styleToCountToFormat.get(length);
|
||||
PatternData messagePatternData = countToFormat.get(keyword);
|
||||
|
||||
stringBuffer.append(messagePatternData.prefix);
|
||||
if (messagePatternData.suffix != null) { // there is a number (may not happen with, say, Arabic dual)
|
||||
// Fix field position
|
||||
fieldPosition.setBeginIndex(pos.getBeginIndex() + messagePatternData.prefix.length());
|
||||
fieldPosition.setEndIndex(pos.getEndIndex() + messagePatternData.prefix.length());
|
||||
stringBuffer.append(formattedNumber);
|
||||
stringBuffer.append(messagePatternData.suffix);
|
||||
}
|
||||
return stringBuffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format a sequence of measures.
|
||||
* @param stringBuffer as in {@link #format(Object, StringBuffer, FieldPosition)}
|
||||
* @param fieldPosition as in {@link #format(Object, StringBuffer, FieldPosition)}
|
||||
* @param measures a sequence of one or more measures.
|
||||
* @return passed-in buffer with appended text.
|
||||
*/
|
||||
public StringBuffer format(StringBuffer stringBuffer, FieldPosition fieldPosition, Measure... measures) {
|
||||
StringBuffer[] results = new StringBuffer[measures.length];
|
||||
for (int i = 0; i < measures.length; ++i) {
|
||||
results[i] = format(measures[i], new StringBuffer(), fieldPosition);
|
||||
}
|
||||
ListFormatter listFormatter = ListFormatter.getInstance(locale,
|
||||
length == FormatWidth.WIDE ? ListFormatter.Style.DURATION : ListFormatter.Style.DURATION_SHORT);
|
||||
return stringBuffer.append(listFormatter.format(results));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a sequence of measures.
|
||||
* @param measures a sequence of one or more measures.
|
||||
* @return passed-in buffer with appended text.
|
||||
*/
|
||||
public String format(Measure... measures) {
|
||||
StringBuffer result = format(new StringBuffer(), new FieldPosition(0), measures);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
static final class ParseData {
|
||||
transient Map<String,BitSet> prefixMap;
|
||||
transient Map<String,BitSet> suffixMap;
|
||||
transient BitSet nullSuffix;
|
||||
|
||||
ParseData(ULocale locale, Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat) {
|
||||
prefixMap = new TreeMap<String,BitSet>(LONGEST_FIRST);
|
||||
suffixMap = new TreeMap<String,BitSet>(LONGEST_FIRST);
|
||||
nullSuffix = new BitSet();
|
||||
for (Entry<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> entry3 : unitToStyleToCountToFormat.entrySet()) {
|
||||
MeasureUnit unit = entry3.getKey();
|
||||
int unitIndex = index.addItem(unit);
|
||||
for (Entry<FormatWidth, Map<String, PatternData>> entry : entry3.getValue().entrySet()) {
|
||||
//Style style = entry.getKey();
|
||||
for (Entry<String, PatternData> entry2 : entry.getValue().entrySet()) {
|
||||
//String keyword = entry2.getKey();
|
||||
PatternData data = entry2.getValue();
|
||||
setBits(prefixMap, data.prefix, unitIndex);
|
||||
if (data.suffix == null) {
|
||||
nullSuffix.set(unitIndex);
|
||||
} else {
|
||||
setBits(suffixMap, data.suffix, unitIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void setBits(Map<String, BitSet> map, String string, int unitIndex) {
|
||||
BitSet bs = map.get(string);
|
||||
if (bs == null) {
|
||||
map.put(string, bs = new BitSet());
|
||||
}
|
||||
bs.set(unitIndex);
|
||||
}
|
||||
public static ParseData of(ULocale locale,
|
||||
Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat) {
|
||||
ParseData result = localeToParseData.get(locale);
|
||||
if (result == null) {
|
||||
localeToParseData.put(locale, result = new ParseData(locale, unitToStyleToCountToFormat));
|
||||
// System.out.println("Prefix:\t" + result.prefixMap.size());
|
||||
// System.out.println("Suffix:\t" + result.suffixMap.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Measure parse(NumberFormat numberFormat, String toParse, ParsePosition parsePosition) {
|
||||
// TODO optimize this as necessary
|
||||
// In particular, if we've already matched a suffix and number, store that.
|
||||
// If the same suffix turns up we can jump
|
||||
int startIndex = parsePosition.getIndex();
|
||||
Number bestNumber = null;
|
||||
int bestUnit = -1;
|
||||
int longestMatch = -1;
|
||||
int furthestError = -1;
|
||||
for (Entry<String, BitSet> prefixEntry : prefixMap.entrySet()) {
|
||||
String prefix = prefixEntry.getKey();
|
||||
BitSet prefixSet = prefixEntry.getValue();
|
||||
for (Entry<String, BitSet> suffixEntry : suffixMap.entrySet()) {
|
||||
String suffix = suffixEntry.getKey();
|
||||
BitSet suffixSet = suffixEntry.getValue();
|
||||
parsePosition.setIndex(startIndex);
|
||||
if (looseMatches(prefix, toParse, parsePosition)) {
|
||||
// if (nullSuffix.intersects(prefixSet))
|
||||
//// // can only happen with singular rule
|
||||
//// if (longestMatch < parsePosition.getIndex()) {
|
||||
//// longestMatch = parsePosition.getIndex();
|
||||
//// Collection<Double> samples = rules.getSamples(keyword);
|
||||
//// bestNumber = samples.iterator().next();
|
||||
//// bestUnit = unit;
|
||||
//// }
|
||||
// }
|
||||
Number number = numberFormat.parse(toParse, parsePosition);
|
||||
if (parsePosition.getErrorIndex() >= 0) {
|
||||
if (furthestError < parsePosition.getErrorIndex()) {
|
||||
furthestError = parsePosition.getErrorIndex();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (looseMatches(suffix, toParse, parsePosition) && prefixSet.intersects(suffixSet)) {
|
||||
if (longestMatch < parsePosition.getIndex()) {
|
||||
longestMatch = parsePosition.getIndex();
|
||||
bestNumber = number;
|
||||
bestUnit = getFirst(prefixSet, suffixSet);
|
||||
}
|
||||
} else if (furthestError < parsePosition.getErrorIndex()) {
|
||||
furthestError = parsePosition.getErrorIndex();
|
||||
}
|
||||
} else if (furthestError < parsePosition.getErrorIndex()) {
|
||||
furthestError = parsePosition.getErrorIndex();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (longestMatch >= 0) {
|
||||
parsePosition.setIndex(longestMatch);
|
||||
return new Measure(bestNumber, index.getUnit(bestUnit));
|
||||
}
|
||||
parsePosition.setErrorIndex(furthestError);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Index<T> {
|
||||
List<T> intToItem = new ArrayList<T>();
|
||||
Map<T,Integer> itemToInt = new HashMap<T,Integer>();
|
||||
|
||||
int getIndex(T item) {
|
||||
return itemToInt.get(item);
|
||||
}
|
||||
T getUnit(int index) {
|
||||
return intToItem.get(index);
|
||||
}
|
||||
int addItem(T item) {
|
||||
Integer index = itemToInt.get(item);
|
||||
if (index != null) {
|
||||
return index;
|
||||
}
|
||||
int size = intToItem.size();
|
||||
itemToInt.put(item, size);
|
||||
intToItem.add(item);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Measure parseObject(String toParse, ParsePosition parsePosition) {
|
||||
if (parseData == null) {
|
||||
parseData = ParseData.of(locale, unitToStyleToCountToFormat);
|
||||
}
|
||||
// int index = parsePosition.getIndex();
|
||||
// int errorIndex = parsePosition.getIndex();
|
||||
Measure result = parseData.parse(numberFormat, toParse, parsePosition);
|
||||
// if (result == null) {
|
||||
// parsePosition.setIndex(index);
|
||||
// parsePosition.setErrorIndex(errorIndex);
|
||||
// result = compatCurrencyFormat.parseCurrency(toParse, parsePosition);
|
||||
// }
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param prefixSet
|
||||
* @param suffixSet
|
||||
* @return
|
||||
*/
|
||||
private static int getFirst(BitSet prefixSet, BitSet suffixSet) {
|
||||
for (int i = prefixSet.nextSetBit(0); i >= 0; i = prefixSet.nextSetBit(i+1)) {
|
||||
if (suffixSet.get(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param suffix
|
||||
* @param arg0
|
||||
* @param arg1
|
||||
* @return
|
||||
*/
|
||||
// TODO make this lenient
|
||||
private static boolean looseMatches(String suffix, String arg0, ParsePosition arg1) {
|
||||
boolean matches = suffix.regionMatches(0, arg0, arg1.getIndex(), suffix.length());
|
||||
if (matches) {
|
||||
arg1.setErrorIndex(-1);
|
||||
arg1.setIndex(arg1.getIndex() + suffix.length());
|
||||
} else {
|
||||
arg1.setErrorIndex(arg1.getIndex());
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
static final Comparator<String> LONGEST_FIRST = new Comparator<String>() {
|
||||
public int compare(String as, String bs) {
|
||||
if (as.length() > bs.length()) {
|
||||
return -1;
|
||||
}
|
||||
if (as.length() < bs.length()) {
|
||||
return 1;
|
||||
}
|
||||
return as.compareTo(bs);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != GeneralMeasureFormat.class) {
|
||||
return false;
|
||||
}
|
||||
GeneralMeasureFormat other = (GeneralMeasureFormat) obj;
|
||||
return locale.equals(other.locale)
|
||||
&& length == other.length
|
||||
&& numberFormat.equals(other.numberFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// TODO Auto-generated method stub
|
||||
return (locale.hashCode() * 37 + length.hashCode()) * 37 + numberFormat.hashCode();
|
||||
}
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new GeneralMeasureProxy(locale, length, numberFormat);
|
||||
}
|
||||
|
||||
static class GeneralMeasureProxy implements Externalizable {
|
||||
private ULocale locale;
|
||||
private FormatWidth length;
|
||||
private NumberFormat numberFormat;
|
||||
|
||||
public GeneralMeasureProxy(ULocale locale, FormatWidth length, NumberFormat numberFormat) {
|
||||
this.locale = locale;
|
||||
this.length = length;
|
||||
this.numberFormat = numberFormat;
|
||||
}
|
||||
|
||||
// Must have public constructor, to enable Externalizable
|
||||
public GeneralMeasureProxy() {
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0); // version
|
||||
out.writeObject(locale);
|
||||
out.writeObject(length);
|
||||
out.writeObject(numberFormat);
|
||||
out.writeShort(0); // allow for more data.
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
byte version = in.readByte(); // version
|
||||
locale = (ULocale) in.readObject();
|
||||
length = (FormatWidth) in.readObject();
|
||||
numberFormat = (NumberFormat) in.readObject();
|
||||
// allow for more data from future version
|
||||
int extra = in.readShort();
|
||||
if (extra > 0) {
|
||||
byte[] extraBytes = new byte[extra];
|
||||
in.read(extraBytes, 0, extra);
|
||||
}
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return GeneralMeasureFormat.getInstance(locale, length, numberFormat);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.text.ParsePosition;
|
||||
@ -30,6 +32,7 @@ import com.ibm.icu.text.CurrencyDisplayNames;
|
||||
import com.ibm.icu.text.CurrencyMetaInfo;
|
||||
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyDigits;
|
||||
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyFilter;
|
||||
import com.ibm.icu.util.MeasureUnit.MeasureUnitProxy;
|
||||
import com.ibm.icu.util.ULocale.Category;
|
||||
|
||||
/**
|
||||
@ -53,7 +56,6 @@ import com.ibm.icu.util.ULocale.Category;
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
public class Currency extends MeasureUnit implements Serializable {
|
||||
// using serialver from jdk1.4.2_05
|
||||
private static final long serialVersionUID = -5839973855554750484L;
|
||||
private static final boolean DEBUG = ICUDebug.enabled("currency");
|
||||
|
||||
@ -61,11 +63,6 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
private static ICUCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>> CURRENCY_NAME_CACHE =
|
||||
new SimpleCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>>();
|
||||
|
||||
/**
|
||||
* ISO 4217 3-letter code.
|
||||
*/
|
||||
private String isoCode;
|
||||
|
||||
/**
|
||||
* Selector for getName() indicating a symbolic name for a
|
||||
* currency, such as "$" for USD.
|
||||
@ -192,7 +189,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
List<String> list = info.currencies(CurrencyFilter.all());
|
||||
HashSet<Currency> resultSet = new HashSet<Currency>(list.size());
|
||||
for (String code : list) {
|
||||
resultSet.add(new Currency(code));
|
||||
resultSet.add(getInstance(code));
|
||||
}
|
||||
return resultSet;
|
||||
}
|
||||
@ -207,7 +204,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
|
||||
String variant = loc.getVariant();
|
||||
if ("EURO".equals(variant)) {
|
||||
return new Currency(EUR_STR);
|
||||
return getInstance(EUR_STR);
|
||||
}
|
||||
|
||||
String code = currencyCodeCache.get(loc);
|
||||
@ -230,7 +227,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
}
|
||||
currencyCodeCache.put(loc, code);
|
||||
}
|
||||
return new Currency(code);
|
||||
return getInstance(code);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,9 +247,10 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
throw new IllegalArgumentException(
|
||||
"The input currency code is not 3-letter alphabetic code.");
|
||||
}
|
||||
return new Currency(theISOCode.toUpperCase(Locale.ENGLISH));
|
||||
return (Currency) MeasureUnit.addUnit("currency", theISOCode.toUpperCase(Locale.ENGLISH), CURRENCY_FACTORY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static boolean isAlpha3Code(String code) {
|
||||
if (code.length() != 3) {
|
||||
return false;
|
||||
@ -396,37 +394,12 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
private static final ULocale UND = new ULocale("und");
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
/**
|
||||
* Return a hashcode for this currency.
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
public int hashCode() {
|
||||
return isoCode.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if rhs is a Currency instance,
|
||||
* is non-null, and has the same currency code.
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
public boolean equals(Object rhs) {
|
||||
if (rhs == null) return false;
|
||||
if (rhs == this) return true;
|
||||
try {
|
||||
Currency c = (Currency) rhs;
|
||||
return isoCode.equals(c.isoCode);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ISO 4217 3-letter code for this currency object.
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
public String getCurrencyCode() {
|
||||
return isoCode;
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -437,19 +410,19 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
* @stable ICU 49
|
||||
*/
|
||||
public int getNumericCode() {
|
||||
int code = 0;
|
||||
int result = 0;
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(
|
||||
ICUResourceBundle.ICU_BASE_NAME,
|
||||
"currencyNumericCodes",
|
||||
ICUResourceBundle.ICU_DATA_CLASS_LOADER);
|
||||
UResourceBundle codeMap = bundle.get("codeMap");
|
||||
UResourceBundle numCode = codeMap.get(isoCode);
|
||||
code = numCode.getInt();
|
||||
UResourceBundle numCode = codeMap.get(code);
|
||||
result = numCode.getInt();
|
||||
} catch (MissingResourceException e) {
|
||||
// fall through
|
||||
}
|
||||
return code;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -533,7 +506,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
}
|
||||
|
||||
CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
|
||||
return nameStyle == SYMBOL_NAME ? names.getSymbol(isoCode) : names.getName(isoCode);
|
||||
return nameStyle == SYMBOL_NAME ? names.getSymbol(code) : names.getName(code);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -582,7 +555,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
}
|
||||
|
||||
CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
|
||||
return names.getPluralName(isoCode, pluralCount);
|
||||
return names.getPluralName(code, pluralCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -759,7 +732,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
*/
|
||||
public int getDefaultFractionDigits() {
|
||||
CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
|
||||
CurrencyDigits digits = info.currencyDigits(isoCode);
|
||||
CurrencyDigits digits = info.currencyDigits(code);
|
||||
return digits.fractionDigits;
|
||||
}
|
||||
|
||||
@ -771,7 +744,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
*/
|
||||
public double getRoundingIncrement() {
|
||||
CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
|
||||
CurrencyDigits digits = info.currencyDigits(isoCode);
|
||||
CurrencyDigits digits = info.currencyDigits(code);
|
||||
|
||||
int data1 = digits.roundingIncrement;
|
||||
|
||||
@ -798,7 +771,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
public String toString() {
|
||||
return isoCode;
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -809,7 +782,7 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
* @stable ICU 3.4
|
||||
*/
|
||||
protected Currency(String theISOCode) {
|
||||
isoCode = theISOCode;
|
||||
super("currency", theISOCode);
|
||||
}
|
||||
|
||||
// POW10[i] = 10^i
|
||||
@ -927,5 +900,9 @@ public class Currency extends MeasureUnit implements Serializable {
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
}
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new MeasureUnitProxy(type, code);
|
||||
}
|
||||
}
|
||||
//eof
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
/**
|
||||
* General purpose formatting width enum.
|
||||
*/
|
||||
public enum FormatWidth {
|
||||
WIDE("units"),
|
||||
SHORT("unitsShort"),
|
||||
NARROW("unitsNarrow");
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated internal use
|
||||
*/
|
||||
public final String resourceKey;
|
||||
|
||||
private FormatWidth(String resourceKey) {
|
||||
this.resourceKey = resourceKey;
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ package com.ibm.icu.util;
|
||||
* <p>Measure objects are parsed and formatted by subclasses of
|
||||
* MeasureFormat.
|
||||
*
|
||||
* <p>Measure objects are immutable.
|
||||
* <p>Measure objects are immutable. All subclasses must guarantee that.
|
||||
*
|
||||
* @see java.lang.Number
|
||||
* @see com.ibm.icu.util.MeasureUnit
|
||||
@ -28,7 +28,7 @@ package com.ibm.icu.util;
|
||||
* @author Alan Liu
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
public abstract class Measure {
|
||||
public class Measure {
|
||||
|
||||
private final Number number;
|
||||
|
||||
@ -40,7 +40,7 @@ public abstract class Measure {
|
||||
* @param unit the unit
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
protected Measure(Number number, MeasureUnit unit) {
|
||||
public Measure(Number number, MeasureUnit unit) {
|
||||
if (number == null || unit == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
@ -1,15 +1,29 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (c) 2004-2007, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
* Author: Alan Liu
|
||||
* Created: April 20, 2004
|
||||
* Since: ICU 3.0
|
||||
**********************************************************************
|
||||
*/
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2004-2013, Google Inc, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.text.UnicodeSet;
|
||||
|
||||
/**
|
||||
* A unit such as length, mass, volume, currency, etc. A unit is
|
||||
* coupled with a numeric amount to produce a Measure.
|
||||
@ -18,10 +32,367 @@ package com.ibm.icu.util;
|
||||
* @author Alan Liu
|
||||
* @stable ICU 3.0
|
||||
*/
|
||||
public abstract class MeasureUnit {
|
||||
public class MeasureUnit implements Comparable<MeasureUnit>, Serializable {
|
||||
private static final long serialVersionUID = -1839973855554750484L;
|
||||
|
||||
private static final Map<String, Map<String,MeasureUnit>> cache
|
||||
= new HashMap<String, Map<String,MeasureUnit>>();
|
||||
|
||||
protected final String type;
|
||||
protected final String code;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
* @param code
|
||||
*/
|
||||
protected MeasureUnit() {}
|
||||
protected MeasureUnit(String type, String code) {
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a measurement unit.
|
||||
* @param type the type, such as "length"
|
||||
* @param code the code, such as "meter"
|
||||
* @return the unit.
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static MeasureUnit getInstance(String type, String code) {
|
||||
Map<String, MeasureUnit> tmp = cache.get(type);
|
||||
if (tmp != null) {
|
||||
MeasureUnit result = tmp.get(code);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (type == null || !ASCII.containsAll(type) || code == null || ASCII_HYPHEN.containsAll(code)) {
|
||||
throw new NullPointerException("The type or code are invalid.");
|
||||
}
|
||||
synchronized (MeasureUnit.class) {
|
||||
return (Currency) MeasureUnit.addUnit(type, code, UNIT_FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
static final UnicodeSet ASCII = new UnicodeSet('a', 'z').freeze();
|
||||
static final UnicodeSet ASCII_HYPHEN = new UnicodeSet('-', '-', 'a', 'z').freeze();
|
||||
|
||||
protected interface Factory {
|
||||
MeasureUnit create(String type, String code);
|
||||
}
|
||||
|
||||
private static Factory UNIT_FACTORY = new Factory() {
|
||||
public MeasureUnit create(String type, String code) {
|
||||
return new MeasureUnit(type, code);
|
||||
}
|
||||
};
|
||||
|
||||
static Factory CURRENCY_FACTORY = new Factory() {
|
||||
public MeasureUnit create(String type, String code) {
|
||||
return new Currency(code);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// /**
|
||||
// * Register a unit for later use
|
||||
// * @param type the type, such as "length"
|
||||
// * @param code the code, such as "meter"
|
||||
// * @return the unit.
|
||||
// * @draft ICU 52
|
||||
// * @provisional This API might change or be removed in a future release.
|
||||
// */
|
||||
// public static synchronized MeasureUnit registerUnit(String type, String code) {
|
||||
// MeasureUnit result = getInstance(type, code);
|
||||
// if (result == null) {
|
||||
// result = addUnit(type, code, MY_FACTORY);
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
static {
|
||||
// load all of the units for English, since we know that that is a superset.
|
||||
/**
|
||||
* units{
|
||||
* duration{
|
||||
* day{
|
||||
* one{"{0} ден"}
|
||||
* other{"{0} дена"}
|
||||
* }
|
||||
*/
|
||||
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "en");
|
||||
for (FormatWidth key : FormatWidth.values()) {
|
||||
try {
|
||||
ICUResourceBundle unitsTypeRes = resource.getWithFallback(key.resourceKey);
|
||||
int size = unitsTypeRes.getSize();
|
||||
for ( int index = 0; index < size; ++index) {
|
||||
UResourceBundle unitsRes = unitsTypeRes.get(index);
|
||||
String type = unitsRes.getKey();
|
||||
int unitsSize = unitsRes.getSize();
|
||||
for ( int index2 = 0; index2 < unitsSize; ++index2) {
|
||||
String unitName = unitsRes.get(index2).getKey();
|
||||
addUnit(type, unitName, UNIT_FACTORY);
|
||||
}
|
||||
}
|
||||
} catch ( MissingResourceException e ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// preallocate currencies
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(
|
||||
ICUResourceBundle.ICU_BASE_NAME,
|
||||
"currencyNumericCodes",
|
||||
ICUResourceBundle.ICU_DATA_CLASS_LOADER);
|
||||
UResourceBundle codeMap = bundle.get("codeMap");
|
||||
for (Enumeration<String> it = codeMap.getKeys(); it.hasMoreElements();) {
|
||||
MeasureUnit.addUnit("currency", it.nextElement(), Currency.CURRENCY_FACTORY);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
// Must only be called at static initialization, or inside synchronized block.
|
||||
protected static MeasureUnit addUnit(String type, String unitName, Factory factory) {
|
||||
Map<String, MeasureUnit> tmp = cache.get(type);
|
||||
if (tmp == null) {
|
||||
cache.put(type, tmp = new HashMap<String, MeasureUnit>());
|
||||
} else {
|
||||
// "intern" the type by setting to first item's type.
|
||||
type = tmp.entrySet().iterator().next().getValue().type;
|
||||
}
|
||||
MeasureUnit unit = tmp.get(unitName);
|
||||
if (unit == null) {
|
||||
tmp.put(unitName, unit = factory.create(type, unitName));
|
||||
}
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the available general units' types.
|
||||
* @return available units
|
||||
*/
|
||||
public static Set<String> getAvailableTypes() {
|
||||
return Collections.unmodifiableSet(cache.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the available general units for a given type.
|
||||
* @return available units
|
||||
*/
|
||||
public static Collection<MeasureUnit> getAvailable(String type) {
|
||||
Map<String, MeasureUnit> units = cache.get(type);
|
||||
return units == null ? null : Collections.unmodifiableCollection(units.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the available general units.
|
||||
* @return available units
|
||||
*/
|
||||
public static Set<MeasureUnit> getAvailable() {
|
||||
Set<MeasureUnit> result = new TreeSet<MeasureUnit>();
|
||||
for (String type : new TreeSet<String>(MeasureUnit.getAvailableTypes())) {
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
|
||||
result.add(unit);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hashcode for this currency.
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return code.hashCode() ^ type.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if rhs is a Currency instance,
|
||||
* is non-null, and has the same currency code.
|
||||
* @stable ICU 2.2
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object rhs) {
|
||||
if (rhs == null) return false;
|
||||
if (rhs == this) return true;
|
||||
try {
|
||||
MeasureUnit c = (MeasureUnit) rhs;
|
||||
return type.equals(c.type) && code.equals(c.code);
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*/
|
||||
public int compareTo(MeasureUnit other) {
|
||||
int diff;
|
||||
return this == other ? 0
|
||||
: (diff = type.compareTo(other.type)) != 0 ? diff
|
||||
: code.compareTo(other.code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + "-" + code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the type for this unit
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the code for this unit.
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful constants. Not necessarily complete: see {@link #getAvailable()}.
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final MeasureUnit
|
||||
/** Constant for unit of acceleration: g-force */
|
||||
G_FORCE = MeasureUnit.getInstance("acceleration", "g-force"),
|
||||
/** Constant for unit of angle: degree */
|
||||
DEGREE = MeasureUnit.getInstance("angle", "degree"),
|
||||
/** Constant for unit of angle: minute */
|
||||
ARC_MINUTE = MeasureUnit.getInstance("angle", "minute"),
|
||||
/** Constant for unit of angle: second */
|
||||
ARC_SECOND = MeasureUnit.getInstance("angle", "second"),
|
||||
/** Constant for unit of area: acre */
|
||||
ACRE = MeasureUnit.getInstance("area", "acre"),
|
||||
/** Constant for unit of area: hectare */
|
||||
HECTARE = MeasureUnit.getInstance("area", "hectare"),
|
||||
/** Constant for unit of area: square-foot */
|
||||
SQUARE_FOOT = MeasureUnit.getInstance("area", "square-foot"),
|
||||
/** Constant for unit of area: square-kilometer */
|
||||
SQUARE_KILOMETER = MeasureUnit.getInstance("area", "square-kilometer"),
|
||||
/** Constant for unit of area: square-meter */
|
||||
SQUARE_METER = MeasureUnit.getInstance("area", "square-meter"),
|
||||
/** Constant for unit of area: square-mile */
|
||||
SQUARE_MILE = MeasureUnit.getInstance("area", "square-mile"),
|
||||
/** Constant for unit of duration: day */
|
||||
DAY = MeasureUnit.getInstance("duration", "day"),
|
||||
/** Constant for unit of duration: hour */
|
||||
HOUR = MeasureUnit.getInstance("duration", "hour"),
|
||||
/** Constant for unit of duration: millisecond */
|
||||
MILLISECOND = MeasureUnit.getInstance("duration", "millisecond"),
|
||||
/** Constant for unit of duration: minute */
|
||||
MINUTE = MeasureUnit.getInstance("duration", "minute"),
|
||||
/** Constant for unit of duration: month */
|
||||
MONTH = MeasureUnit.getInstance("duration", "month"),
|
||||
/** Constant for unit of duration: second */
|
||||
SECOND = MeasureUnit.getInstance("duration", "second"),
|
||||
/** Constant for unit of duration: week */
|
||||
WEEK = MeasureUnit.getInstance("duration", "week"),
|
||||
/** Constant for unit of duration: year */
|
||||
YEAR = MeasureUnit.getInstance("duration", "year"),
|
||||
/** Constant for unit of length: centimeter */
|
||||
CENTIMETER = MeasureUnit.getInstance("length", "centimeter"),
|
||||
/** Constant for unit of length: foot */
|
||||
FOOT = MeasureUnit.getInstance("length", "foot"),
|
||||
/** Constant for unit of length: inch */
|
||||
INCH = MeasureUnit.getInstance("length", "inch"),
|
||||
/** Constant for unit of length: kilometer */
|
||||
KILOMETER = MeasureUnit.getInstance("length", "kilometer"),
|
||||
/** Constant for unit of length: light-year */
|
||||
LIGHT_YEAR = MeasureUnit.getInstance("length", "light-year"),
|
||||
/** Constant for unit of length: meter */
|
||||
METER = MeasureUnit.getInstance("length", "meter"),
|
||||
/** Constant for unit of length: mile */
|
||||
MILE = MeasureUnit.getInstance("length", "mile"),
|
||||
/** Constant for unit of length: millimeter */
|
||||
MILLIMETER = MeasureUnit.getInstance("length", "millimeter"),
|
||||
/** Constant for unit of length: picometer */
|
||||
PICOMETER = MeasureUnit.getInstance("length", "picometer"),
|
||||
/** Constant for unit of length: yard */
|
||||
YARD = MeasureUnit.getInstance("length", "yard"),
|
||||
/** Constant for unit of mass: gram */
|
||||
GRAM = MeasureUnit.getInstance("mass", "gram"),
|
||||
/** Constant for unit of mass: kilogram */
|
||||
KILOGRAM = MeasureUnit.getInstance("mass", "kilogram"),
|
||||
/** Constant for unit of mass: ounce */
|
||||
OUNCE = MeasureUnit.getInstance("mass", "ounce"),
|
||||
/** Constant for unit of mass: pound */
|
||||
POUND = MeasureUnit.getInstance("mass", "pound"),
|
||||
/** Constant for unit of power: horsepower */
|
||||
HORSEPOWER = MeasureUnit.getInstance("power", "horsepower"),
|
||||
/** Constant for unit of power: kilowatt */
|
||||
KILOWATT = MeasureUnit.getInstance("power", "kilowatt"),
|
||||
/** Constant for unit of power: watt */
|
||||
WATT = MeasureUnit.getInstance("power", "watt"),
|
||||
/** Constant for unit of pressure: hectopascal */
|
||||
HECTOPASCAL = MeasureUnit.getInstance("pressure", "hectopascal"),
|
||||
/** Constant for unit of pressure: inch-hg */
|
||||
INCH_HG = MeasureUnit.getInstance("pressure", "inch-hg"),
|
||||
/** Constant for unit of pressure: millibar */
|
||||
MILLIBAR = MeasureUnit.getInstance("pressure", "millibar"),
|
||||
/** Constant for unit of speed: kilometer-per-hour */
|
||||
KILOMETER_PER_HOUR = MeasureUnit.getInstance("speed", "kilometer-per-hour"),
|
||||
/** Constant for unit of speed: meter-per-second */
|
||||
METER_PER_SECOND = MeasureUnit.getInstance("speed", "meter-per-second"),
|
||||
/** Constant for unit of speed: mile-per-hour */
|
||||
MILE_PER_HOUR = MeasureUnit.getInstance("speed", "mile-per-hour"),
|
||||
/** Constant for unit of temperature: celsius */
|
||||
CELSIUS = MeasureUnit.getInstance("temperature", "celsius"),
|
||||
/** Constant for unit of temperature: fahrenheit */
|
||||
FAHRENHEIT = MeasureUnit.getInstance("temperature", "fahrenheit"),
|
||||
/** Constant for unit of volume: cubic-kilometer */
|
||||
CUBIC_KILOMETER = MeasureUnit.getInstance("volume", "cubic-kilometer"),
|
||||
/** Constant for unit of volume: cubic-mile */
|
||||
CUBIC_MILE = MeasureUnit.getInstance("volume", "cubic-mile"),
|
||||
/** Constant for unit of volume: liter */
|
||||
LITER = MeasureUnit.getInstance("volume", "liter");
|
||||
|
||||
/** Private **/
|
||||
|
||||
private Object writeReplace() throws ObjectStreamException {
|
||||
return new MeasureUnitProxy(type, code);
|
||||
}
|
||||
|
||||
static final class MeasureUnitProxy implements Externalizable {
|
||||
private String type;
|
||||
private String code;
|
||||
|
||||
public MeasureUnitProxy(String type, String code) {
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
// Must have public constructor, to enable Externalizable
|
||||
public MeasureUnitProxy() {
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
out.writeByte(0); // version
|
||||
out.writeUTF(type);
|
||||
out.writeUTF(code);
|
||||
out.writeShort(0); // allow for more data.
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
byte version = in.readByte(); // version
|
||||
type = in.readUTF();
|
||||
code = in.readUTF();
|
||||
// allow for more data from future version
|
||||
int extra = in.readShort();
|
||||
if (extra > 0) {
|
||||
byte[] extraBytes = new byte[extra];
|
||||
in.read(extraBytes, 0, extra);
|
||||
}
|
||||
}
|
||||
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return "currency".equals(type)
|
||||
? Currency.getInstance(code)
|
||||
: MeasureUnit.getInstance(type, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
|
||||
/**
|
||||
* Measurement unit for time units.
|
||||
* @see TimeUnitAmount
|
||||
@ -14,10 +15,11 @@ package com.ibm.icu.util;
|
||||
* @stable ICU 4.0
|
||||
*/
|
||||
public class TimeUnit extends MeasureUnit {
|
||||
private static final long serialVersionUID = -2839973855554750484L;
|
||||
|
||||
/**
|
||||
* Supports selected time duration units
|
||||
*/
|
||||
private final String name;
|
||||
private final int index;
|
||||
|
||||
// Total number of time units. Adjust as necessary.
|
||||
@ -42,7 +44,7 @@ public class TimeUnit extends MeasureUnit {
|
||||
// idx must be sequential and must order time units from largest to smallest.
|
||||
// e.g YEAR is 0; MONTH is 1; ...; SECOND is 6.
|
||||
private TimeUnit(String name, int idx) {
|
||||
this.name = name;
|
||||
super("duration", name);
|
||||
this.index = idx;
|
||||
values[idx] = this; // store in values array
|
||||
}
|
||||
@ -55,16 +57,6 @@ public class TimeUnit extends MeasureUnit {
|
||||
return values.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* A string representation for debugging.
|
||||
* It is for debugging purpose. The value might change.
|
||||
* Please do not count on the value.
|
||||
* @stable ICU 4.0
|
||||
*/
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
// Returns the index for this TimeUnit. Something between 0 inclusive and
|
||||
// number of time units exclusive. Smaller time units have larger indexes.
|
||||
int getIndex() {
|
||||
|
@ -0,0 +1,306 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.dev.test.serializable.SerializableTest;
|
||||
import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.text.DecimalFormat;
|
||||
import com.ibm.icu.text.GeneralMeasureFormat;
|
||||
import com.ibm.icu.text.MeasureFormat;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.FormatWidth;
|
||||
import com.ibm.icu.util.Measure;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* @author markdavis
|
||||
*/
|
||||
public class MeasureUnitTest extends TestFmwk {
|
||||
/**
|
||||
* @author markdavis
|
||||
*
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
//generateConstants(); if (true) return;
|
||||
new MeasureUnitTest().run(args);
|
||||
}
|
||||
|
||||
public void testAUnit() {
|
||||
String lastType = null;
|
||||
for (MeasureUnit expected : MeasureUnit.getAvailable()) {
|
||||
String type = expected.getType();
|
||||
String code = expected.getCode();
|
||||
if (!type.equals(lastType)) {
|
||||
logln(type);
|
||||
lastType = type;
|
||||
}
|
||||
MeasureUnit actual = MeasureUnit.getInstance(type, code);
|
||||
assertSame("Identity check", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public void testMultiples() {
|
||||
for (ULocale locale : new ULocale[]{
|
||||
ULocale.ENGLISH,
|
||||
new ULocale("ru"),
|
||||
//ULocale.JAPANESE
|
||||
}) {
|
||||
|
||||
for (FormatWidth style : FormatWidth.values()) {
|
||||
GeneralMeasureFormat gmlf = GeneralMeasureFormat.getInstance(locale, style);
|
||||
String formatted = gmlf.format(
|
||||
new Measure(2, MeasureUnit.MILE),
|
||||
new Measure(1, MeasureUnit.FOOT),
|
||||
new Measure(2.3, MeasureUnit.INCH));
|
||||
logln(locale + ",\t" + style + ": " + formatted);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testGram() {
|
||||
checkRoundtrip(ULocale.ENGLISH, MeasureUnit.GRAM, 1, 0, FormatWidth.SHORT);
|
||||
checkRoundtrip(ULocale.ENGLISH, MeasureUnit.G_FORCE, 1, 0, FormatWidth.SHORT);
|
||||
}
|
||||
|
||||
public void testRoundtripFormat() {
|
||||
for (ULocale locale : new ULocale[]{
|
||||
ULocale.ENGLISH,
|
||||
new ULocale("ru"),
|
||||
//ULocale.JAPANESE
|
||||
}) {
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable()) {
|
||||
for (double d : new double[]{2.1, 1}) {
|
||||
for (int fractionalDigits : new int[]{0, 1}) {
|
||||
for (FormatWidth style : FormatWidth.values()) {
|
||||
checkRoundtrip(locale, unit, d, fractionalDigits, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRoundtrip(ULocale locale, MeasureUnit unit, double d, int fractionalDigits, FormatWidth style) {
|
||||
if (unit instanceof Currency) {
|
||||
return; // known limitation
|
||||
}
|
||||
Measure amount = new Measure(d, unit);
|
||||
String header = locale
|
||||
+ "\t" + unit
|
||||
+ "\t" + d
|
||||
+ "\t" + fractionalDigits;
|
||||
ParsePosition pex = new ParsePosition(0);
|
||||
NumberFormat nformat = NumberFormat.getInstance(locale);
|
||||
nformat.setMinimumFractionDigits(fractionalDigits);
|
||||
|
||||
GeneralMeasureFormat format = GeneralMeasureFormat.getInstance(locale, style, nformat);
|
||||
|
||||
FieldPosition pos = new FieldPosition(DecimalFormat.FRACTION_FIELD);
|
||||
StringBuffer b = format.format(amount, new StringBuffer(), pos);
|
||||
String message = header + "\t" + style
|
||||
+ "\t«" + b.substring(0, pos.getBeginIndex())
|
||||
+ "⟪" + b.substring(pos.getBeginIndex(), pos.getEndIndex())
|
||||
+ "⟫" + b.substring(pos.getEndIndex()) + "»";
|
||||
pex.setIndex(0);
|
||||
Measure unitAmount = format.parseObject(b.toString(), pex);
|
||||
if (!assertNotNull(message, unitAmount)) {
|
||||
logln("Parse: «"
|
||||
+ b.substring(0,pex.getErrorIndex())
|
||||
+ "||" + b.substring(pex.getErrorIndex()) + "»");
|
||||
} else if (style != FormatWidth.NARROW) { // narrow items may collide
|
||||
if (unit.equals(MeasureUnit.GRAM)) {
|
||||
logKnownIssue("cldrupdate", "waiting on collision fix for gram");
|
||||
return;
|
||||
}
|
||||
if (unit.equals(MeasureUnit.ARC_MINUTE) || unit.equals(MeasureUnit.ARC_SECOND) || unit.equals(MeasureUnit.METER)) {
|
||||
logKnownIssue("8474", "Waiting for CLDR data");
|
||||
} else {
|
||||
assertEquals(message + "\tParse Roundtrip of unit", unit, unitAmount.getUnit());
|
||||
}
|
||||
double actualNumber = unitAmount.getNumber().doubleValue();
|
||||
assertEquals(message + "\tParse Roundtrip of number", d, actualNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public void testExamples() {
|
||||
GeneralMeasureFormat fmtFr = GeneralMeasureFormat.getInstance(ULocale.FRENCH, FormatWidth.SHORT);
|
||||
Measure measure = new Measure(23, MeasureUnit.CELSIUS);
|
||||
assertEquals("", "23°C", fmtFr.format(measure));
|
||||
|
||||
Measure measureF = new Measure(70, MeasureUnit.FAHRENHEIT);
|
||||
assertEquals("", "70°F", fmtFr.format(measureF));
|
||||
|
||||
GeneralMeasureFormat fmtFrFull = GeneralMeasureFormat.getInstance(ULocale.FRENCH, FormatWidth.WIDE);
|
||||
if (!logKnownIssue("8474", "needs latest CLDR data")) {
|
||||
assertEquals("", "70 pieds, 5,3 pouces", fmtFrFull.format(new Measure(70, MeasureUnit.FOOT),
|
||||
new Measure(5.3, MeasureUnit.INCH)));
|
||||
assertEquals("", "1 pied, 1 pouce", fmtFrFull.format(new Measure(1, MeasureUnit.FOOT),
|
||||
new Measure(1, MeasureUnit.INCH)));
|
||||
}
|
||||
// Degenerate case
|
||||
GeneralMeasureFormat fmtEn = GeneralMeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
|
||||
assertEquals("", "1 inch, 2 feet", fmtEn.format(new Measure(1, MeasureUnit.INCH),
|
||||
new Measure(2, MeasureUnit.FOOT)));
|
||||
|
||||
logln("Show all currently available units");
|
||||
String lastType = null;
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable()) {
|
||||
String type = unit.getType();
|
||||
if (!type.equals(lastType)) {
|
||||
logln(type);
|
||||
lastType = type;
|
||||
}
|
||||
logln("\t" + unit);
|
||||
}
|
||||
// TODO
|
||||
// Add these examples (and others) to the class definition.
|
||||
// Clarify that these classes *do not* do conversion; they simply do the formatting of whatever units they
|
||||
// are provided.
|
||||
}
|
||||
|
||||
static void generateConstants() {
|
||||
System.out.println("static final MeasureUnit");
|
||||
Map<String, MeasureUnit> seen = new HashMap<String, MeasureUnit>();
|
||||
boolean first = true;
|
||||
for (String type : new TreeSet<String>(MeasureUnit.getAvailableTypes())) {
|
||||
for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
|
||||
String code = unit.getCode();
|
||||
String name = code.toUpperCase(Locale.ENGLISH).replace("-", "_");
|
||||
|
||||
if (type.equals("angle")) {
|
||||
if (code.equals("minute") || code.equals("second")) {
|
||||
name = "ARC_" + name;
|
||||
}
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
System.out.print(",");
|
||||
}
|
||||
if (seen.containsKey(name)) {
|
||||
System.out.println("\nCollision!!" + unit + ", " + seen.get(name));
|
||||
} else {
|
||||
seen.put(name, unit);
|
||||
}
|
||||
System.out.println("\n\t/** Constant for unit of " + type +
|
||||
": " +
|
||||
code +
|
||||
" */");
|
||||
|
||||
System.out.print("\t" + name + " = MeasureUnit.getInstance(\"" +
|
||||
type +
|
||||
"\", \"" +
|
||||
code +
|
||||
"\")");
|
||||
}
|
||||
System.out.println(";");
|
||||
}
|
||||
}
|
||||
|
||||
public void TestSerial() {
|
||||
checkStreamingEquality(MeasureUnit.CELSIUS);
|
||||
checkStreamingEquality(GeneralMeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.NARROW));
|
||||
checkStreamingEquality(Currency.getInstance("EUR"));
|
||||
}
|
||||
|
||||
public <T extends Serializable> void checkStreamingEquality(T item) {
|
||||
try {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOut);
|
||||
objectOutputStream.writeObject(item);
|
||||
objectOutputStream.close();
|
||||
byte[] contents = byteOut.toByteArray();
|
||||
logln("bytes: " + contents.length + "; " + item.getClass() + ": " + showBytes(contents));
|
||||
ByteArrayInputStream byteIn = new ByteArrayInputStream(contents);
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(byteIn);
|
||||
Object obj = objectInputStream.readObject();
|
||||
assertEquals("Streamed Object equals ", item, obj);
|
||||
} catch (IOException e) {
|
||||
assertNull("Test Serialization " + item.getClass(), e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
assertNull("Test Serialization " + item.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contents
|
||||
* @return
|
||||
*/
|
||||
private String showBytes(byte[] contents) {
|
||||
StringBuilder b = new StringBuilder('[');
|
||||
for (int i = 0; i < contents.length; ++i) {
|
||||
int item = contents[i] & 0xFF;
|
||||
if (item >= 0x20 && item <= 0x7F) {
|
||||
b.append((char) item);
|
||||
} else {
|
||||
b.append('(').append(Utility.hex(item, 2)).append(')');
|
||||
}
|
||||
}
|
||||
return b.append(']').toString();
|
||||
}
|
||||
|
||||
public static class MeasureUnitHandler implements SerializableTest.Handler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
MeasureUnit items[] = {
|
||||
MeasureUnit.CELSIUS,
|
||||
Currency.getInstance("EUR")
|
||||
};
|
||||
return items;
|
||||
}
|
||||
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
MeasureUnit a1 = (MeasureUnit) a;
|
||||
MeasureUnit b1 = (MeasureUnit) b;
|
||||
return a1.getType().equals(b1.getType())
|
||||
&& a1.getCode().equals(b1.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public static class GeneralMeasureFormatHandler implements SerializableTest.Handler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
GeneralMeasureFormat items[] = {
|
||||
GeneralMeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.SHORT),
|
||||
GeneralMeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.WIDE, NumberFormat.getIntegerInstance(ULocale.CANADA_FRENCH
|
||||
)),
|
||||
};
|
||||
return items;
|
||||
}
|
||||
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
GeneralMeasureFormat a1 = (GeneralMeasureFormat) a;
|
||||
GeneralMeasureFormat b1 = (GeneralMeasureFormat) b;
|
||||
return a1.getLocale().equals(b1.getLocale())
|
||||
&& a1.getLength().equals(b1.getLength())
|
||||
// && a1.getNumberFormat().equals(b1.getNumberFormat())
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,10 @@
|
||||
|
||||
package com.ibm.icu.dev.test.format;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -15,6 +19,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.text.DecimalFormat;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.MessageFormat;
|
||||
@ -23,6 +28,7 @@ import com.ibm.icu.text.PluralFormat;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.text.PluralRules.SampleType;
|
||||
import com.ibm.icu.text.UFieldPosition;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
@ -85,7 +91,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
plfOddOrEven.applyPattern("other{# is odd or even.}");
|
||||
|
||||
NumberFormat numberFormat =
|
||||
NumberFormat.getInstance(ULocale.getDefault());
|
||||
NumberFormat.getInstance(ULocale.getDefault());
|
||||
for (int i = 0; i < 22; ++i) {
|
||||
assertEquals("Fallback to other gave wrong results",
|
||||
numberFormat.format(i) + " is odd or even.",
|
||||
@ -98,7 +104,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
|
||||
// ICU 4.8 does not check for duplicate keywords any more.
|
||||
PluralFormat pf = new PluralFormat(ULocale.ENGLISH, oddAndEven,
|
||||
"odd{foo} odd{bar} other{foobar}");
|
||||
"odd{foo} odd{bar} other{foobar}");
|
||||
assertEquals("should use first occurrence of the 'odd' keyword", "foo", pf.format(1));
|
||||
pf.applyPattern("odd{foo} other{bar} other{foobar}");
|
||||
assertEquals("should use first occurrence of the 'other' keyword", "bar", pf.format(2));
|
||||
@ -111,7 +117,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("odd{foo}");
|
||||
errln("Not defining plural case other should result in an " +
|
||||
"exception but did not.");
|
||||
"exception but did not.");
|
||||
}catch (IllegalArgumentException e){}
|
||||
|
||||
// ICU 4.8 does not check for unknown keywords any more.
|
||||
@ -125,7 +131,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("*odd{foo} other{bar}");
|
||||
errln("Defining a message for an invalid keyword should result in " +
|
||||
"an exception but did not.");
|
||||
"an exception but did not.");
|
||||
}catch (IllegalArgumentException e){}
|
||||
|
||||
// Test invalid syntax
|
||||
@ -136,26 +142,26 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("odd{foo},other{bar}");
|
||||
errln("Separating keyword{message} items with other characters " +
|
||||
"than space should provoke an exception but did not.");
|
||||
"than space should provoke an exception but did not.");
|
||||
}catch (IllegalArgumentException e){}
|
||||
try {
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("od d{foo} other{bar}");
|
||||
errln("Spaces inside keywords should provoke an exception but " +
|
||||
"did not.");
|
||||
"did not.");
|
||||
}catch (IllegalArgumentException e){}
|
||||
try {
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("odd{foo}{foobar}other{foo}");
|
||||
errln("Defining multiple messages after a keyword should provoke " +
|
||||
"an exception but did not.");
|
||||
"an exception but did not.");
|
||||
}catch (IllegalArgumentException e){}
|
||||
|
||||
// Check that nested format is preserved.
|
||||
{
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("odd{The number {0, number, #.#0} is odd.}" +
|
||||
"other{The number {0, number, #.#0} is even.}");
|
||||
"other{The number {0, number, #.#0} is even.}");
|
||||
for (int i = 1; i < 3; ++i) {
|
||||
assertEquals("format did not preserve a nested format string.",
|
||||
((i % 2 == 1) ?
|
||||
@ -169,7 +175,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
{
|
||||
PluralFormat plFmt = new PluralFormat(oddAndEven);
|
||||
plFmt.applyPattern("odd{The number {1,number,#} is odd.}" +
|
||||
"other{The number {2,number,#} is even.}");
|
||||
"other{The number {2,number,#} is even.}");
|
||||
for (int i = 1; i < 3; ++i) {
|
||||
assertEquals("format did not preserve # inside curly braces.",
|
||||
((i % 2 == 1) ? "The number {1,number,#} is odd."
|
||||
@ -180,6 +186,45 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
public void TestSerial() {
|
||||
PluralRules s = PluralRules.forLocale(ULocale.ENGLISH);
|
||||
checkStreamingEquality(s);
|
||||
}
|
||||
|
||||
public void checkStreamingEquality(PluralRules s) {
|
||||
try {
|
||||
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOut);
|
||||
objectOutputStream.writeObject(s);
|
||||
objectOutputStream.close();
|
||||
byte[] contents = byteOut.toByteArray();
|
||||
logln(s.getClass() + ": " + showBytes(contents));
|
||||
ByteArrayInputStream byteIn = new ByteArrayInputStream(contents);
|
||||
ObjectInputStream objectInputStream = new ObjectInputStream(byteIn);
|
||||
Object obj = objectInputStream.readObject();
|
||||
assertEquals("Streamed Object equals ", s, obj);
|
||||
} catch (Exception e) {
|
||||
assertNull("TestSerial", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contents
|
||||
* @return
|
||||
*/
|
||||
private String showBytes(byte[] contents) {
|
||||
StringBuilder b = new StringBuilder('[');
|
||||
for (int i = 0; i < contents.length; ++i) {
|
||||
int item = contents[i] & 0xFF;
|
||||
if (item >= 0x20 && item <= 0x7F) {
|
||||
b.append((char) item);
|
||||
} else {
|
||||
b.append('(').append(Utility.hex(item, 2)).append(')');
|
||||
}
|
||||
}
|
||||
return b.append(']').toString();
|
||||
}
|
||||
|
||||
public void TestSamples() {
|
||||
Map<ULocale,Set<ULocale>> same = new LinkedHashMap();
|
||||
for (ULocale locale : PluralRules.getAvailableULocales()) {
|
||||
@ -242,7 +287,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
try {
|
||||
plFmt.parse("test", new ParsePosition(0));
|
||||
errln("parse() should throw an UnsupportedOperationException but " +
|
||||
"did not");
|
||||
"did not");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
}
|
||||
|
||||
@ -250,7 +295,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
try {
|
||||
plFmt.parseObject("test", new ParsePosition(0));
|
||||
errln("parse() should throw an UnsupportedOperationException but " +
|
||||
"did not");
|
||||
"did not");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
}
|
||||
}
|
||||
@ -276,7 +321,7 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
* (because it does not create an object for any "complex" argument).
|
||||
PluralFormat pf = (PluralFormat)pfmt.getFormatsByArgumentIndex()[1];
|
||||
logln(pf.toPattern());
|
||||
*/
|
||||
*/
|
||||
logln(pfmt.toPattern());
|
||||
MessageFormat pfmt2 = new MessageFormat(pfmt.toPattern());
|
||||
assertEquals("message formats are equal", pfmt, pfmt2);
|
||||
@ -284,22 +329,22 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
|
||||
public void TestExtendedPluralFormat() {
|
||||
String[] targets = {
|
||||
"There are no widgets.",
|
||||
"There is one widget.",
|
||||
"There is a bling widget and one other widget.",
|
||||
"There is a bling widget and 2 other widgets.",
|
||||
"There is a bling widget and 3 other widgets.",
|
||||
"Widgets, five (5-1=4) there be.",
|
||||
"There is a bling widget and 5 other widgets.",
|
||||
"There is a bling widget and 6 other widgets.",
|
||||
"There are no widgets.",
|
||||
"There is one widget.",
|
||||
"There is a bling widget and one other widget.",
|
||||
"There is a bling widget and 2 other widgets.",
|
||||
"There is a bling widget and 3 other widgets.",
|
||||
"Widgets, five (5-1=4) there be.",
|
||||
"There is a bling widget and 5 other widgets.",
|
||||
"There is a bling widget and 6 other widgets.",
|
||||
};
|
||||
String pluralStyle =
|
||||
"offset:1.0 "
|
||||
+ "=0 {There are no widgets.} "
|
||||
+ "=1.0 {There is one widget.} "
|
||||
+ "=5 {Widgets, five (5-1=#) there be.} "
|
||||
+ "one {There is a bling widget and one other widget.} "
|
||||
+ "other {There is a bling widget and # other widgets.}";
|
||||
"offset:1.0 "
|
||||
+ "=0 {There are no widgets.} "
|
||||
+ "=1.0 {There is one widget.} "
|
||||
+ "=5 {Widgets, five (5-1=#) there be.} "
|
||||
+ "one {There is a bling widget and one other widget.} "
|
||||
+ "other {There is a bling widget and # other widgets.}";
|
||||
PluralFormat pf = new PluralFormat(ULocale.ENGLISH, pluralStyle);
|
||||
MessageFormat mf = new MessageFormat("{0,plural," + pluralStyle + "}", ULocale.ENGLISH);
|
||||
Integer args[] = new Integer[1];
|
||||
@ -318,11 +363,11 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
|
||||
public void TestExtendedPluralFormatParsing() {
|
||||
String[] failures = {
|
||||
"offset:1..0 =0 {Foo}",
|
||||
"offset:1.0 {Foo}",
|
||||
"=0= {Foo}",
|
||||
"=0 {Foo} =0.0 {Bar}",
|
||||
" = {Foo}",
|
||||
"offset:1..0 =0 {Foo}",
|
||||
"offset:1.0 {Foo}",
|
||||
"=0= {Foo}",
|
||||
"=0 {Foo} =0.0 {Bar}",
|
||||
" = {Foo}",
|
||||
};
|
||||
for (String fmt : failures) {
|
||||
try {
|
||||
@ -348,12 +393,47 @@ public class PluralFormatUnitTest extends TestFmwk {
|
||||
assertEquals("PluralFormat.format(111)", "111th file", pf.format(111));
|
||||
}
|
||||
|
||||
public void TestBasicFraction() {
|
||||
String[][] tests = {
|
||||
{"en", "one: j is 1"},
|
||||
{"1", "0", "1", "one"},
|
||||
{"1", "2", "1.00", "other"},
|
||||
};
|
||||
ULocale locale = null;
|
||||
NumberFormat nf = null;
|
||||
PluralRules pr = null;
|
||||
for (String[] row : tests) {
|
||||
switch(row.length) {
|
||||
case 2:
|
||||
locale = ULocale.forLanguageTag(row[0]);
|
||||
nf = NumberFormat.getInstance(locale);
|
||||
pr = PluralRules.createRules(row[1]);
|
||||
break;
|
||||
case 4:
|
||||
double n = Double.parseDouble(row[0]);
|
||||
int minFracDigits = Integer.parseInt(row[1]);
|
||||
nf.setMinimumFractionDigits(minFracDigits);
|
||||
String expectedFormat = row[2];
|
||||
String expectedKeyword = row[3];
|
||||
|
||||
UFieldPosition pos = new UFieldPosition();
|
||||
String formatted = nf.format(1.0, new StringBuffer(), pos).toString();
|
||||
int countVisibleFractionDigits = pos.getCountVisibleFractionDigits();
|
||||
long fractionDigits = pos.getFractionDigits();
|
||||
String keyword = pr.select(n, countVisibleFractionDigits, fractionDigits);
|
||||
assertEquals("Formatted " + n + "\t" + minFracDigits, expectedFormat, formatted);
|
||||
assertEquals("Keyword " + n + "\t" + minFracDigits, expectedKeyword, keyword);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void TestDecimals() {
|
||||
// Simple number replacement.
|
||||
PluralFormat pf = new PluralFormat(ULocale.ENGLISH, "one{one meter}other{# meters}");
|
||||
assertEquals("simple format(1)", "one meter", pf.format(1));
|
||||
assertEquals("simple format(1.5)", "1.5 meters", pf.format(1.5));
|
||||
|
||||
PluralFormat pf2 = new PluralFormat(ULocale.ENGLISH,
|
||||
"offset:1 one{another meter}other{another # meters}");
|
||||
pf2.setNumberFormat(new DecimalFormat("0.0", new DecimalFormatSymbols(ULocale.ENGLISH)));
|
||||
|
@ -30,6 +30,7 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.dev.test.serializable.SerializableTest;
|
||||
import com.ibm.icu.dev.util.CollectionUtilities;
|
||||
import com.ibm.icu.dev.util.Relation;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
@ -42,6 +43,8 @@ import com.ibm.icu.text.PluralRules.KeywordStatus;
|
||||
import com.ibm.icu.text.PluralRules.FixedDecimal;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.text.PluralRules.SampleType;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.Output;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
@ -1020,4 +1023,26 @@ public class PluralRulesTest extends TestFmwk {
|
||||
}
|
||||
logln("max \tsize:\t" + max);
|
||||
}
|
||||
|
||||
public static class FixedDecimalHandler implements SerializableTest.Handler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
FixedDecimal items[] = {
|
||||
new FixedDecimal(3d),
|
||||
new FixedDecimal(3d, 2),
|
||||
new FixedDecimal(3.1d, 1),
|
||||
new FixedDecimal(3.1d, 2),
|
||||
};
|
||||
return items;
|
||||
}
|
||||
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
FixedDecimal a1 = (FixedDecimal) a;
|
||||
FixedDecimal b1 = (FixedDecimal) b;
|
||||
return a1.equals(b1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
@ -114,9 +117,12 @@ public class CompatibilityTest extends TestFmwk
|
||||
}catch (MissingResourceException e){
|
||||
warnln("Could not load the data. "+e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
errln("Exception: " + e.toString());
|
||||
|
||||
if (e.getMessage() == null
|
||||
|| !e.getMessage().contains("com.ibm.icu.util.Currency")) {
|
||||
StringWriter b = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(b));
|
||||
assertNull("", b.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,6 +157,12 @@ public class CompatibilityTest extends TestFmwk
|
||||
{"ICU_51.1", "com.ibm.icu.text.PluralFormat.dat"},
|
||||
{"ICU_51.1", "com.ibm.icu.text.PluralRules.dat"},
|
||||
|
||||
// Currency format changed in 52, which means NumberFormat did also
|
||||
{"ICU_3.6", "com.ibm.icu.util.Currency.dat"},
|
||||
{"ICU_3.6", "com.ibm.icu.text.NumberFormat.dat"},
|
||||
{"ICU_51.1", "com.ibm.icu.util.Currency.dat"},
|
||||
{"ICU_51.1", "com.ibm.icu.text.NumberFormat.dat"},
|
||||
|
||||
{"ICU_3.6", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
|
||||
{"ICU_3.8.1", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
|
||||
{"ICU_4.0", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2005-2012, International Business Machines Corporation and *
|
||||
* Copyright (C) 2005-2013, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
@ -110,7 +110,7 @@ public class CoverageTest extends CompatibilityTest implements URLHandler.URLVis
|
||||
try {
|
||||
/*Field uid = */c.getDeclaredField("serialVersionUID");
|
||||
} catch (Exception e) {
|
||||
errln("No serialVersionUID");
|
||||
errln("No serialVersionUID for " + name);
|
||||
}
|
||||
|
||||
if (inputStream == null) {
|
||||
|
@ -150,18 +150,6 @@ public class ExceptionTests
|
||||
return exceptions;
|
||||
}
|
||||
}
|
||||
|
||||
static class FixedDecimalExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
IllegalIcuArgumentException[] exceptions = {
|
||||
new IllegalIcuArgumentException("Bad argument FOO")
|
||||
};
|
||||
return exceptions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
|
@ -13,6 +13,8 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.dev.test.format.MeasureUnitTest;
|
||||
import com.ibm.icu.dev.test.format.PluralRulesTest;
|
||||
import com.ibm.icu.impl.JavaTimeZone;
|
||||
import com.ibm.icu.impl.OlsonTimeZone;
|
||||
import com.ibm.icu.impl.TimeZoneAdapter;
|
||||
@ -417,10 +419,14 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
|
||||
Currency curr_a = (Currency) a;
|
||||
Currency curr_b = (Currency) b;
|
||||
|
||||
return curr_a.getCurrencyCode().equals(curr_b.getCurrencyCode());
|
||||
return a == b
|
||||
|| a != null && b != null
|
||||
&& curr_a.getCurrencyCode() != null
|
||||
&& curr_a.getCurrencyCode().equals(curr_b.getCurrencyCode());
|
||||
|
||||
}
|
||||
}
|
||||
@ -709,7 +715,10 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
map.put("com.ibm.icu.impl.locale.LocaleSyntaxException", new ExceptionTests.LocaleSyntaxExceptionHandler());
|
||||
map.put("com.ibm.icu.impl.IllegalIcuArgumentException", new ExceptionTests.IllegalIcuArgumentExceptionHandler());
|
||||
|
||||
map.put("com.ibm.icu.text.PluralRules$FixedDecimal", new ExceptionTests.FixedDecimalExceptionHandler());
|
||||
map.put("com.ibm.icu.text.PluralRules$FixedDecimal", new PluralRulesTest.FixedDecimalHandler());
|
||||
map.put("com.ibm.icu.util.MeasureUnit", new MeasureUnitTest.MeasureUnitHandler());
|
||||
map.put("com.ibm.icu.util.TimeUnit", new MeasureUnitTest.MeasureUnitHandler());
|
||||
map.put("com.ibm.icu.text.GeneralMeasureFormat", new MeasureUnitTest.GeneralMeasureFormatHandler());
|
||||
}
|
||||
|
||||
public SerializableTest()
|
||||
|
Loading…
Reference in New Issue
Block a user