blob: 49c3680b65540fa45a2e6e09f3970a3ce8e20204 [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2004-2013, Google Inc, International Business Machines *
* Corporation and others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
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.
*
* @see com.ibm.icu.util.Measure
* @author Alan Liu
* @stable ICU 3.0
*/
public class MeasureUnit implements Comparable<MeasureUnit>, Serializable {
private static final long serialVersionUID = -1839973855554750484L;
private static final Map<String, Map<String,MeasureUnit>> cache
= new HashMap<String, Map<String,MeasureUnit>>();
/**
* @internal
* @deprecated This API is ICU internal only.
*/
protected final String type;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
protected final String code;
/**
* @internal
* @deprecated This API is ICU internal only.
*/
protected MeasureUnit(String type, String code) {
this.type = type;
this.code = code;
}
/**
* 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.
*
* @param type the type, such as "length"
* @param code the code, such as "meter"
* @return the unit.
* @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;
}
}
}
if (type == null || !ASCII.containsAll(type) || code == null || !ASCII_HYPHEN.containsAll(code)) {
throw new NullPointerException("The type or code are invalid.");
}
return MeasureUnit.addUnit(type, code, UNIT_FACTORY);
}
static final UnicodeSet ASCII = new UnicodeSet('a', 'z').freeze();
static final UnicodeSet ASCII_HYPHEN = new UnicodeSet('-', '-', 'a', 'z').freeze();
/**
* @internal
* @deprecated This API is ICU internal only.
*/
protected interface Factory {
MeasureUnit create(String type, String code);
}
private static Factory UNIT_FACTORY = new Factory() {
public MeasureUnit create(String type, String code) {
return new MeasureUnit(type, code);
}
};
static Factory CURRENCY_FACTORY = new Factory() {
public MeasureUnit create(String type, String code) {
return new Currency(code);
}
};
// /**
// * Register a unit for later use
// * @param type the type, such as "length"
// * @param code the code, such as "meter"
// * @return the unit.
// * @draft ICU 52
// * @provisional This API might change or be removed in a future release.
// */
// public static synchronized MeasureUnit registerUnit(String type, String code) {
// MeasureUnit result = getInstance(type, code);
// if (result == null) {
// result = addUnit(type, code, MY_FACTORY);
// }
// return result;
// }
//
static {
// load all of the units for English, since we know that that is a superset.
/**
* units{
* duration{
* day{
* one{"{0} ден"}
* other{"{0} дена"}
* }
*/
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "en");
for (FormatWidth key : FormatWidth.values()) {
try {
ICUResourceBundle unitsTypeRes = resource.getWithFallback(key.resourceKey);
int size = unitsTypeRes.getSize();
for ( int index = 0; index < size; ++index) {
UResourceBundle unitsRes = unitsTypeRes.get(index);
String type = unitsRes.getKey();
int unitsSize = unitsRes.getSize();
for ( int index2 = 0; index2 < unitsSize; ++index2) {
String unitName = unitsRes.get(index2).getKey();
addUnit(type, unitName, UNIT_FACTORY);
}
}
} catch ( MissingResourceException e ) {
continue;
}
}
// preallocate currencies
try {
UResourceBundle bundle = UResourceBundle.getBundleInstance(
ICUResourceBundle.ICU_BASE_NAME,
"currencyNumericCodes",
ICUResourceBundle.ICU_DATA_CLASS_LOADER);
UResourceBundle codeMap = bundle.get("codeMap");
for (Enumeration<String> it = codeMap.getKeys(); it.hasMoreElements();) {
MeasureUnit.addUnit("currency", it.nextElement(), Currency.CURRENCY_FACTORY);
}
} catch (MissingResourceException e) {
// fall through
}
}
// Must only be called at static initialization, or inside synchronized block.
/**
* @internal
* @deprecated This API is ICU internal only.
*/
protected synchronized static MeasureUnit addUnit(String type, String unitName, Factory factory) {
Map<String, MeasureUnit> tmp = cache.get(type);
if (tmp == null) {
cache.put(type, tmp = new HashMap<String, MeasureUnit>());
} else {
// "intern" the type by setting to first item's type.
type = tmp.entrySet().iterator().next().getValue().type;
}
MeasureUnit unit = tmp.get(unitName);
if (unit == null) {
tmp.put(unitName, unit = factory.create(type, unitName));
}
return unit;
}
/**
* Get all of the available general units' types.
* @return available units
* @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.
*/
public static final MeasureUnit
/** Constant for unit of acceleration: g-force */
G_FORCE = MeasureUnit.getInstance("acceleration", "g-force"),
/** Constant for unit of angle: degree */
DEGREE = MeasureUnit.getInstance("angle", "degree"),
/** Constant for unit of angle: minute */
ARC_MINUTE = MeasureUnit.getInstance("angle", "minute"),
/** Constant for unit of angle: second */
ARC_SECOND = MeasureUnit.getInstance("angle", "second"),
/** Constant for unit of area: acre */
ACRE = MeasureUnit.getInstance("area", "acre"),
/** Constant for unit of area: hectare */
HECTARE = MeasureUnit.getInstance("area", "hectare"),
/** Constant for unit of area: square-foot */
SQUARE_FOOT = MeasureUnit.getInstance("area", "square-foot"),
/** Constant for unit of area: square-kilometer */
SQUARE_KILOMETER = MeasureUnit.getInstance("area", "square-kilometer"),
/** Constant for unit of area: square-meter */
SQUARE_METER = MeasureUnit.getInstance("area", "square-meter"),
/** Constant for unit of area: square-mile */
SQUARE_MILE = MeasureUnit.getInstance("area", "square-mile"),
/** Constant for unit of duration: day */
DAY = MeasureUnit.getInstance("duration", "day"),
/** Constant for unit of duration: hour */
HOUR = MeasureUnit.getInstance("duration", "hour"),
/** Constant for unit of duration: millisecond */
MILLISECOND = MeasureUnit.getInstance("duration", "millisecond"),
/** Constant for unit of duration: minute */
MINUTE = MeasureUnit.getInstance("duration", "minute"),
/** Constant for unit of duration: month */
MONTH = MeasureUnit.getInstance("duration", "month"),
/** Constant for unit of duration: second */
SECOND = MeasureUnit.getInstance("duration", "second"),
/** Constant for unit of duration: week */
WEEK = MeasureUnit.getInstance("duration", "week"),
/** Constant for unit of duration: year */
YEAR = MeasureUnit.getInstance("duration", "year"),
/** Constant for unit of length: centimeter */
CENTIMETER = MeasureUnit.getInstance("length", "centimeter"),
/** Constant for unit of length: foot */
FOOT = MeasureUnit.getInstance("length", "foot"),
/** Constant for unit of length: inch */
INCH = MeasureUnit.getInstance("length", "inch"),
/** Constant for unit of length: kilometer */
KILOMETER = MeasureUnit.getInstance("length", "kilometer"),
/** Constant for unit of length: light-year */
LIGHT_YEAR = MeasureUnit.getInstance("length", "light-year"),
/** Constant for unit of length: meter */
METER = MeasureUnit.getInstance("length", "meter"),
/** Constant for unit of length: mile */
MILE = MeasureUnit.getInstance("length", "mile"),
/** Constant for unit of length: millimeter */
MILLIMETER = MeasureUnit.getInstance("length", "millimeter"),
/** Constant for unit of length: picometer */
PICOMETER = MeasureUnit.getInstance("length", "picometer"),
/** Constant for unit of length: yard */
YARD = MeasureUnit.getInstance("length", "yard"),
/** Constant for unit of mass: gram */
GRAM = MeasureUnit.getInstance("mass", "gram"),
/** Constant for unit of mass: kilogram */
KILOGRAM = MeasureUnit.getInstance("mass", "kilogram"),
/** Constant for unit of mass: ounce */
OUNCE = MeasureUnit.getInstance("mass", "ounce"),
/** Constant for unit of mass: pound */
POUND = MeasureUnit.getInstance("mass", "pound"),
/** Constant for unit of power: horsepower */
HORSEPOWER = MeasureUnit.getInstance("power", "horsepower"),
/** Constant for unit of power: kilowatt */
KILOWATT = MeasureUnit.getInstance("power", "kilowatt"),
/** Constant for unit of power: watt */
WATT = MeasureUnit.getInstance("power", "watt"),
/** Constant for unit of pressure: hectopascal */
HECTOPASCAL = MeasureUnit.getInstance("pressure", "hectopascal"),
/** Constant for unit of pressure: inch-hg */
INCH_HG = MeasureUnit.getInstance("pressure", "inch-hg"),
/** Constant for unit of pressure: millibar */
MILLIBAR = MeasureUnit.getInstance("pressure", "millibar"),
/** Constant for unit of speed: kilometer-per-hour */
KILOMETER_PER_HOUR = MeasureUnit.getInstance("speed", "kilometer-per-hour"),
/** Constant for unit of speed: meter-per-second */
METER_PER_SECOND = MeasureUnit.getInstance("speed", "meter-per-second"),
/** Constant for unit of speed: mile-per-hour */
MILE_PER_HOUR = MeasureUnit.getInstance("speed", "mile-per-hour"),
/** Constant for unit of temperature: celsius */
CELSIUS = MeasureUnit.getInstance("temperature", "celsius"),
/** Constant for unit of temperature: fahrenheit */
FAHRENHEIT = MeasureUnit.getInstance("temperature", "fahrenheit"),
/** Constant for unit of volume: cubic-kilometer */
CUBIC_KILOMETER = MeasureUnit.getInstance("volume", "cubic-kilometer"),
/** Constant for unit of volume: cubic-mile */
CUBIC_MILE = MeasureUnit.getInstance("volume", "cubic-mile"),
/** Constant for unit of volume: liter */
LITER = MeasureUnit.getInstance("volume", "liter");
/** Private **/
private Object writeReplace() throws ObjectStreamException {
return new MeasureUnitProxy(type, code);
}
static final class MeasureUnitProxy implements Externalizable {
private static final long serialVersionUID = -3910681415330989598L;
private String type;
private String code;
public MeasureUnitProxy(String type, String code) {
this.type = type;
this.code = code;
}
// Must have public constructor, to enable Externalizable
public MeasureUnitProxy() {
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeByte(0); // version
out.writeUTF(type);
out.writeUTF(code);
out.writeShort(0); // allow for more data.
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
/* byte version = */ in.readByte(); // version
type = in.readUTF();
code = in.readUTF();
// allow for more data from future version
int extra = in.readShort();
if (extra > 0) {
byte[] extraBytes = new byte[extra];
in.read(extraBytes, 0, extra);
}
}
private Object readResolve() throws ObjectStreamException {
return "currency".equals(type)
? Currency.getInstance(code)
: MeasureUnit.getInstance(type, code);
}
}
}