
com.pervasivecode.utils.measure.ScalingFormatter Maven / Gradle / Ivy
Show all versions of measurement-utils-jsr363 Show documentation
package com.pervasivecode.utils.measure;
import static com.google.common.base.Preconditions.checkNotNull;
import java.text.NumberFormat;
import javax.measure.Quantity;
import javax.measure.Unit;
import javax.measure.format.UnitFormat;
import tec.uom.lib.common.BinaryPrefix;
import tec.uom.se.format.SimpleUnitFormat;
import tec.uom.se.unit.MetricPrefix;
import tec.uom.se.unit.ProductUnit;
import tec.uom.se.unit.Units;
/**
* Formatter for {@link Quantity} objects that applies scale prefixes to make the formatted value
* easier to read.
*
* Example: formatting a value of 12,000 meters for the US locale and the SI prefix system would
* result in a format of "12 km" since the value would be scaled to kilometers.
*
* Note: The system of scale prefixes, the number-value formatter, and the unit-label provider are
* all parameterized, to enable formatting for various locales and prefix systems. This means that
* callers will need to provide an appropriately localized NumberFormat and UnitLabelProvider, as
* well as a QuantityPrefixSelector capable of applying the caller's desired system of prefixes as
* appropriate.
*
* @param The type of measurement that the formatter is able to format. Example:
* {@link javax.measure.quantity.Mass Mass}
* @see ScalingFormatters
*/
public class ScalingFormatter> implements QuantityFormatter {
private final QuantityPrefixSelector prefixSelector;
private final Unit baseUnit;
private final NumberFormat numberFormat;
private final UnitFormat unitFormat;
/**
* Set up a ScalingFormatter with the specified scaling and formatting behavior.
*
* @param baseInformationUnit The fundamental unit of this type of measurement, e.g.
* {@link Units#OHM OHM}.
* @param prefixSelector This decides which scale prefix is appropriate to use for any given value
* of any given magnitude.
* @param numberFormat This constructs the correct String representation for the numeric portion
* of the Quantity, after it has been scaled by the {@code prefixSelector}.
*/
public ScalingFormatter(Unit baseInformationUnit, QuantityPrefixSelector prefixSelector,
NumberFormat numberFormat) {
this.baseUnit = checkNotNull(baseInformationUnit);
this.prefixSelector = checkNotNull(prefixSelector);
this.numberFormat = checkNotNull(numberFormat);
this.unitFormat = SimpleUnitFormat.getInstance();
}
/**
* Set up a ScalingFormatter with the specified scaling and formatting behavior.
*
* @param baseInformationUnit The fundamental unit of this type of measurement, e.g.
* {@link Units#OHM OHM}.
* @param prefixSelector This decides which scale prefix is appropriate to use for any given value
* of any given magnitude.
* @param numberFormat This constructs the correct String representation for the numeric portion
* of the Quantity, after it has been scaled by the {@code prefixSelector}.
* @param unitLabelProvider Provider of additional unit+prefix labels for units and prefixes other
* than the base SI units and prefixes directly supported by {@link SimpleUnitFormat}.
* (Note that unit symbols for compound units are not correctly generated by
* {@link ProductUnit}, e.g. "m/s" for {@link Units#METRE METRE} divided by
* {@link Units#SECOND SECOND}, so they must be explicitly provided here.)
*/
public ScalingFormatter(Unit baseInformationUnit, QuantityPrefixSelector prefixSelector,
NumberFormat numberFormat, UnitLabelProvider unitLabelProvider) {
this(baseInformationUnit, prefixSelector, numberFormat);
UnitFormatLabelSetter labelSetter =
new UnitFormatLabelSetter(unitLabelProvider, unitFormat);
labelSetter.setBaseUnitLabel(baseInformationUnit);
labelSetter.setSiUnitLabels(baseInformationUnit);
labelSetter.setIecBinaryUnitLabels(baseInformationUnit);
}
/**
* Scale and format a value.
*
* @param measure The value to scale and format.
* @return The scaled and formatted String representation of {@code measure}.
*/
@Override
public String format(Quantity measure) {
Quantity m = prefixSelector.selectBestPrefix(measure, baseUnit);
StringBuilder sb = new StringBuilder();
sb.append(this.numberFormat.format(m.getValue().doubleValue()));
sb.append(' ');
sb.append(this.unitFormat.format(m.getUnit()));
return sb.toString();
}
/**
* Populate a SimpleUnitFormat instance with labels that include prefixes from the SI and IEC
* Binary systems of prefixes.
*
* @param The kind of measurement that this instance will provide labels for. Example:
* {@link javax.measure.quantity.Area Area}
*/
private static class UnitFormatLabelSetter
> {
private final UnitLabelProvider
unitLabels;
private final UnitFormat fmt;
public UnitFormatLabelSetter(UnitLabelProvider
unitLabels, UnitFormat fmt) {
this.unitLabels = unitLabels;
this.fmt = fmt;
}
private void setLabel(Unit
unit) {
String label = unitLabels.getLabel(unit);
if (label != null) {
fmt.label(unit, label);
}
}
public void setBaseUnitLabel(Unit
unit) {
setLabel(unit);
}
// TODO consider removing this hardcoded list of prefixes in favor of querying UnitLabelProvider
// instances for the list of all prefixes which they can provide.
public void setSiUnitLabels(Unit
baseUnit) {
setLabel(MetricPrefix.KILO(baseUnit));
setLabel(MetricPrefix.MEGA(baseUnit));
setLabel(MetricPrefix.GIGA(baseUnit));
setLabel(MetricPrefix.TERA(baseUnit));
setLabel(MetricPrefix.PETA(baseUnit));
setLabel(MetricPrefix.EXA(baseUnit));
setLabel(MetricPrefix.ZETTA(baseUnit));
setLabel(MetricPrefix.YOTTA(baseUnit));
}
public void setIecBinaryUnitLabels(Unit
baseUnit) {
setLabel(BinaryPrefix.KIBI(baseUnit));
setLabel(BinaryPrefix.MEBI(baseUnit));
setLabel(BinaryPrefix.GIBI(baseUnit));
setLabel(BinaryPrefix.TEBI(baseUnit));
setLabel(BinaryPrefix.PEBI(baseUnit));
setLabel(BinaryPrefix.EXBI(baseUnit));
setLabel(BinaryPrefix.ZEBI(baseUnit));
setLabel(BinaryPrefix.YOBI(baseUnit));
}
}
}