*/
package com.ibm.icu.text;
-import java.io.Serializable;
+import java.io.ObjectStreamException;
import java.text.FieldPosition;
import java.text.ParsePosition;
import com.ibm.icu.util.CurrencyAmount;
+import com.ibm.icu.util.Measure;
import com.ibm.icu.util.ULocale;
/**
* @see com.ibm.icu.text.DecimalFormat
* @author Alan Liu
*/
-class CurrencyFormat extends MeasureFormat implements Serializable {
+class CurrencyFormat extends MeasureFormat {
// Generated by serialver from JDK 1.4.1_01
static final long serialVersionUID = -931679363692504634L;
private NumberFormat fmt;
+ private transient final MeasureFormat mf;
public CurrencyFormat(ULocale locale) {
+ mf = MeasureFormat.getInstance(locale, FormatWidth.WIDE);
fmt = NumberFormat.getCurrencyInstance(locale.toLocale());
}
* @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
*/
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
- try {
- CurrencyAmount currency = (CurrencyAmount) obj;
+ if (!(obj instanceof CurrencyAmount)) {
+ throw new IllegalArgumentException("Invalid type: " + obj.getClass().getName());
+ }
+ CurrencyAmount currency = (CurrencyAmount) obj;
+
+ // Since we extend MeasureFormat, we have to maintain thread-safety.
+ synchronized (fmt) {
fmt.setCurrency(currency.getCurrency());
return fmt.format(currency.getNumber(), toAppendTo, pos);
- } catch (ClassCastException e) {
- throw new IllegalArgumentException("Invalid type: " + obj.getClass().getName());
}
}
* Override Format.parseObject().
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
*/
- public Object parseObject(String source, ParsePosition pos) {
- return fmt.parseCurrency(source, pos);
+ @Override
+ public CurrencyAmount parseObject(String source, ParsePosition pos) {
+ synchronized (fmt) {
+ return fmt.parseCurrency(source, pos);
+ }
+ }
+
+ // boilerplate code to make CurrencyFormat otherwise follow the contract of
+ // MeasureFormat
+
+ @Override
+ public String formatMeasures(Measure... measures) {
+ return mf.formatMeasures(measures);
+ }
+
+ @Override
+ public <T extends Appendable> T formatMeasure(
+ Measure measure, T appendable, FieldPosition fieldPosition) {
+ return mf.formatMeasure(measure, appendable, fieldPosition);
+ }
+
+ @Override
+ public <T extends Appendable> T formatMeasures(
+ T appendable, FieldPosition fieldPosition, Measure... measures) {
+ return mf.formatMeasures(appendable, fieldPosition, measures);
+ }
+
+ @Override
+ public MeasureFormat.FormatWidth getWidth() {
+ return mf.getWidth();
+ }
+
+ @Override
+ public ULocale getLocale() {
+ return mf.getLocale();
+ }
+
+ @Override
+ public NumberFormat getNumberFormat() {
+ return mf.getNumberFormat();
+ }
+
+ // End boilerplate.
+
+
+ @Override
+ public int hashCode() {
+ return getLocale().hashCode() + 154321962;
+ }
+
+ @Override
+ protected boolean equalsSameClass(MeasureFormat other) {
+ return getLocale().equals(other.getLocale());
+ }
+
+ // Serialization
+
+ private Object writeReplace() throws ObjectStreamException {
+ return mf.toCurrencyProxy();
+ }
+
+ // Preserve backward serialize backward compatibility.
+ private Object readResolve() throws ObjectStreamException {
+ return new CurrencyFormat(fmt.getLocale(ULocale.ACTUAL_LOCALE));
}
}
+++ /dev/null
-/*
- *******************************************************************************
- * 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.ULocale.Category;
-import com.ibm.icu.util.UResourceBundle;
-
-/**
- * Mutable class for formatting GeneralMeasures, or sequences of them.
- * @author markdavis
- * @internal
- * @deprecated This API is ICU internal only.
- */
-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;
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public static MeasureFormat getCurrencyFormat() {
- return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
- }
-
- /**
- * @return the locale of the format.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public ULocale getLocale() {
- return locale;
- }
-
- /**
- * @return the desired length for the format
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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;
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @SuppressWarnings("unchecked")
- @Override
- public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
- if (obj instanceof Collection) {
- Collection<Measure> coll = (Collection<Measure>) obj;
- return format(toAppendTo, pos, coll.toArray(new Measure[coll.size()]));
- } else if (obj instanceof Measure[]) {
- return format(toAppendTo, pos, (Measure[]) obj);
- } else {
- return format((Measure) obj, toAppendTo, pos);
- }
- }
-
- /**
- * Format a general measure (type-safe).
- * @param measure the measure to format
- * @param toAppendTo as in {@link #format(Object, StringBuffer, FieldPosition)}
- * @param pos as in {@link #format(Object, StringBuffer, FieldPosition)}
- * @return passed-in buffer with appended text.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public StringBuffer format(Measure measure, StringBuffer toAppendTo, FieldPosition pos) {
- Number n = measure.getNumber();
- MeasureUnit unit = measure.getUnit();
- UFieldPosition fpos = new UFieldPosition(pos.getFieldAttribute(), pos.getField());
- StringBuffer formattedNumber = numberFormat.format(n, new StringBuffer(), fpos);
- String keyword = rules.select(new PluralRules.FixedDecimal(n.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
-
- Map<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
- Map<String, PatternData> countToFormat = styleToCountToFormat.get(length);
- PatternData messagePatternData = countToFormat.get(keyword);
-
- toAppendTo.append(messagePatternData.prefix);
- if (messagePatternData.suffix != null) { // there is a number (may not happen with, say, Arabic dual)
- // Fix field position
- pos.setBeginIndex(fpos.getBeginIndex() + messagePatternData.prefix.length());
- pos.setEndIndex(fpos.getEndIndex() + messagePatternData.prefix.length());
- toAppendTo.append(formattedNumber);
- toAppendTo.append(messagePatternData.suffix);
- }
- return toAppendTo;
- }
-
-
- /**
- * Format a sequence of measures.
- * @param toAppendto as in {@link #format(Object, StringBuffer, FieldPosition)}
- * @param pos as in {@link #format(Object, StringBuffer, FieldPosition)}
- * @param measures a sequence of one or more measures.
- * @return passed-in buffer with appended text.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public StringBuffer format(StringBuffer toAppendto, FieldPosition pos, Measure... measures) {
- StringBuffer[] results = new StringBuffer[measures.length];
- for (int i = 0; i < measures.length; ++i) {
- results[i] = format(measures[i], new StringBuffer(), pos);
- }
- ListFormatter listFormatter = ListFormatter.getInstance(locale,
- length == FormatWidth.WIDE ? ListFormatter.Style.DURATION : ListFormatter.Style.DURATION_SHORT);
- return toAppendto.append(listFormatter.format((Object[]) results));
- }
-
- /**
- * Format a sequence of measures.
- * @param measures a sequence of one or more measures.
- * @return passed-in buffer with appended text.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- 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 synchronized 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;
- }
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @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);
- }
- };
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @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);
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @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 static final long serialVersionUID = -6033308329886716770L;
-
- 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);
- }
- }
-}
\ No newline at end of file
/*
**********************************************************************
-* Copyright (c) 2004-2011, International Business Machines
+* Copyright (c) 2004-2013, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
* Author: Alan Liu
*/
package com.ibm.icu.text;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectStreamException;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.SimpleCache;
+import com.ibm.icu.util.Currency;
+import com.ibm.icu.util.Measure;
+import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
+import com.ibm.icu.util.UResourceBundle;
/**
- * A formatter for Measure objects. This is an abstract base class.
+ * A formatter for Measure objects.
*
- * <p>To format or parse a Measure object, first create a formatter
+ * <p>To format a Measure object, first create a formatter
* object using a MeasureFormat factory method. Then use that
- * object's format and parse methods.
+ * object's format, formatMeasure, or formatMeasures methods.
+ *
+ * Here is sample code:
+ * <pre>
+ * MeasureFormat fmtFr = MeasureFormat.getInstance(
+ * ULocale.FRENCH, FormatWidth.SHORT);
+ * Measure measure = new Measure(23, MeasureUnit.CELSIUS);
+ *
+ * // Output: 23 °C
+ * System.out.println(fmtFr.format(measure));
+ *
+ * Measure measureF = new Measure(70, MeasureUnit.FAHRENHEIT);
+ *
+ * // Output: 70 °F
+ * System.out.println(fmtFr.format(measureF));
+ *
+ * MeasureFormat fmtFrFull = MeasureFormat.getInstance(
+ * ULocale.FRENCH, FormatWidth.WIDE);
+ * // Output: 70 pieds, 5,3 pouces
+ * System.out.println(fmtFrFull.formatMeasures(
+ * new Measure(70, MeasureUnit.FOOT),
+ * new Measure(5.3, MeasureUnit.INCH)));
+ *
+ * // Output: 1 pied, 1 pouce
+ * System.out.println(fmtFrFull.formatMeasures(
+ * new Measure(1, MeasureUnit.FOOT),
+ * new Measure(1, MeasureUnit.INCH)));
+ * }
+ *
+ * MeasureFormat fmtEn = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
+ *
+ * // Output: 1 inch, 2 feet
+ * fmtEn.formatMeasures(
+ * new Measure(1, MeasureUnit.INCH),
+ * new Measure(2, MeasureUnit.FOOT));
+ * </pre>
+ * <p>
+ * This class does not do conversions from one unit to another. It simply formats
+ * whatever units it is given
+ * <p>
+ * This class is immutable so long as no mutable subclass, namely TimeUnitFormat,
+ * is used. Although this class has existing subclasses, this class does not
+ * support new sub-classes.
*
* @see com.ibm.icu.text.UFormat
* @author Alan Liu
* @stable ICU 3.0
*/
-public abstract class MeasureFormat extends UFormat {
+public class MeasureFormat extends UFormat {
+
+
// Generated by serialver from JDK 1.4.1_01
static final long serialVersionUID = -7182021401701778240L;
+
+ private final transient ULocale locale;
+
+ // NumberFormat is known to lack thread-safety, all access to this
+ // field must be synchronized.
+ private final transient NumberFormat numberFormat;
+
+ private final transient FormatWidth length;
+
+ // 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, Map<String, PatternData>>> unitToStyleToCountToFormat;
+
+ static final SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>> localeToUnitToStyleToCountToFormat
+ = new SimpleCache<ULocale,Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>>();
+
+ // 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.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ public static enum FormatWidth {
+
+ /**
+ * Spell out everything.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ WIDE("units"),
+
+ /**
+ * Abbreviate when possible.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ SHORT("unitsShort"),
+
+ /**
+ * Brief. Use only a symbol for the unit when possible.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ NARROW("unitsNarrow");
+
+ final String resourceKey;
+
+ private FormatWidth(String resourceKey) {
+ this.resourceKey = resourceKey;
+ }
+ }
+
+ /**
+ * Create a format from the locale, length, and format.
+ *
+ * @param locale the locale.
+ * @param width hints how long formatted strings should be.
+ * @return The new MeasureFormat object.
+ * @draft ICU 53
+ * @provisional
+ */
+ public static MeasureFormat getInstance(ULocale locale, FormatWidth width) {
+ return getInstance(locale, width, NumberFormat.getInstance(locale));
+ }
+
+ /**
+ * Create a format from the locale, length, and format.
+ *
+ * @param locale the locale.
+ * @param width hints how long formatted strings should be.
+ * @param format This is defensively copied.
+ * @return The new MeasureFormat object.
+ * @draft ICU 53
+ * @provisional
+ */
+ public static MeasureFormat getInstance(ULocale locale, FormatWidth width, NumberFormat format) {
+ PluralRules rules = PluralRules.forLocale(locale);
+ Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat;
+ unitToStyleToCountToFormat = localeToUnitToStyleToCountToFormat.get(locale);
+ if (unitToStyleToCountToFormat == null) {
+ unitToStyleToCountToFormat = loadLocaleData(locale, rules);
+ localeToUnitToStyleToCountToFormat.put(locale, unitToStyleToCountToFormat);
+ }
+ return new MeasureFormat(locale, width, format, rules, unitToStyleToCountToFormat);
+ }
+
+ /**
+ * Format a sequence of measures. Uses the ListFormatter unit lists.
+ * So, for example, one could format “3 feet, 2 inches”.
+ * Zero values are formatted (eg, “3 feet, 0 inches”). 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 descending order, with all but the last Measure having integer values
+ * (eg, not “3.2 feet, 2 inches”).
+ *
+ * @param measures a sequence of one or more measures.
+ * @return the formatted string.
+ * @draft ICU 53
+ * @provisional
+ */
+ public String formatMeasures(Measure... measures) {
+ StringBuilder result = this.formatMeasures(
+ new StringBuilder(), new FieldPosition(0), measures);
+ return result.toString();
+ }
+
+ /**
+ * Able to format Collection<? extends Measure>, Measure[], and Measure
+ * by delegating to formatMeasure or formatMeasures.
+ * If the pos argument identifies a field used by the format
+ * then its indices are set to the beginning and end of the first such field
+ * encountered.
+ *
+ * @param obj must be a Collection<? extends Measure>, Measure[], or Measure object.
+ * @param toAppendTo Formatted string appended here.
+ * @pram pos Identifies a field in the formatted text.
+ * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition)
+ *
+ * @draft ICU53
+ * @provisional
+ */
+ @Override
+ public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+ if (obj instanceof Collection) {
+ Collection<?> coll = (Collection<?>) obj;
+ Measure[] measures = new Measure[coll.size()];
+ int idx = 0;
+ for (Object o : coll) {
+ if (!(o instanceof Measure)) {
+ throw new IllegalArgumentException(obj.toString());
+ }
+ measures[idx++] = (Measure) o;
+ }
+ return formatMeasures(toAppendTo, pos, measures);
+ } else if (obj instanceof Measure[]) {
+ return formatMeasures(toAppendTo, pos, (Measure[]) obj);
+ } else if (obj instanceof Measure){
+ return this.formatMeasure((Measure) obj, toAppendTo, pos);
+ } else {
+ throw new IllegalArgumentException(obj.toString());
+ }
+ }
+
+ /**
+ * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
+ * @throws UnsupportedOperationException Not supported.
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public Measure parseObject(String source, ParsePosition pos) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ /**
+ * Formats a single measure and adds to appendable.
+ * If the fieldPosition argument identifies a field used by the format,
+ * then its indices are set to the beginning and end of the first such
+ * field encountered.
+ *
+ * @param measure the measure to format
+ * @param appendable the formatted string appended here.
+ * @param fieldPosition Identifies a field in the formatted text.
+ * @return appendable.
+ * @see MeasureFormat#formatMeasures(Measure...)
+ * @draft ICU 53
+ * @provisional
+ */
+ public <T extends Appendable> T formatMeasure(
+ Measure measure, T appendable, FieldPosition fieldPosition) {
+ Number n = measure.getNumber();
+ MeasureUnit unit = measure.getUnit();
+ UFieldPosition fpos = new UFieldPosition(fieldPosition.getFieldAttribute(), fieldPosition.getField());
+ StringBuffer formattedNumber = numberFormat.format(n, new StringBuffer(), fpos);
+ String keyword = rules.select(new PluralRules.FixedDecimal(n.doubleValue(), fpos.getCountVisibleFractionDigits(), fpos.getFractionDigits()));
+ Map<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
+ Map<String, PatternData> countToFormat = styleToCountToFormat.get(length);
+ PatternData messagePatternData = countToFormat.get(keyword);
+ try {
+ appendable.append(messagePatternData.prefix);
+ if (messagePatternData.suffix != null) { // there is a number (may not happen with, say, Arabic dual)
+ // Fix field position
+ if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
+ fieldPosition.setBeginIndex(fpos.getBeginIndex() + messagePatternData.prefix.length());
+ fieldPosition.setEndIndex(fpos.getEndIndex() + messagePatternData.prefix.length());
+ }
+ appendable.append(formattedNumber);
+ appendable.append(messagePatternData.suffix);
+ }
+ return appendable;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Formats a sequence of measures and adds to appendable.
+ * If the fieldPosition argument identifies a field used by the format,
+ * then its indices are set to the beginning and end of the first such
+ * field encountered.
+ *
+ * @param appendable the formatted string appended here.
+ * @param fieldPosition Identifies a field in the formatted text.
+ * @param measures the measures to format.
+ * @return appendable.
+ * @see MeasureFormat#formatMeasures(Measure...)
+ * @draft ICU 53
+ * @provisional
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends Appendable> T formatMeasures(
+ T appendable, FieldPosition fieldPosition, Measure... measures) {
+
+ // Zero out our field position so that we can tell when we find our field.
+ FieldPosition fpos = new FieldPosition(fieldPosition.getFieldAttribute(), fieldPosition.getField());
+ FieldPosition dummyPos = new FieldPosition(0);
+
+ int fieldPositionFoundIndex = -1;
+ StringBuilder[] results = new StringBuilder[measures.length];
+ for (int i = 0; i < measures.length; ++i) {
+ if (fieldPositionFoundIndex == -1) {
+ results[i] = formatMeasure(measures[i], new StringBuilder(), fpos);
+ if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
+ fieldPositionFoundIndex = i;
+ }
+ } else {
+ results[i] = formatMeasure(measures[i], new StringBuilder(), dummyPos);
+ }
+ }
+ ListFormatter listFormatter = ListFormatter.getInstance(locale,
+ length == FormatWidth.WIDE ? ListFormatter.Style.DURATION : ListFormatter.Style.DURATION_SHORT);
+
+ // Fix up FieldPosition indexes if our field is found.
+ if (fieldPositionFoundIndex != -1) {
+ String listPattern = listFormatter.getPatternForNumItems(measures.length);
+ int positionInPattern = listPattern.indexOf("{" + fieldPositionFoundIndex + "}");
+ if (positionInPattern == -1) {
+ throw new IllegalStateException("Can't find position with ListFormatter.");
+ }
+ // Now we have to adjust our position in pattern
+ // based on the previous values.
+ for (int i = 0; i < fieldPositionFoundIndex; i++) {
+ positionInPattern += (results[i].length() - ("{" + i + "}").length());
+ }
+ fieldPosition.setBeginIndex(fpos.getBeginIndex() + positionInPattern);
+ fieldPosition.setEndIndex(fpos.getEndIndex() + positionInPattern);
+ }
+
+ // This is safe because appendable is of type T.
+ try {
+ return (T) appendable.append(listFormatter.format((Object[]) results));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * For two MeasureFormat objects, a and b, to be equal, <code>a.getClass().equals(b.getClass())</code>
+ * <code>a.equalsSameClass(b)</code> must be true.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public final boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof MeasureFormat)) {
+ return false;
+ }
+ if (!getClass().equals(other.getClass())) {
+ return false;
+ }
+ return equalsSameClass((MeasureFormat) other);
+ }
+
/**
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public int hashCode() {
+ return (numberFormat.hashCode() * 31 + locale.hashCode()) * 31 + length.hashCode();
+ }
+
+ /**
+ * Returns true if this object is equal to other. The class of this and the class of other
+ * are guaranteed to be equal.
+ *
+ * @deprecated For ICU internal use only.
* @internal
- * @deprecated This API is ICU internal only.
*/
- protected MeasureFormat() {}
+ protected boolean equalsSameClass(MeasureFormat other) {
+ return objEquals(numberFormat,other.numberFormat)
+ && objEquals(locale, other.locale) && objEquals(length, other.length);
+ }
+
+
+ /**
+ * Get the format width this instance is using.
+ * @draft ICU 53
+ * @provisional
+ */
+ public MeasureFormat.FormatWidth getWidth() {
+ return length;
+ }
+
+ /**
+ * Get the locale of this instance.
+ * @draft ICU 53
+ * @provisional
+ */
+ public ULocale getLocale() {
+ return locale;
+ }
+ /**
+ * Get a copy of the number format.
+ * @draft ICU 53
+ * @provisional
+ */
+ public NumberFormat getNumberFormat() {
+ synchronized (numberFormat) {
+ return (NumberFormat) numberFormat.clone();
+ }
+ }
+
/**
* Return a formatter for CurrencyAmount objects in the given
* locale.
public static MeasureFormat getCurrencyFormat() {
return getCurrencyFormat(ULocale.getDefault(Category.FORMAT));
}
+
+ MeasureFormat withLocale(ULocale locale) {
+ return MeasureFormat.getInstance(locale, getWidth());
+ }
+
+ MeasureFormat withNumberFormat(NumberFormat format) {
+ return new MeasureFormat(
+ this.locale,
+ this.length,
+ (NumberFormat) format.clone(),
+ this.rules,
+ this.unitToStyleToCountToFormat);
+ }
+
+ private MeasureFormat(
+ ULocale locale,
+ FormatWidth width,
+ NumberFormat format,
+ PluralRules rules,
+ Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat) {
+ this.locale = locale;
+ this.length = width;
+ this.numberFormat = format;
+ this.rules = rules;
+ this.unitToStyleToCountToFormat = unitToStyleToCountToFormat;
+ }
+
+ /**
+ * For backward compatibility only.
+ * @internal
+ * @deprecated
+ */
+ protected MeasureFormat() {
+ // Make compiler happy by setting final fields to null.
+ this.length = null;
+ this.locale = null;
+ this.numberFormat = null;
+ this.rules = null;
+ this.unitToStyleToCountToFormat = null;
+
+ }
+
+ /**
+ * Returns formatting data for all MeasureUnits except for currency ones.
+ */
+ private static Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> loadLocaleData(
+ ULocale locale, PluralRules rules) {
+ Set<String> keywords = rules.getKeywords();
+ Map<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>> unitToStyleToCountToFormat
+ = new HashMap<MeasureUnit, EnumMap<FormatWidth, Map<String, PatternData>>>();
+ ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
+ for (MeasureUnit unit : MeasureUnit.getAvailable()) {
+ // Currency data cannot be found here. Skip.
+ if (unit instanceof Currency) {
+ continue;
+ }
+ EnumMap<FormatWidth, Map<String, PatternData>> styleToCountToFormat = unitToStyleToCountToFormat.get(unit);
+ if (styleToCountToFormat == null) {
+ unitToStyleToCountToFormat.put(unit, styleToCountToFormat = new EnumMap<FormatWidth, Map<String, PatternData>>(FormatWidth.class));
+ }
+ for (FormatWidth styleItem : FormatWidth.values()) {
+ try {
+ ICUResourceBundle unitTypeRes = resource.getWithFallback(styleItem.resourceKey);
+ ICUResourceBundle unitsRes = unitTypeRes.getWithFallback(unit.getType());
+ ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(unit.getSubtype());
+ Map<String, PatternData> countToFormat = styleToCountToFormat.get(styleItem);
+ if (countToFormat == null) {
+ styleToCountToFormat.put(styleItem, countToFormat = new HashMap<String, PatternData>());
+ }
+ // TODO(rocketman): Seems like we should be iterating over the bundles in
+ // oneUnitRes instead of all the plural key words since most languages have
+ // just 1 or 2 forms.
+ 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;
+ }
+
+ 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 static boolean objEquals(Object lhs, Object rhs) {
+ return lhs == null ? rhs == null : lhs.equals(rhs);
+
+ }
+
+ Object toTimeUnitProxy() {
+ return new MeasureProxy(locale, length, numberFormat, TIME_UNIT_FORMAT);
+ }
+
+ Object toCurrencyProxy() {
+ return new MeasureProxy(locale, length, numberFormat, CURRENCY_FORMAT);
+ }
+
+ private Object writeReplace() throws ObjectStreamException {
+ return new MeasureProxy(
+ locale, length, numberFormat, MEASURE_FORMAT);
+ }
+
+ static class MeasureProxy implements Externalizable {
+ private static final long serialVersionUID = -6033308329886716770L;
+
+ private ULocale locale;
+ private FormatWidth length;
+ private NumberFormat numberFormat;
+ private int subClass;
+ private HashMap<Object, Object> keyValues;
+
+ public MeasureProxy(
+ ULocale locale,
+ FormatWidth length,
+ NumberFormat numberFormat,
+ int subClass) {
+ this.locale = locale;
+ this.length = length;
+ this.numberFormat = numberFormat;
+ this.subClass = subClass;
+ this.keyValues = new HashMap<Object, Object>();
+ }
+
+ // Must have public constructor, to enable Externalizable
+ public MeasureProxy() {
+ }
+
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeByte(0); // version
+ out.writeObject(locale);
+ out.writeObject(length);
+ out.writeObject(numberFormat);
+ out.writeByte(subClass);
+ out.writeObject(keyValues);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ in.readByte(); // version.
+ locale = (ULocale) in.readObject();
+ if (locale == null) {
+ throw new InvalidObjectException("Missing locale.");
+ }
+ length = (FormatWidth) in.readObject();
+ if (length == null) {
+ throw new InvalidObjectException("Missing width.");
+ }
+ numberFormat = (NumberFormat) in.readObject();
+ if (numberFormat == null) {
+ 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();
+ if (keyValues == null) {
+ throw new InvalidObjectException("Missing optional values map.");
+ }
+ }
+
+ private TimeUnitFormat createTimeUnitFormat() throws InvalidObjectException {
+ int style;
+ if (length == FormatWidth.WIDE) {
+ style = TimeUnitFormat.FULL_NAME;
+ } else if (length == FormatWidth.SHORT) {
+ style = TimeUnitFormat.ABBREVIATED_NAME;
+ } else {
+ throw new InvalidObjectException("Bad width: " + length);
+ }
+ TimeUnitFormat result = new TimeUnitFormat(locale, style);
+ result.setNumberFormat(numberFormat);
+ return result;
+ }
+
+ private Object readResolve() throws ObjectStreamException {
+ switch (subClass) {
+ case MEASURE_FORMAT:
+ return MeasureFormat.getInstance(locale, length, numberFormat);
+ case TIME_UNIT_FORMAT:
+ return createTimeUnitFormat();
+ case CURRENCY_FORMAT:
+ return new CurrencyFormat(locale);
+ default:
+ throw new InvalidObjectException("Unknown subclass: " + subClass);
+ }
+ }
+ }
}
*/
package com.ibm.icu.text;
+import java.io.ObjectStreamException;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.TreeMap;
import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.util.Measure;
import com.ibm.icu.util.TimeUnit;
import com.ibm.icu.util.TimeUnitAmount;
import com.ibm.icu.util.ULocale;
private static final long serialVersionUID = -3707773153184971529L;
+ // These fields are supposed to be the same as the fields in mf. They
+ // are here for serialization backward compatibility and to support parsing.
+ private NumberFormat format;
+ private ULocale locale;
+ private int style;
+
+ // We use this field in lieu of the super class because the super class
+ // is immutable while this class is mutable. The contents of the super class
+ // is an empty shell. Every public method of the super class is overridden to
+ // delegate to this field. Each time this object mutates, it replaces this field with
+ // a new immutable instance.
+ private transient MeasureFormat mf;
+
+ private transient Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
+ private transient PluralRules pluralRules;
+ private transient boolean isReady;
+
private static final String DEFAULT_PATTERN_FOR_SECOND = "{0} s";
private static final String DEFAULT_PATTERN_FOR_MINUTE = "{0} min";
private static final String DEFAULT_PATTERN_FOR_HOUR = "{0} h";
private static final String DEFAULT_PATTERN_FOR_MONTH = "{0} m";
private static final String DEFAULT_PATTERN_FOR_YEAR = "{0} y";
- private NumberFormat format;
- private ULocale locale;
- private transient Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
- private transient PluralRules pluralRules;
- private transient boolean isReady;
- private int style;
-
/**
* Create empty format using full name style, for example, "hours".
* Use setLocale and/or setFormat to modify.
* @stable ICU 4.0
*/
public TimeUnitFormat() {
+ mf = MeasureFormat.getInstance(ULocale.getDefault(), FormatWidth.WIDE);
isReady = false;
style = FULL_NAME;
-
}
/**
if (style < FULL_NAME || style >= TOTAL_STYLES) {
throw new IllegalArgumentException("style should be either FULL_NAME or ABBREVIATED_NAME style");
}
+ mf = MeasureFormat.getInstance(
+ locale, style == FULL_NAME ? FormatWidth.WIDE : FormatWidth.SHORT);
this.style = style;
this.locale = locale;
isReady = false;
}
+
+ private TimeUnitFormat(ULocale locale, int style, NumberFormat numberFormat) {
+ this(locale, style);
+ setNumberFormat(numberFormat);
+ }
/**
* Create TimeUnitFormat given a Locale and a formatting style.
* @stable ICU 4.0
*/
public TimeUnitFormat setLocale(ULocale locale) {
- if ( locale != this.locale ) {
+ if (locale != this.locale){
+ mf = mf.withLocale(locale);
this.locale = locale;
isReady = false;
}
if (format == this.format) {
return this;
}
- if ( format == null ) {
- if ( locale == null ) {
+ if (format == null) {
+ if (locale == null) {
isReady = false;
- return this;
+ mf = mf.withLocale(ULocale.getDefault());
} else {
this.format = NumberFormat.getNumberInstance(locale);
+ mf = mf.withNumberFormat(this.format);
}
} else {
this.format = format;
+ mf = mf.withNumberFormat(this.format);
}
return this;
}
*/
public StringBuffer format(Object obj, StringBuffer toAppendTo,
FieldPosition pos) {
- if ( !(obj instanceof TimeUnitAmount) ) {
- throw new IllegalArgumentException(
- "cannot format a non TimeUnitAmount object");
- }
- if (!isReady) {
- setup();
- }
- TimeUnitAmount amount = (TimeUnitAmount) obj;
- Map<String, Object[]> countToPattern = timeUnitToCountToPatterns.get(amount.getTimeUnit());
- String formattedNumber = format.format(amount.getNumber());
- double doubleNumber = amount.getNumber().doubleValue();
- String count;
- if (format instanceof DecimalFormat) {
- count = pluralRules.select(((DecimalFormat) format).getFixedDecimal(doubleNumber));
- } else {
- count = pluralRules.select(doubleNumber);
- }
- MessageFormat pattern = (MessageFormat)(countToPattern.get(count))[style];
- return pattern.format(new Object[]{formattedNumber}, toAppendTo, pos);
+ return mf.format(obj, toAppendTo, pos);
}
/**
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
* @stable ICU 4.0
*/
- public Object parseObject(String source, ParsePosition pos) {
+ @Override
+ public TimeUnitAmount parseObject(String source, ParsePosition pos) {
if (!isReady) {
setup();
}
}
}
-
- /*
- * Initialize locale, number formatter, plural rules, and
- * time units patterns.
- * Initially, we are storing all of these as MessageFormats.
- * I think it might actually be simpler to make them Decimal Formats later.
- */
private void setup() {
if (locale == null) {
if (format != null) {
}
private void setup(String resourceKey, Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
- int style, Set<String> pluralKeywords) {
- // fill timeUnitToCountToPatterns from resource file
- try {
- ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
- ICUResourceBundle unitsRes = resource.getWithFallback(resourceKey);
- int size = unitsRes.getSize();
- for ( int index = 0; index < size; ++index) {
- String timeUnitName = unitsRes.get(index).getKey();
- TimeUnit timeUnit = null;
- if ( timeUnitName.equals("year") ) {
- timeUnit = TimeUnit.YEAR;
- } else if ( timeUnitName.equals("month") ) {
- timeUnit = TimeUnit.MONTH;
- } else if ( timeUnitName.equals("day") ) {
- timeUnit = TimeUnit.DAY;
- } else if ( timeUnitName.equals("hour") ) {
- timeUnit = TimeUnit.HOUR;
- } else if ( timeUnitName.equals("minute") ) {
- timeUnit = TimeUnit.MINUTE;
- } else if ( timeUnitName.equals("second") ) {
- timeUnit = TimeUnit.SECOND;
- } else if ( timeUnitName.equals("week") ) {
- timeUnit = TimeUnit.WEEK;
- } else {
- continue;
- }
- ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(timeUnitName);
- int count = oneUnitRes.getSize();
- Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
- if (countToPatterns == null) {
- countToPatterns = new TreeMap<String, Object[]>();
- timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
- }
- for ( int pluralIndex = 0; pluralIndex < count; ++pluralIndex) {
- String pluralCount = oneUnitRes.get(pluralIndex).getKey();
- if (!pluralKeywords.contains(pluralCount))
- continue;
- String pattern = oneUnitRes.get(pluralIndex).getString();
- final MessageFormat messageFormat = new MessageFormat(pattern, locale);
- // save both full name and abbreviated name in one table
- // is good space-wise, but it degrades performance,
- // since it needs to check whether the needed space
- // is already allocated or not.
- Object[] pair = countToPatterns.get(pluralCount);
- if (pair == null) {
- pair = new Object[2];
- countToPatterns.put(pluralCount, pair);
- }
- pair[style] = messageFormat;
- }
- }
- } catch ( MissingResourceException e ) {
- }
-
- // there should be patterns for each plural rule in each time unit.
- // For each time unit,
- // for each plural rule, following is unit pattern fall-back rule:
- // ( for example: "one" hour )
- // look for its unit pattern in its locale tree.
- // if pattern is not found in its own locale, such as de_DE,
- // look for the pattern in its parent, such as de,
- // keep looking till found or till root.
- // if the pattern is not found in root either,
- // fallback to plural count "other",
- // look for the pattern of "other" in the locale tree:
- // "de_DE" to "de" to "root".
- // If not found, fall back to value of
- // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
- //
- // Following is consistency check to create pattern for each
- // plural rule in each time unit using above fall-back rule.
- //
- final TimeUnit[] timeUnits = TimeUnit.values();
- Set<String> keywords = pluralRules.getKeywords();
- for ( int i = 0; i < timeUnits.length; ++i ) {
- // for each time unit,
- // get all the patterns for each plural rule in this locale.
- final TimeUnit timeUnit = timeUnits[i];
- Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
- if (countToPatterns == null) {
- countToPatterns = new TreeMap<String, Object[]>();
- timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
- }
- for (String pluralCount : keywords) {
- if ( countToPatterns.get(pluralCount) == null ||
- countToPatterns.get(pluralCount)[style] == null ) {
- // look through parents
- searchInTree(resourceKey, style, timeUnit, pluralCount, pluralCount, countToPatterns);
- }
- }
- }
+ int style, Set<String> pluralKeywords) {
+// fill timeUnitToCountToPatterns from resource file
+try {
+ ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
+ ICUResourceBundle unitsRes = resource.getWithFallback(resourceKey);
+ int size = unitsRes.getSize();
+ for ( int index = 0; index < size; ++index) {
+ String timeUnitName = unitsRes.get(index).getKey();
+ TimeUnit timeUnit = null;
+ if ( timeUnitName.equals("year") ) {
+ timeUnit = TimeUnit.YEAR;
+ } else if ( timeUnitName.equals("month") ) {
+ timeUnit = TimeUnit.MONTH;
+ } else if ( timeUnitName.equals("day") ) {
+ timeUnit = TimeUnit.DAY;
+ } else if ( timeUnitName.equals("hour") ) {
+ timeUnit = TimeUnit.HOUR;
+ } else if ( timeUnitName.equals("minute") ) {
+ timeUnit = TimeUnit.MINUTE;
+ } else if ( timeUnitName.equals("second") ) {
+ timeUnit = TimeUnit.SECOND;
+ } else if ( timeUnitName.equals("week") ) {
+ timeUnit = TimeUnit.WEEK;
+ } else {
+ continue;
+ }
+ ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(timeUnitName);
+ int count = oneUnitRes.getSize();
+ Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
+ if (countToPatterns == null) {
+ countToPatterns = new TreeMap<String, Object[]>();
+ timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
+ }
+ for ( int pluralIndex = 0; pluralIndex < count; ++pluralIndex) {
+ String pluralCount = oneUnitRes.get(pluralIndex).getKey();
+ if (!pluralKeywords.contains(pluralCount))
+ continue;
+ String pattern = oneUnitRes.get(pluralIndex).getString();
+ final MessageFormat messageFormat = new MessageFormat(pattern, locale);
+ // save both full name and abbreviated name in one table
+ // is good space-wise, but it degrades performance,
+ // since it needs to check whether the needed space
+ // is already allocated or not.
+ Object[] pair = countToPatterns.get(pluralCount);
+ if (pair == null) {
+ pair = new Object[2];
+ countToPatterns.put(pluralCount, pair);
+ }
+ pair[style] = messageFormat;
+ }
+ }
+} catch ( MissingResourceException e ) {
+}
+// there should be patterns for each plural rule in each time unit.
+// For each time unit,
+// for each plural rule, following is unit pattern fall-back rule:
+// ( for example: "one" hour )
+// look for its unit pattern in its locale tree.
+// if pattern is not found in its own locale, such as de_DE,
+// look for the pattern in its parent, such as de,
+// keep looking till found or till root.
+// if the pattern is not found in root either,
+// fallback to plural count "other",
+// look for the pattern of "other" in the locale tree:
+// "de_DE" to "de" to "root".
+// If not found, fall back to value of
+// static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
+//
+// Following is consistency check to create pattern for each
+// plural rule in each time unit using above fall-back rule.
+//
+final TimeUnit[] timeUnits = TimeUnit.values();
+Set<String> keywords = pluralRules.getKeywords();
+for ( int i = 0; i < timeUnits.length; ++i ) {
+ // for each time unit,
+ // get all the patterns for each plural rule in this locale.
+ final TimeUnit timeUnit = timeUnits[i];
+ Map<String, Object[]> countToPatterns = timeUnitToCountToPatterns.get(timeUnit);
+ if (countToPatterns == null) {
+ countToPatterns = new TreeMap<String, Object[]>();
+ timeUnitToCountToPatterns.put(timeUnit, countToPatterns);
+ }
+ for (String pluralCount : keywords) {
+ if ( countToPatterns.get(pluralCount) == null ||
+ countToPatterns.get(pluralCount)[style] == null ) {
+ // look through parents
+ searchInTree(resourceKey, style, timeUnit, pluralCount, pluralCount, countToPatterns);
+ }
+ }
+}
+}
+// srcPluralCount is the original plural count on which the pattern is
+// searched for.
+// searchPluralCount is the fallback plural count.
+// For example, to search for pattern for ""one" hour",
+// "one" is the srcPluralCount,
+// if the pattern is not found even in root, fallback to
+// using patterns of plural count "other",
+// then, "other" is the searchPluralCount.
+private void searchInTree(String resourceKey, int styl,
+ TimeUnit timeUnit, String srcPluralCount,
+ String searchPluralCount, Map<String, Object[]> countToPatterns) {
+ULocale parentLocale=locale;
+String srcTimeUnitName = timeUnit.toString();
+while ( parentLocale != null ) {
+ try {
+ // look for pattern for srcPluralCount in locale tree
+ ICUResourceBundle unitsRes = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, parentLocale);
+ unitsRes = unitsRes.getWithFallback(resourceKey);
+ ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(srcTimeUnitName);
+ String pattern = oneUnitRes.getStringWithFallback(searchPluralCount);
+ final MessageFormat messageFormat = new MessageFormat(pattern, locale);
+ Object[] pair = countToPatterns.get(srcPluralCount);
+ if (pair == null) {
+ pair = new Object[2];
+ countToPatterns.put(srcPluralCount, pair);
+ }
+ pair[styl] = messageFormat;
+ return;
+ } catch ( MissingResourceException e ) {
+ }
+ parentLocale=parentLocale.getFallback();
+}
+// if no unitsShort resource was found even after fallback to root locale
+// then search the units resource fallback from the current level to root
+if ( parentLocale == null && resourceKey.equals("unitsShort") ) {
+ searchInTree("units", styl, timeUnit, srcPluralCount, searchPluralCount, countToPatterns);
+ if ( countToPatterns != null &&
+ countToPatterns.get(srcPluralCount) != null &&
+ countToPatterns.get(srcPluralCount)[styl] != null ) {
+ return;
+ }
+}
+// if not found the pattern for this plural count at all,
+// fall-back to plural count "other"
+if ( searchPluralCount.equals("other") ) {
+ // set default fall back the same as the resource in root
+ MessageFormat messageFormat = null;
+ if ( timeUnit == TimeUnit.SECOND ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_SECOND, locale);
+ } else if ( timeUnit == TimeUnit.MINUTE ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MINUTE, locale);
+ } else if ( timeUnit == TimeUnit.HOUR ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_HOUR, locale);
+ } else if ( timeUnit == TimeUnit.WEEK ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_WEEK, locale);
+ } else if ( timeUnit == TimeUnit.DAY ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_DAY, locale);
+ } else if ( timeUnit == TimeUnit.MONTH ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MONTH, locale);
+ } else if ( timeUnit == TimeUnit.YEAR ) {
+ messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_YEAR, locale);
+ }
+ Object[] pair = countToPatterns.get(srcPluralCount);
+ if (pair == null) {
+ pair = new Object[2];
+ countToPatterns.put(srcPluralCount, pair);
+ }
+ pair[styl] = messageFormat;
+} else {
+ // fall back to rule "other", and search in parents
+ searchInTree(resourceKey, styl, timeUnit, srcPluralCount, "other", countToPatterns);
+}
+}
+
+ // boilerplate code to make TimeUnitFormat otherwise follow the contract of
+ // MeasureFormat
+
+ @Override
+ public String formatMeasures(Measure... measures) {
+ return mf.formatMeasures(measures);
}
-
-
-
- // srcPluralCount is the original plural count on which the pattern is
- // searched for.
- // searchPluralCount is the fallback plural count.
- // For example, to search for pattern for ""one" hour",
- // "one" is the srcPluralCount,
- // if the pattern is not found even in root, fallback to
- // using patterns of plural count "other",
- // then, "other" is the searchPluralCount.
- private void searchInTree(String resourceKey, int styl,
- TimeUnit timeUnit, String srcPluralCount,
- String searchPluralCount, Map<String, Object[]> countToPatterns) {
- ULocale parentLocale=locale;
- String srcTimeUnitName = timeUnit.toString();
- while ( parentLocale != null ) {
- try {
- // look for pattern for srcPluralCount in locale tree
- ICUResourceBundle unitsRes = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, parentLocale);
- unitsRes = unitsRes.getWithFallback(resourceKey);
- ICUResourceBundle oneUnitRes = unitsRes.getWithFallback(srcTimeUnitName);
- String pattern = oneUnitRes.getStringWithFallback(searchPluralCount);
- final MessageFormat messageFormat = new MessageFormat(pattern, locale);
- Object[] pair = countToPatterns.get(srcPluralCount);
- if (pair == null) {
- pair = new Object[2];
- countToPatterns.put(srcPluralCount, pair);
- }
- pair[styl] = messageFormat;
- return;
- } catch ( MissingResourceException e ) {
- }
- parentLocale=parentLocale.getFallback();
- }
-
- // if no unitsShort resource was found even after fallback to root locale
- // then search the units resource fallback from the current level to root
- if ( parentLocale == null && resourceKey.equals("unitsShort") ) {
- searchInTree("units", styl, timeUnit, srcPluralCount, searchPluralCount, countToPatterns);
- if ( countToPatterns != null &&
- countToPatterns.get(srcPluralCount) != null &&
- countToPatterns.get(srcPluralCount)[styl] != null ) {
- return;
- }
- }
-
- // if not found the pattern for this plural count at all,
- // fall-back to plural count "other"
- if ( searchPluralCount.equals("other") ) {
- // set default fall back the same as the resource in root
- MessageFormat messageFormat = null;
- if ( timeUnit == TimeUnit.SECOND ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_SECOND, locale);
- } else if ( timeUnit == TimeUnit.MINUTE ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MINUTE, locale);
- } else if ( timeUnit == TimeUnit.HOUR ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_HOUR, locale);
- } else if ( timeUnit == TimeUnit.WEEK ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_WEEK, locale);
- } else if ( timeUnit == TimeUnit.DAY ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_DAY, locale);
- } else if ( timeUnit == TimeUnit.MONTH ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_MONTH, locale);
- } else if ( timeUnit == TimeUnit.YEAR ) {
- messageFormat = new MessageFormat(DEFAULT_PATTERN_FOR_YEAR, locale);
- }
- Object[] pair = countToPatterns.get(srcPluralCount);
- if (pair == null) {
- pair = new Object[2];
- countToPatterns.put(srcPluralCount, pair);
- }
- pair[styl] = messageFormat;
- } else {
- // fall back to rule "other", and search in parents
- searchInTree(resourceKey, styl, timeUnit, srcPluralCount, "other", countToPatterns);
- }
+
+ @Override
+ public <T extends Appendable> T formatMeasure(
+ Measure measure, T appendable, FieldPosition fieldPosition) {
+ return mf.formatMeasure(measure, appendable, fieldPosition);
+ }
+
+ @Override
+ public <T extends Appendable> T formatMeasures(
+ T appendable, FieldPosition fieldPosition, Measure... measures) {
+ return mf.formatMeasures(appendable, fieldPosition, measures);
+ }
+
+ @Override
+ public MeasureFormat.FormatWidth getWidth() {
+ return mf.getWidth();
+ }
+
+ @Override
+ public ULocale getLocale() {
+ return mf.getLocale();
+ }
+
+ @Override
+ public NumberFormat getNumberFormat() {
+ return mf.getNumberFormat();
+ }
+
+ // End boilerplate.
+
+ // equals / hashcode
+
+ @Override
+ public int hashCode() {
+ return getLocale().hashCode() + 911247101;
+ }
+
+ @Override
+ protected boolean equalsSameClass(MeasureFormat other) {
+ return getLocale().equals(other.getLocale());
+ }
+
+ // End equals / hashcode
+
+ // Serialization
+
+ private Object writeReplace() throws ObjectStreamException {
+ return mf.toTimeUnitProxy();
+ }
+
+ // Preserve backward serialize backward compatibility.
+ private Object readResolve() throws ObjectStreamException {
+ return new TimeUnitFormat(locale, style, format);
}
}
package com.ibm.icu.util;
import java.io.ObjectStreamException;
-import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.text.ParsePosition;
import java.util.ArrayList;
* @author Alan Liu
* @stable ICU 2.2
*/
-public class Currency extends MeasureUnit implements Serializable {
+public class Currency extends MeasureUnit {
private static final long serialVersionUID = -5839973855554750484L;
private static final boolean DEBUG = ICUDebug.enabled("currency");
throw new IllegalArgumentException(
"The input currency code is not 3-letter alphabetic code.");
}
- return (Currency) MeasureUnit.addUnit("currency", theISOCode.toUpperCase(Locale.ENGLISH), CURRENCY_FACTORY);
+ return (Currency) MeasureUnit.internalGetInstance("currency", theISOCode.toUpperCase(Locale.ENGLISH));
}
// isoCode is kept for readResolve() and Currency class no longer
// use it. So this statement actually does not have any effect.
- isoCode = code;
+ isoCode = theISOCode;
}
// POW10[i] = 10^i
private final String isoCode;
private Object readResolve() throws ObjectStreamException {
+ // The old isoCode field used to determine the currency.
return Currency.getInstance(isoCode);
}
}
+++ /dev/null
-/*
- *******************************************************************************
- * Copyright (C) 2013, International Business Machines Corporation and *
- * others. All Rights Reserved. *
- *******************************************************************************
- */
-package com.ibm.icu.util;
-
-/**
- * General purpose formatting width enum.
- * @internal
- * @deprecated This API is ICU internal only.
- */
-public enum FormatWidth {
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- WIDE("units"),
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- SHORT("unitsShort"),
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- NARROW("unitsNarrow");
-
- /**
- * @internal
- * @deprecated internal use
- */
- public final String resourceKey;
-
- private FormatWidth(String resourceKey) {
- this.resourceKey = resourceKey;
- }
-}
\ No newline at end of file
/**
* An amount of a specified unit, consisting of a Number and a Unit.
* For example, a length measure consists of a Number and a length
- * unit, such as feet or meters. This is an abstract class.
- * Subclasses specify a concrete Unit type.
+ * unit, such as feet or meters.
*
* <p>Measure objects are parsed and formatted by subclasses of
* MeasureFormat.
*
* <p>Measure objects are immutable. All subclasses must guarantee that.
+ * (However, subclassing is discouraged.)
*
* @see java.lang.Number
* @see com.ibm.icu.util.MeasureUnit
public class Measure {
private final Number number;
-
private final MeasureUnit unit;
/**
* @stable ICU 3.0
*/
public boolean equals(Object obj) {
- if (obj == null) return false;
- if (obj == this) return true;
- try {
- Measure m = (Measure) obj;
- return unit.equals(m.unit) && numbersEqual(number, m.number);
- } catch (ClassCastException e) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Measure)) {
return false;
}
+ Measure m = (Measure) obj;
+ return unit.equals(m.unit) && numbersEqual(number, m.number);
}
/*
* @stable ICU 3.0
*/
public int hashCode() {
- return number.hashCode() ^ unit.hashCode();
+ return 31 * Double.valueOf(number.doubleValue()).hashCode() + unit.hashCode();
}
/**
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.
+ * coupled with a numeric amount to produce a Measure. MeasureUnit objects are immutable.
+ * All subclasses must guarantee that. (However, subclassing is discouraged.)
+
*
* @see com.ibm.icu.util.Measure
* @author Alan Liu
* @stable ICU 3.0
*/
-public class MeasureUnit implements Comparable<MeasureUnit>, Serializable {
+public class MeasureUnit implements Serializable {
private static final long serialVersionUID = -1839973855554750484L;
-
+
+ // Used to pre-fill the cache. These same constants appear in MeasureFormat too.
+ private static final String[] unitKeys = new String[]{"units", "unitsShort", "unitsNarrow"};
+
private static final Map<String, Map<String,MeasureUnit>> cache
= new HashMap<String, Map<String,MeasureUnit>>();
this.type = type;
this.code = code;
}
+
+ /**
+ * Get the type, such as "length"
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ public String getType() {
+ return type;
+ }
+
/**
- * Create an instance of a measurement unit.
- * <p>
- * Warning: Currently, the values of the parameters depend on the structure of
- * ICU resource bundles. Do not use this function unless you know what you are
- * doing.
+ * Get the subType, such as “foot”.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ public String getSubtype() {
+ return code;
+ }
+
+
+
+ /**
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public int hashCode() {
+ return 31 * type.hashCode() + code.hashCode();
+ }
+
+ /**
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (rhs == this) {
+ return true;
+ }
+ if (!(rhs instanceof MeasureUnit)) {
+ return false;
+ }
+ MeasureUnit c = (MeasureUnit) rhs;
+ return type.equals(c.type) && code.equals(c.code);
+ }
+
+ /**
+ * @draft ICU 53
+ * @provisional
+ */
+ @Override
+ public String toString() {
+ return type + "-" + code;
+ }
+
+ /**
+ * Get all of the available units' types.
*
- * @param type the type, such as "length"
- * @param code the code, such as "meter"
- * @return the unit.
+ * @draft ICU 53
+ * @provisional
+ */
+ public synchronized static Set<String> getAvailableTypes() {
+ return new HashSet<String>(cache.keySet());
+ }
+
+ /**
+ * For the given type, return the available units.
+ * @param type the type
+ * @return the available units for type
+ * @draft ICU 53
+ * @provisional
+ */
+ public synchronized static Collection<MeasureUnit> getAvailable(String type) {
+ Map<String, MeasureUnit> units = cache.get(type);
+ return units == null ? null : new ArrayList<MeasureUnit>(units.values());
+ }
+
+ /**
+ * Get all of the available units.
+ *
+ * @draft ICU 53
+ * @provisional
+ */
+ public synchronized static Set<MeasureUnit> getAvailable() {
+ Set<MeasureUnit> result = new HashSet<MeasureUnit>();
+ for (String type : new HashSet<String>(MeasureUnit.getAvailableTypes())) {
+ for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
+ result.add(unit);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Create a MeasureUnit instance (creates a singleton instance).
+ * <p>
+ * Normally this method should not be used, since there will be no formatting data
+ * available for it, and it may not be returned by getAvailable().
+ * However, for special purposes (such as CLDR tooling), it is available.
+ *
* @internal
* @deprecated This API is ICU internal only.
*/
- public static MeasureUnit getInstance(String type, String code) {
- synchronized (MeasureUnit.class){
- Map<String, MeasureUnit> tmp = cache.get(type);
- if (tmp != null) {
- MeasureUnit result = tmp.get(code);
- if (result != null) {
- return result;
- }
+ public static MeasureUnit internalGetInstance(String type, String code) {
+ if (type == null || code == null) {
+ throw new NullPointerException("Type and code must be non-null");
+ }
+ if (!"currency".equals(type)) {
+ if (!ASCII.containsAll(type) || !ASCII_HYPHEN.containsAll(code)) {
+ throw new IllegalArgumentException("The type or code are invalid.");
}
}
- if (type == null || !ASCII.containsAll(type) || code == null || !ASCII_HYPHEN.containsAll(code)) {
- throw new NullPointerException("The type or code are invalid.");
+ Factory factory;
+ if ("currency".equals(type)) {
+ factory = CURRENCY_FACTORY;
+ } else if ("duration".equals(type)) {
+ factory = TIMEUNIT_FACTORY;
+ } else {
+ factory = UNIT_FACTORY;
}
- return MeasureUnit.addUnit(type, code, UNIT_FACTORY);
+ return MeasureUnit.addUnit(type, code, factory);
}
static final UnicodeSet ASCII = new UnicodeSet('a', 'z').freeze();
};
static Factory CURRENCY_FACTORY = new Factory() {
- public MeasureUnit create(String type, String code) {
+ public MeasureUnit create(String unusedType, String code) {
return new Currency(code);
}
};
+
+ static Factory TIMEUNIT_FACTORY = new Factory() {
+ public MeasureUnit create(String type, String code) {
+ return new TimeUnit(type, code);
+ }
+ };
// /**
* }
*/
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "en");
- for (FormatWidth key : FormatWidth.values()) {
+ for (String key : unitKeys) {
try {
- ICUResourceBundle unitsTypeRes = resource.getWithFallback(key.resourceKey);
+ ICUResourceBundle unitsTypeRes = resource.getWithFallback(key);
int size = unitsTypeRes.getSize();
for ( int index = 0; index < size; ++index) {
UResourceBundle unitsRes = unitsTypeRes.get(index);
for ( int index2 = 0; index2 < unitsSize; ++index2) {
ICUResourceBundle unitNameRes = (ICUResourceBundle)unitsRes.get(index2);
if (unitNameRes.get("other") != null) {
- addUnit(type, unitNameRes.getKey(), UNIT_FACTORY);
+ internalGetInstance(type, unitNameRes.getKey());
}
}
}
ICUResourceBundle.ICU_DATA_CLASS_LOADER);
UResourceBundle codeMap = bundle.get("codeMap");
for (Enumeration<String> it = codeMap.getKeys(); it.hasMoreElements();) {
- MeasureUnit.addUnit("currency", it.nextElement(), CURRENCY_FACTORY);
+ MeasureUnit.internalGetInstance("currency", it.nextElement());
}
} catch (MissingResourceException e) {
// fall through
return unit;
}
- /**
- * Get all of the available general units' types.
- * @return available units
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public synchronized static Set<String> getAvailableTypes() {
- return new HashSet<String>(cache.keySet());
- }
+
- /**
- * Get all of the available general units for a given type.
- * @return available units
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public synchronized static Collection<MeasureUnit> getAvailable(String type) {
- Map<String, MeasureUnit> units = cache.get(type);
- return units == null ? null : new ArrayList<MeasureUnit>(units.values());
- }
-
- /**
- * Get all of the available general units.
- * @return available units
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public synchronized 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;
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Override
- public int hashCode() {
- return code.hashCode() ^ type.hashCode();
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @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;
- }
- }
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public int compareTo(MeasureUnit other) {
- int diff;
- return this == other ? 0
- : (diff = type.compareTo(other.type)) != 0 ? diff
- : code.compareTo(other.code);
- }
-
- /**
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Override
- public String toString() {
- return type + "-" + code;
- }
-
- /**
- * @return the type for this unit
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public String getType() {
- return type;
- }
-
- /**
- * @return the code for this unit.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- public String getCode() {
- return code;
- }
/**
* Useful constants. Not necessarily complete: see {@link #getAvailable()}.
- * @internal
- * @deprecated This API is ICU internal only.
+ * @draft ICU 53
+ * @provisional
*/
public static final MeasureUnit
/** Constant for unit of acceleration: g-force */
- G_FORCE = MeasureUnit.getInstance("acceleration", "g-force"),
+ G_FORCE = MeasureUnit.internalGetInstance("acceleration", "g-force"),
/** Constant for unit of angle: degree */
- DEGREE = MeasureUnit.getInstance("angle", "degree"),
+ DEGREE = MeasureUnit.internalGetInstance("angle", "degree"),
/** Constant for unit of angle: minute */
- ARC_MINUTE = MeasureUnit.getInstance("angle", "arc-minute"),
+ ARC_MINUTE = MeasureUnit.internalGetInstance("angle", "arc-minute"),
/** Constant for unit of angle: second */
- ARC_SECOND = MeasureUnit.getInstance("angle", "arc-second"),
+ ARC_SECOND = MeasureUnit.internalGetInstance("angle", "arc-second"),
/** Constant for unit of area: acre */
- ACRE = MeasureUnit.getInstance("area", "acre"),
+ ACRE = MeasureUnit.internalGetInstance("area", "acre"),
/** Constant for unit of area: hectare */
- HECTARE = MeasureUnit.getInstance("area", "hectare"),
+ HECTARE = MeasureUnit.internalGetInstance("area", "hectare"),
/** Constant for unit of area: square-foot */
- SQUARE_FOOT = MeasureUnit.getInstance("area", "square-foot"),
+ SQUARE_FOOT = MeasureUnit.internalGetInstance("area", "square-foot"),
/** Constant for unit of area: square-kilometer */
- SQUARE_KILOMETER = MeasureUnit.getInstance("area", "square-kilometer"),
+ SQUARE_KILOMETER = MeasureUnit.internalGetInstance("area", "square-kilometer"),
/** Constant for unit of area: square-meter */
- SQUARE_METER = MeasureUnit.getInstance("area", "square-meter"),
+ SQUARE_METER = MeasureUnit.internalGetInstance("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"),
+ SQUARE_MILE = MeasureUnit.internalGetInstance("area", "square-mile"),
/** 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"),
+ MILLISECOND = MeasureUnit.internalGetInstance("duration", "millisecond"),
/** Constant for unit of length: centimeter */
- CENTIMETER = MeasureUnit.getInstance("length", "centimeter"),
+ CENTIMETER = MeasureUnit.internalGetInstance("length", "centimeter"),
/** Constant for unit of length: foot */
- FOOT = MeasureUnit.getInstance("length", "foot"),
+ FOOT = MeasureUnit.internalGetInstance("length", "foot"),
/** Constant for unit of length: inch */
- INCH = MeasureUnit.getInstance("length", "inch"),
+ INCH = MeasureUnit.internalGetInstance("length", "inch"),
/** Constant for unit of length: kilometer */
- KILOMETER = MeasureUnit.getInstance("length", "kilometer"),
+ KILOMETER = MeasureUnit.internalGetInstance("length", "kilometer"),
/** Constant for unit of length: light-year */
- LIGHT_YEAR = MeasureUnit.getInstance("length", "light-year"),
+ LIGHT_YEAR = MeasureUnit.internalGetInstance("length", "light-year"),
/** Constant for unit of length: meter */
- METER = MeasureUnit.getInstance("length", "meter"),
+ METER = MeasureUnit.internalGetInstance("length", "meter"),
/** Constant for unit of length: mile */
- MILE = MeasureUnit.getInstance("length", "mile"),
+ MILE = MeasureUnit.internalGetInstance("length", "mile"),
/** Constant for unit of length: millimeter */
- MILLIMETER = MeasureUnit.getInstance("length", "millimeter"),
+ MILLIMETER = MeasureUnit.internalGetInstance("length", "millimeter"),
/** Constant for unit of length: picometer */
- PICOMETER = MeasureUnit.getInstance("length", "picometer"),
+ PICOMETER = MeasureUnit.internalGetInstance("length", "picometer"),
/** Constant for unit of length: yard */
- YARD = MeasureUnit.getInstance("length", "yard"),
+ YARD = MeasureUnit.internalGetInstance("length", "yard"),
/** Constant for unit of mass: gram */
- GRAM = MeasureUnit.getInstance("mass", "gram"),
+ GRAM = MeasureUnit.internalGetInstance("mass", "gram"),
/** Constant for unit of mass: kilogram */
- KILOGRAM = MeasureUnit.getInstance("mass", "kilogram"),
+ KILOGRAM = MeasureUnit.internalGetInstance("mass", "kilogram"),
/** Constant for unit of mass: ounce */
- OUNCE = MeasureUnit.getInstance("mass", "ounce"),
+ OUNCE = MeasureUnit.internalGetInstance("mass", "ounce"),
/** Constant for unit of mass: pound */
- POUND = MeasureUnit.getInstance("mass", "pound"),
+ POUND = MeasureUnit.internalGetInstance("mass", "pound"),
/** Constant for unit of power: horsepower */
- HORSEPOWER = MeasureUnit.getInstance("power", "horsepower"),
+ HORSEPOWER = MeasureUnit.internalGetInstance("power", "horsepower"),
/** Constant for unit of power: kilowatt */
- KILOWATT = MeasureUnit.getInstance("power", "kilowatt"),
+ KILOWATT = MeasureUnit.internalGetInstance("power", "kilowatt"),
/** Constant for unit of power: watt */
- WATT = MeasureUnit.getInstance("power", "watt"),
+ WATT = MeasureUnit.internalGetInstance("power", "watt"),
/** Constant for unit of pressure: hectopascal */
- HECTOPASCAL = MeasureUnit.getInstance("pressure", "hectopascal"),
+ HECTOPASCAL = MeasureUnit.internalGetInstance("pressure", "hectopascal"),
/** Constant for unit of pressure: inch-hg */
- INCH_HG = MeasureUnit.getInstance("pressure", "inch-hg"),
+ INCH_HG = MeasureUnit.internalGetInstance("pressure", "inch-hg"),
/** Constant for unit of pressure: millibar */
- MILLIBAR = MeasureUnit.getInstance("pressure", "millibar"),
+ MILLIBAR = MeasureUnit.internalGetInstance("pressure", "millibar"),
/** Constant for unit of speed: kilometer-per-hour */
- KILOMETER_PER_HOUR = MeasureUnit.getInstance("speed", "kilometer-per-hour"),
+ KILOMETER_PER_HOUR = MeasureUnit.internalGetInstance("speed", "kilometer-per-hour"),
/** Constant for unit of speed: meter-per-second */
- METER_PER_SECOND = MeasureUnit.getInstance("speed", "meter-per-second"),
+ METER_PER_SECOND = MeasureUnit.internalGetInstance("speed", "meter-per-second"),
/** Constant for unit of speed: mile-per-hour */
- MILE_PER_HOUR = MeasureUnit.getInstance("speed", "mile-per-hour"),
+ MILE_PER_HOUR = MeasureUnit.internalGetInstance("speed", "mile-per-hour"),
/** Constant for unit of temperature: celsius */
- CELSIUS = MeasureUnit.getInstance("temperature", "celsius"),
+ CELSIUS = MeasureUnit.internalGetInstance("temperature", "celsius"),
/** Constant for unit of temperature: fahrenheit */
- FAHRENHEIT = MeasureUnit.getInstance("temperature", "fahrenheit"),
+ FAHRENHEIT = MeasureUnit.internalGetInstance("temperature", "fahrenheit"),
/** Constant for unit of volume: cubic-kilometer */
- CUBIC_KILOMETER = MeasureUnit.getInstance("volume", "cubic-kilometer"),
+ CUBIC_KILOMETER = MeasureUnit.internalGetInstance("volume", "cubic-kilometer"),
/** Constant for unit of volume: cubic-mile */
- CUBIC_MILE = MeasureUnit.getInstance("volume", "cubic-mile"),
+ CUBIC_MILE = MeasureUnit.internalGetInstance("volume", "cubic-mile"),
/** Constant for unit of volume: liter */
- LITER = MeasureUnit.getInstance("volume", "liter");
+ LITER = MeasureUnit.internalGetInstance("volume", "liter");
+
+ public static TimeUnit
+ /** Constant for unit of duration: year */
+ YEAR = (TimeUnit) MeasureUnit.internalGetInstance("duration", "year"),
+ /** Constant for unit of duration: month */
+ MONTH = (TimeUnit) MeasureUnit.internalGetInstance("duration", "month"),
+ /** Constant for unit of duration: week */
+ WEEK = (TimeUnit) MeasureUnit.internalGetInstance("duration", "week"),
+ /** Constant for unit of duration: day */
+ DAY = (TimeUnit) MeasureUnit.internalGetInstance("duration", "day"),
+ /** Constant for unit of duration: hour */
+ HOUR = (TimeUnit) MeasureUnit.internalGetInstance("duration", "hour"),
+ /** Constant for unit of duration: minute */
+ MINUTE = (TimeUnit) MeasureUnit.internalGetInstance("duration", "minute"),
+ /** Constant for unit of duration: second */
+ SECOND = (TimeUnit) MeasureUnit.internalGetInstance("duration", "second");
/** Private **/
}
private Object readResolve() throws ObjectStreamException {
- return "currency".equals(type)
- ? Currency.getInstance(code)
- : MeasureUnit.getInstance(type, code);
+ return MeasureUnit.internalGetInstance(type, code);
}
}
}
*/
package com.ibm.icu.util;
+import java.io.InvalidObjectException;
+import java.io.ObjectStreamException;
+
/**
* Measurement unit for time units.
*/
public class TimeUnit extends MeasureUnit {
private static final long serialVersionUID = -2839973855554750484L;
-
- /**
- * Supports selected time duration units
- */
- private final int index;
-
- // Total number of time units. Adjust as necessary.
- static final int TIME_UNIT_COUNT = 7;
- private static TimeUnit[] values = new TimeUnit[TIME_UNIT_COUNT];
-
- /**
- * Constant value for supported time unit.
- * @stable ICU 4.0
+ /**
+ * Here for serialization backward compatibility only.
*/
- public static TimeUnit
- SECOND = new TimeUnit("second", 6),
- MINUTE = new TimeUnit("minute", 5),
- HOUR = new TimeUnit("hour", 4),
- DAY = new TimeUnit("day", 3),
- WEEK = new TimeUnit("week", 2),
- MONTH = new TimeUnit("month", 1),
- YEAR = new TimeUnit("year", 0);
-
+ private final int index;
- // 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) {
- super("duration", name);
- this.index = idx;
- values[idx] = this; // store in values array
+ TimeUnit(String type, String code) {
+ super(type, code);
+ index = 0;
}
/**
* @stable ICU 4.0
*/
public static TimeUnit[] values() {
- return values.clone();
+ return new TimeUnit[] {
+ (TimeUnit) SECOND,
+ (TimeUnit) MINUTE,
+ (TimeUnit) HOUR,
+ (TimeUnit) DAY,
+ (TimeUnit) WEEK,
+ (TimeUnit) MONTH,
+ (TimeUnit) YEAR};
}
-
- // Returns the index for this TimeUnit. Something between 0 inclusive and
- // number of time units exclusive. Smaller time units have larger indexes.
- int getIndex() {
- return index;
+
+ private Object writeReplace() throws ObjectStreamException {
+ return new MeasureUnitProxy(type, code);
+ }
+
+ // For backward compatibility only
+ private Object readResolve() throws ObjectStreamException {
+ // The old index field used to uniquely identify the time unit.
+ switch (index) {
+ case 6:
+ return SECOND;
+ case 5:
+ return MINUTE;
+ case 4:
+ return HOUR;
+ case 3:
+ return DAY;
+ case 2:
+ return WEEK;
+ case 1:
+ return MONTH;
+ case 0:
+ return YEAR;
+ default:
+ throw new InvalidObjectException("Bad index: " + index);
+ }
}
}
import java.io.Serializable;
import java.text.FieldPosition;
import java.text.ParsePosition;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeSet;
import com.ibm.icu.dev.test.serializable.SerializableTest;
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.MeasureFormat.FormatWidth;
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.TimeUnit;
*/
public class MeasureUnitTest extends TestFmwk {
- private static final TimeUnitAmount[] _19m = {new TimeUnitAmount(19.0, TimeUnit.MINUTE)};
- private static final TimeUnitAmount[] _1h_23_5s = {
- new TimeUnitAmount(1.0, TimeUnit.HOUR),
- new TimeUnitAmount(23.5, TimeUnit.SECOND)};
- private static final TimeUnitAmount[] _1h_23_5m = {
- new TimeUnitAmount(1.0, TimeUnit.HOUR),
- new TimeUnitAmount(23.5, TimeUnit.MINUTE)};
- private static final TimeUnitAmount[] _1h_0m_23s = {
- new TimeUnitAmount(1.0, TimeUnit.HOUR),
- new TimeUnitAmount(0.0, TimeUnit.MINUTE),
- new TimeUnitAmount(23.0, TimeUnit.SECOND)};
- private static final TimeUnitAmount[] _2y_5M_3w_4d = {
- new TimeUnitAmount(2.0, TimeUnit.YEAR),
- new TimeUnitAmount(5.0, TimeUnit.MONTH),
- new TimeUnitAmount(3.0, TimeUnit.WEEK),
- new TimeUnitAmount(4.0, TimeUnit.DAY)};
- private static final TimeUnitAmount[] _1m_59_9996s = {
- new TimeUnitAmount(1.0, TimeUnit.MINUTE),
- new TimeUnitAmount(59.9996, TimeUnit.SECOND)};
-
-
/**
* @author markdavis
*
}
public void TestFormatPeriodEn() {
+ TimeUnitAmount[] _19m = {new TimeUnitAmount(19.0, TimeUnit.MINUTE)};
+ TimeUnitAmount[] _1h_23_5s = {
+ new TimeUnitAmount(1.0, TimeUnit.HOUR),
+ new TimeUnitAmount(23.5, TimeUnit.SECOND)};
+ TimeUnitAmount[] _1h_23_5m = {
+ new TimeUnitAmount(1.0, TimeUnit.HOUR),
+ new TimeUnitAmount(23.5, TimeUnit.MINUTE)};
+ TimeUnitAmount[] _1h_0m_23s = {
+ new TimeUnitAmount(1.0, TimeUnit.HOUR),
+ new TimeUnitAmount(0.0, TimeUnit.MINUTE),
+ new TimeUnitAmount(23.0, TimeUnit.SECOND)};
+ TimeUnitAmount[] _2y_5M_3w_4d = {
+ new TimeUnitAmount(2.0, TimeUnit.YEAR),
+ new TimeUnitAmount(5.0, TimeUnit.MONTH),
+ new TimeUnitAmount(3.0, TimeUnit.WEEK),
+ new TimeUnitAmount(4.0, TimeUnit.DAY)};
+ TimeUnitAmount[] _1m_59_9996s = {
+ new TimeUnitAmount(1.0, TimeUnit.MINUTE),
+ new TimeUnitAmount(59.9996, TimeUnit.SECOND)};
+
Object[][] fullData = {
{_1m_59_9996s, "1 minute, 59.9996 seconds"},
{_19m, "19 minutes"},
{_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, 4 days"},
{_0h_0m_17s, "0:00:17"},
{_6h_56_92m, "6:56.92"}};
- */
+ */
NumberFormat nf = NumberFormat.getNumberInstance(ULocale.ENGLISH);
nf.setMaximumFractionDigits(4);
- GeneralMeasureFormat gmf = GeneralMeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE, nf);
- verifyFormatPeriod("en FULL", gmf, fullData);
- gmf = GeneralMeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT, nf);
- verifyFormatPeriod("en SHORT", gmf, abbrevData);
-
-
+ MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE, nf);
+ verifyFormatPeriod("en FULL", mf, fullData);
+ mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT, nf);
+ verifyFormatPeriod("en SHORT", mf, abbrevData);
}
- private void verifyFormatPeriod(String desc, GeneralMeasureFormat gmf, Object[][] testData) {
+
+
+ private void verifyFormatPeriod(String desc, MeasureFormat mf, Object[][] testData) {
StringBuilder builder = new StringBuilder();
boolean failure = false;
for (Object[] testCase : testData) {
- String actual = gmf.format((Measure[]) testCase[0]);
+ String actual = mf.format((Measure[]) testCase[0]);
if (!testCase[1].equals(actual)) {
builder.append(String.format("%s: Expected: '%s', got: '%s'\n", desc, testCase[1], actual));
failure = true;
String lastType = null;
for (MeasureUnit expected : MeasureUnit.getAvailable()) {
String type = expected.getType();
- String code = expected.getCode();
+ String code = expected.getSubtype();
if (!type.equals(lastType)) {
logln(type);
lastType = type;
}
- MeasureUnit actual = MeasureUnit.getInstance(type, code);
+ MeasureUnit actual = MeasureUnit.internalGetInstance(type, code);
assertSame("Identity check", expected, actual);
}
}
-
- public void testTimePeriods() {
-
- }
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);
- }
-
+ ULocale russia = new ULocale("ru");
+ Object[][] data = new Object[][] {
+ {ULocale.ENGLISH, FormatWidth.WIDE, "2 miles, 1 foot, 2.3 inches"},
+ {ULocale.ENGLISH, FormatWidth.SHORT, "2 mi, 1 ft, 2.3 in"},
+ {ULocale.ENGLISH, FormatWidth.NARROW, "2mi, 1′, 2.3″"},
+ {russia, FormatWidth.WIDE, "2 мили, 1 фут и 2,3 дюйма"},
+ {russia, FormatWidth.SHORT, "2 мили 1 фут 2,3 дюйма"},
+ {russia, FormatWidth.NARROW, "2 мили 1 фут 2,3 дюйма"},
+ };
+ for (Object[] row : data) {
+ MeasureFormat mf = MeasureFormat.getInstance(
+ (ULocale) row[0], (FormatWidth) row[1]);
+ assertEquals(
+ "testMultiples",
+ row[2],
+ mf.formatMeasures(
+ new Measure(2, MeasureUnit.MILE),
+ new Measure(1, MeasureUnit.FOOT),
+ new Measure(2.3, MeasureUnit.INCH)));
}
}
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);
- }
+ MeasureFormat mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
+ assertEquals(
+ "testGram",
+ "1 g",
+ mf.format(new Measure(1, MeasureUnit.GRAM)));
+ assertEquals(
+ "testGram",
+ "1 G",
+ mf.format(new Measure(1, MeasureUnit.G_FORCE)));
}
public void testExamples() {
- GeneralMeasureFormat fmtFr = GeneralMeasureFormat.getInstance(ULocale.FRENCH, FormatWidth.SHORT);
+ MeasureFormat fmtFr = MeasureFormat.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);
+ MeasureFormat fmtFrFull = MeasureFormat.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),
+ assertEquals("", "70 pieds, 5,3 pouces", fmtFrFull.formatMeasures(new Measure(70, MeasureUnit.FOOT),
new Measure(5.3, MeasureUnit.INCH)));
- assertEquals("", "1 pied, 1 pouce", fmtFrFull.format(new Measure(1, MeasureUnit.FOOT),
+ assertEquals("", "1 pied, 1 pouce", fmtFrFull.formatMeasures(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),
+ MeasureFormat fmtEn = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.WIDE);
+ assertEquals("", "1 inch, 2 feet", fmtEn.formatMeasures(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);
+ }
+
+ public void testFieldPosition() {
+ MeasureFormat fmt = MeasureFormat.getInstance(
+ ULocale.ENGLISH, FormatWidth.SHORT);
+ FieldPosition pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+ fmt.format(new Measure(43.5, MeasureUnit.FOOT), new StringBuffer(), pos);
+ assertEquals("beginIndex", 2, pos.getBeginIndex());
+ assertEquals("endIndex", 3, pos.getEndIndex());
+
+ pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+ fmt.format(new Measure(43, MeasureUnit.FOOT), new StringBuffer(), pos);
+ assertEquals("beginIndex", 0, pos.getBeginIndex());
+ assertEquals("endIndex", 0, pos.getEndIndex());
+ }
+
+ public void testFieldPositionMultiple() {
+ MeasureFormat fmt = MeasureFormat.getInstance(
+ ULocale.ENGLISH, FormatWidth.SHORT);
+ FieldPosition pos = new FieldPosition(NumberFormat.Field.INTEGER);
+ String result = fmt.formatMeasures(
+ new StringBuilder(),
+ pos,
+ new Measure(354, MeasureUnit.METER),
+ new Measure(23, MeasureUnit.CENTIMETER)).toString();
+ assertEquals("result", "354 m, 23 cm", result);
+
+ // According to javadocs for {@link Format#format} FieldPosition is set to
+ // beginning and end of first such field encountered instead of the last
+ // such field encountered.
+ assertEquals("beginIndex", 0, pos.getBeginIndex());
+ assertEquals("endIndex", 3, pos.getEndIndex());
+
+ pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+ result = fmt.formatMeasures(
+ new StringBuilder(),
+ pos,
+ new Measure(354, MeasureUnit.METER),
+ new Measure(23, MeasureUnit.CENTIMETER),
+ new Measure(5.4, MeasureUnit.MILLIMETER)).toString();
+ assertEquals("result", "354 m, 23 cm, 5.4 mm", result);
+ assertEquals("beginIndex", 15, pos.getBeginIndex());
+ assertEquals("endIndex", 16, pos.getEndIndex());
+
+ result = fmt.formatMeasures(
+ new StringBuilder(),
+ pos,
+ new Measure(3, MeasureUnit.METER),
+ new Measure(23, MeasureUnit.CENTIMETER),
+ new Measure(5.4, MeasureUnit.MILLIMETER)).toString();
+ assertEquals("result", "3 m, 23 cm, 5.4 mm", result);
+ assertEquals("beginIndex", 13, pos.getBeginIndex());
+ assertEquals("endIndex", 14, pos.getEndIndex());
+
+ pos = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+ result = fmt.formatMeasures(
+ new StringBuilder(),
+ pos,
+ new Measure(3, MeasureUnit.METER),
+ new Measure(23, MeasureUnit.CENTIMETER),
+ new Measure(5, MeasureUnit.MILLIMETER)).toString();
+ assertEquals("result", "3 m, 23 cm, 5 mm", result);
+ assertEquals("beginIndex", 0, pos.getBeginIndex());
+ assertEquals("endIndex", 0, pos.getEndIndex());
+
+ }
+
+ public void testOldFormatWithList() {
+ List<Measure> measures = new ArrayList<Measure>(2);
+ measures.add(new Measure(5, MeasureUnit.ACRE));
+ measures.add(new Measure(3000, MeasureUnit.SQUARE_FOOT));
+ MeasureFormat fmt = MeasureFormat.getInstance(
+ ULocale.ENGLISH, FormatWidth.WIDE);
+ assertEquals("", "5 acres, 3,000 square feet", fmt.format(measures));
+ assertEquals("", "5 acres", fmt.format(measures.subList(0, 1)));
+ List<String> badList = new ArrayList<String>();
+ badList.add("be");
+ badList.add("you");
+ try {
+ fmt.format(badList);
+ fail("Expected IllegalArgumentException.");
+ } catch (IllegalArgumentException expected) {
+ // Expected
+ }
+ }
+
+ public void testOldFormatWithArray() {
+ Measure[] measures = new Measure[] {
+ new Measure(5, MeasureUnit.ACRE),
+ new Measure(3000, MeasureUnit.SQUARE_FOOT),
+ };
+ MeasureFormat fmt = MeasureFormat.getInstance(
+ ULocale.ENGLISH, FormatWidth.WIDE);
+ assertEquals("", "5 acres, 3,000 square feet", fmt.format(measures));
+ }
+
+ public void testOldFormatBadArg() {
+ MeasureFormat fmt = MeasureFormat.getInstance(
+ ULocale.ENGLISH, FormatWidth.WIDE);
+ try {
+ fmt.format("be");
+ fail("Expected IllegalArgumentExceptino.");
+ } catch (IllegalArgumentException e) {
+ // Expected
}
- // 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() {
boolean first = true;
for (String type : new TreeSet<String>(MeasureUnit.getAvailableTypes())) {
for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
- String code = unit.getCode();
+ String code = unit.getSubtype();
String name = code.toUpperCase(Locale.ENGLISH).replace("-", "_");
if (type.equals("angle")) {
public void TestSerial() {
checkStreamingEquality(MeasureUnit.CELSIUS);
- checkStreamingEquality(GeneralMeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.NARROW));
+ checkStreamingEquality(MeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.NARROW));
checkStreamingEquality(Currency.getInstance("EUR"));
+ checkStreamingEquality(MeasureFormat.getInstance(ULocale.GERMAN, FormatWidth.SHORT));
+ checkStreamingEquality(MeasureFormat.getCurrencyFormat(ULocale.ITALIAN));
}
public <T extends Serializable> void checkStreamingEquality(T item) {
Object obj = objectInputStream.readObject();
assertEquals("Streamed Object equals ", item, obj);
} catch (IOException e) {
+ e.printStackTrace();
assertNull("Test Serialization " + item.getClass(), e);
} catch (ClassNotFoundException e) {
assertNull("Test Serialization " + item.getClass(), e);
};
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());
+ return a1.getType().equals(b1.getType())
+ && a1.getSubtype().equals(b1.getSubtype());
}
}
-
- public static class GeneralMeasureFormatHandler implements SerializableTest.Handler
+
+ public static class MeasureFormatHandler 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
- )),
+ MeasureFormat items[] = {
+ MeasureFormat.getInstance(ULocale.FRANCE, FormatWidth.SHORT),
+ MeasureFormat.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())
+ MeasureFormat a1 = (MeasureFormat) a;
+ MeasureFormat b1 = (MeasureFormat) b;
+ return a1.getLocale().equals(b1.getLocale())
+ && a1.getWidth().equals(b1.getWidth())
+ && a1.getNumberFormat().equals(b1.getNumberFormat())
;
}
}
{"ICU_50.1", "com.ibm.icu.text.PluralRules.dat"},
{"ICU_51.1", "com.ibm.icu.text.PluralRules.dat"},
+
+ // GeneralMeasureFormat was in technical preview, but is going away after ICU 52.1.
+ {"ICU_52.1", "com.ibm.icu.text.GeneralMeasureFormat.dat"},
// RuleBasedNumberFormat
{"ICU_3.6", "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
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());
+ map.put("com.ibm.icu.text.MeasureFormat", new MeasureUnitTest.MeasureFormatHandler());
}
public SerializableTest()