//
// ERXUnitAwareDecimalFormat.java
// Project ERExtensions
//
// Created by tatsuya on Sun Oct 19 2002
//
package er.extensions.formatters;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;
import java.util.Enumeration;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSKeyValueCoding;
/**
* <code>ERXUnitAwareDecimalFormat</code> extends {@link java.text.DecimalFormat}
* to add an automatic unit conversion feature for
* the given unit. Convenient to display friendly values
* for file size, elapsed time, etc.
*
* <strong>Examples:</strong>
* <pre>
*
* import java.text.NumberFormat;
* import er.extensions.ERXUnitAwareDecimalFormat
*
* double smallValue = 123.0d;
* double largeValue = 1234567890.0d;
* NumberFormat formatter = new ERXUnitAwareDecimalFormat(ERXUnitAwareDecimalFormat.BYTE);
* formatter.setMaximumFractionDigits(2);
*
* // Will display "123 bytes"
* System.out.println(formatter.format(smallValue));
*
* // Will display "1.15 GB"
* System.out.println(formatter.format(largeValue));
*
* </pre>
*/
public class ERXUnitAwareDecimalFormat extends DecimalFormat implements Cloneable, Serializable {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
/** Predefined computer mass unit; supports: bytes, KB, MB, GB, TB */
public static final String BYTE = "byte";
/** Predefined metric length unit; supports: nm, micrometer, mm, cm, m, km */
public static final String METER = "meter";
/** Predefined metric weight unit; supports: mg, g, kg, ton, kiloton */
public static final String GRAM = "gram";
/** Predefined time unit; supports: ps, ns, microsecond, ms, sec, min, hour, day */
public static final String SECOND = "second";
/** UnitPrefix is an inner class */
public static class UnitPrefix implements NSKeyValueCoding {
private static NSArray _bytePrefixArray;
private static NSArray _meterPrefixArray;
private static NSArray _gramPrefixArray;
private static NSArray _secondPrefixArray;
protected final String unitSymbol;
protected final String unitName;
protected final double multiplyingFactor;
public UnitPrefix(String unitSymbol, String unitName, double multiplyingFactor) {
this.unitSymbol = unitSymbol;
this.unitName = unitName;
this.multiplyingFactor = multiplyingFactor;
}
public String unitSymbol() {
return unitSymbol;
}
public String unitName() {
return unitName;
}
public double multiplyingFactor() {
return multiplyingFactor;
}
public double adjustScale(double number) {
return number / multiplyingFactor;
}
public double adjustScale(long number) {
return adjustScale((double)number);
}
public static NSArray unitPrefixArrayForUnit(String unitName) {
NSArray unitPrefixArray = NSArray.EmptyArray;
if (BYTE.equals(unitName)) {
if (_bytePrefixArray == null)
_bytePrefixArray = new NSArray(new Object[] {
new UnitPrefix("bytes", "byte", 1.0d),
new UnitPrefix("KB", "kilobyte", 1024.0d),
new UnitPrefix("MB", "megabyte", 1024.0d * 1024.0d),
new UnitPrefix("GB", "gigabyte", 1024.0d * 1024.0d * 1024.0d),
new UnitPrefix("TB", "terabyte", 1024.0d * 1024.0d * 1024.0d * 1024.0d) });
unitPrefixArray = _bytePrefixArray;
} else if (METER.equals(unitName)) {
if (_meterPrefixArray == null)
_meterPrefixArray = new NSArray(new Object[] {
new UnitPrefix("nm", "nanometer", 1.0d / 1000.0d / 1000.0d / 1000.0d),
new UnitPrefix("micrometer", "micrometer", 1.0d / 1000.0d / 1000.0d),
new UnitPrefix("mm", "millimeter", 1.0d / 1000.0d),
new UnitPrefix("cm", "centimeter", 1.0d / 100.0d),
new UnitPrefix("m", "meter", 1.0d),
new UnitPrefix("km", "kilometer", 1000.0d) });
unitPrefixArray = _meterPrefixArray;
} else if (GRAM.equals(unitName)) {
if (_gramPrefixArray == null)
_gramPrefixArray = new NSArray(new Object[] {
new UnitPrefix("mg", "milligram", 1.0d / 1000.0d),
new UnitPrefix("g", "gram", 1.0d),
new UnitPrefix("kg", "kilogram", 1000.0d),
new UnitPrefix("ton", "metric ton", 1000.0d * 1000.0d),
new UnitPrefix("kiloton", "metric kiloton", 1000.0d * 1000.0d * 1000.0d) });
unitPrefixArray = _gramPrefixArray;
} else if (SECOND.equals(unitName)) {
if (_secondPrefixArray == null)
_secondPrefixArray = new NSArray(new Object[] {
new UnitPrefix("ps", "picosecond", 1.0d / 1000.0d / 1000.0d / 1000.0d / 1000.0d),
new UnitPrefix("ns", "nanosecond", 1.0d / 1000.0d / 1000.0d / 1000.0d),
new UnitPrefix("microsecond", "microsecond", 1.0d / 1000.0d / 1000.0d),
new UnitPrefix("ms", "millisecond", 1.0d / 1000.0d),
new UnitPrefix("sec", "second", 1.0d),
new UnitPrefix("min", "minute", 60.0d),
new UnitPrefix("hour", "hour", 60.0d * 60.0d),
new UnitPrefix("day", "day", 60.0d * 60.0d * 24.0d) });
unitPrefixArray = _secondPrefixArray;
}
return unitPrefixArray;
}
public static UnitPrefix findAppropriatePrefix(double number, NSArray unitPrefixArray) {
UnitPrefix unitPrefix = null;
Enumeration e = unitPrefixArray.reverseObjectEnumerator();
while (e.hasMoreElements()) {
unitPrefix = (UnitPrefix)e.nextElement();
if (number >= unitPrefix.multiplyingFactor()) break;
}
return unitPrefix;
}
public Object valueForKey(String key) {
return NSKeyValueCoding.DefaultImplementation.valueForKey(this, key);
}
public void takeValueForKey(Object value, String key) {
throw new NSKeyValueCoding.UnknownKeyException("Can't take the value " + value
+ " for the key " + key
+ " since " + getClass().getName() + " is immutable.",
value, key);
}
private String _toString;
@Override
public String toString() {
if (_toString == null)
_toString = "<" + getClass().getName() + " "
+ unitName + "(" + unitSymbol + ") [" + multiplyingFactor + "] >";
return _toString;
}
}
protected final NSArray unitPrefixArray;
public ERXUnitAwareDecimalFormat() {
super();
unitPrefixArray = NSArray.EmptyArray;
}
public ERXUnitAwareDecimalFormat(String unitName) {
super();
unitPrefixArray = UnitPrefix.unitPrefixArrayForUnit(unitName);
}
public ERXUnitAwareDecimalFormat(NSArray unitPrefixArray) {
super();
this.unitPrefixArray = unitPrefixArray;
}
public ERXUnitAwareDecimalFormat(String pattern, DecimalFormatSymbols symbols) {
super(pattern, symbols);
unitPrefixArray = NSArray.EmptyArray;
}
public ERXUnitAwareDecimalFormat(String pattern, DecimalFormatSymbols symbols, String unitName) {
super(pattern, symbols);
unitPrefixArray = UnitPrefix.unitPrefixArrayForUnit(unitName);
}
public ERXUnitAwareDecimalFormat(String pattern, DecimalFormatSymbols symbols, NSArray unitPrefixArray) {
super(pattern, symbols);
this.unitPrefixArray = unitPrefixArray;
}
@Override
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition fieldPosition) {
StringBuffer result = toAppendTo;
UnitPrefix unitPrefix = UnitPrefix.findAppropriatePrefix(number, unitPrefixArray);
if (unitPrefix == null) {
result = super.format(number, toAppendTo, fieldPosition);
} else {
double convertedNumber = unitPrefix.adjustScale(number);
result = super.format(convertedNumber, toAppendTo, fieldPosition);
// ENHANCEME: Would be nice to be able to specify the place for
// the unit symbol via the format string.
result.append(' ').append(unitPrefix.unitSymbol());
}
return result;
}
@Override
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition fieldPosition) {
return format((double)number, toAppendTo, fieldPosition);
}
}