blob: 74c13b660bc9d95647c3ae746dfbdcaaef19f544 [file] [log] [blame]
// © 2020 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
package com.ibm.icu.impl.number;
import com.ibm.icu.impl.IllegalIcuArgumentException;
import com.ibm.icu.impl.units.MeasureUnitImpl;
import com.ibm.icu.impl.units.UnitsRouter;
import com.ibm.icu.number.Precision;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;
public class UsagePrefsHandler implements MicroPropsGenerator {
private final MicroPropsGenerator fParent;
private UnitsRouter fUnitsRouter;
public UsagePrefsHandler(ULocale locale, MeasureUnit inputUnit, String usage, MicroPropsGenerator parent) {
assert parent != null;
this.fParent = parent;
this.fUnitsRouter =
new UnitsRouter(MeasureUnitImpl.forIdentifier(inputUnit.getIdentifier()), locale.getCountry(), usage);
}
private static Precision parseSkeletonToPrecision(String precisionSkeleton) {
final String kSuffixPrefix = "precision-increment/";
if (!precisionSkeleton.startsWith(kSuffixPrefix)) {
throw new IllegalIcuArgumentException("precisionSkeleton is only precision-increment");
}
String skeleton = precisionSkeleton.substring(kSuffixPrefix.length());
String skeletons[] = skeleton.split("/");
BigDecimal num = new BigDecimal(skeletons[0]);
BigDecimal den =
skeletons.length == 2 ?
new BigDecimal(skeletons[1]) :
new BigDecimal("1");
return Precision.increment(num.divide(den, MathContext.DECIMAL128));
}
/**
* Populates micros.mixedMeasures and modifies quantity, based on the values
* in measures.
*/
protected static void mixedMeasuresToMicros(List<Measure> measures, DecimalQuantity quantity, MicroProps micros) {
micros.mixedMeasures = new ArrayList<>();
if (measures.size() > 1) {
// For debugging
assert (micros.outputUnit.getComplexity() == MeasureUnit.Complexity.MIXED);
// Check that we received the expected number of measurements:
assert measures.size() == micros.outputUnit.splitToSingleUnits().size();
// Mixed units: except for the last value, we pass all values to the
// LongNameHandler via micros->mixedMeasures.
for (int i = 0, n = measures.size() - 1; i < n; i++) {
micros.mixedMeasures.add(measures.get(i));
}
}
// The last value (potentially the only value) gets passed on via quantity.
quantity.setToBigDecimal((BigDecimal) measures.get(measures.size()- 1).getNumber());
}
/**
* Returns the list of possible output units, i.e. the full set of
* preferences, for the localized, usage-specific unit preferences.
* <p>
* The returned pointer should be valid for the lifetime of the
* UsagePrefsHandler instance.
*/
public List<MeasureUnit> getOutputUnits() {
return fUnitsRouter.getOutputUnits();
}
/**
* Obtains the appropriate output value, MeasureUnit and
* rounding/precision behaviour from the UnitsRouter.
* <p>
* The output unit is passed on to the LongNameHandler via
* micros.outputUnit.
*/
@Override
public MicroProps processQuantity(DecimalQuantity quantity) {
MicroProps micros = this.fParent.processQuantity(quantity);
quantity.roundToInfinity(); // Enables toDouble
final UnitsRouter.RouteResult routed = fUnitsRouter.route(quantity.toBigDecimal());
final List<Measure> routedMeasures = routed.measures;
micros.outputUnit = routed.outputUnit.build();
UsagePrefsHandler.mixedMeasuresToMicros(routedMeasures, quantity, micros);
String precisionSkeleton = routed.precision;
assert micros.rounder != null;
if (micros.rounder instanceof Precision.BogusRounder) {
Precision.BogusRounder rounder = (Precision.BogusRounder)micros.rounder;
if (precisionSkeleton != null && precisionSkeleton.length() > 0) {
micros.rounder = rounder.into(parseSkeletonToPrecision(precisionSkeleton));
} else {
// We use the same rounding mode as COMPACT notation: known to be a
// human-friendly rounding mode: integers, but add a decimal digit
// as needed to ensure we have at least 2 significant digits.
micros.rounder = rounder.into(Precision.integer().withMinDigits(2));
}
}
return micros;
}
}