blob: 7ad64bb82f3501de8c1f1b54b38d58b161d1f290 [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.FormattedStringBuilder;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.number.LocalizedNumberFormatter;
import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.ListFormatter;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.SimpleFormatter;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
import java.util.ArrayList;
import java.util.List;
public class MixedUnitLongNameHandler implements MicroPropsGenerator, ModifierStore {
// Not owned
private final PluralRules rules;
// Not owned
private final MicroPropsGenerator parent;
// If this LongNameHandler is for a mixed unit, this stores unit data for
// each of the individual units. For each unit, it stores ARRAY_LENGTH
// strings, as returned by getMeasureData.
private List<String[]> fMixedUnitData;
// A localized NumberFormatter used to format the integer-valued bigger
// units of Mixed Unit measurements.
private LocalizedNumberFormatter fIntegerFormatter;
// A localised list formatter for joining mixed units together.
private ListFormatter fListFormatter;
private MixedUnitLongNameHandler(PluralRules rules, MicroPropsGenerator parent) {
this.rules = rules;
this.parent = parent;
}
/**
* Construct a localized MixedUnitLongNameHandler for the specified
* MeasureUnit. It must be a MIXED unit.
* <p>
*
* @param locale The desired locale.
* @param mixedUnit The mixed measure unit to construct a
* MixedUnitLongNameHandler for.
* @param width Specifies the desired unit rendering.
* @param rules Does not take ownership.
* @param parent Does not take ownership.
*/
public static MixedUnitLongNameHandler forMeasureUnit(ULocale locale, MeasureUnit mixedUnit,
NumberFormatter.UnitWidth width, PluralRules rules,
MicroPropsGenerator parent) {
assert (mixedUnit.getComplexity() == MeasureUnit.Complexity.MIXED);
MixedUnitLongNameHandler result = new MixedUnitLongNameHandler(rules, parent);
List<MeasureUnit> individualUnits = mixedUnit.splitToSingleUnits();
result.fMixedUnitData = new ArrayList<>();
for (int i = 0; i < individualUnits.size(); i++) {
// Grab data for each of the components.
String[] unitData = new String[LongNameHandler.ARRAY_LENGTH];
LongNameHandler.getMeasureData(locale, individualUnits.get(i), width, unitData);
result.fMixedUnitData.add(unitData);
}
ListFormatter.Width listWidth = ListFormatter.Width.SHORT;
if (width == NumberFormatter.UnitWidth.NARROW) {
listWidth = ListFormatter.Width.NARROW;
} else if (width == NumberFormatter.UnitWidth.FULL_NAME) {
// This might be the same as SHORT in most languages:
listWidth = ListFormatter.Width.WIDE;
}
result.fListFormatter = ListFormatter.getInstance(locale, ListFormatter.Type.UNITS, listWidth);
// We need a localised NumberFormatter for the integers of the bigger units
// (providing Arabic numerals, for example).
result.fIntegerFormatter = NumberFormatter.withLocale(locale);
return result;
}
/**
* Produces a plural-appropriate Modifier for a mixed unit: `quantity` is
* taken as the final smallest unit, while the larger unit values must be
* provided via `micros.mixedMeasures`.
*/
@Override
public MicroProps processQuantity(DecimalQuantity quantity) {
assert (fMixedUnitData.size() > 1);
MicroProps micros;
// if (parent != null)
micros = parent.processQuantity(quantity);
micros.modOuter = getMixedUnitModifier(quantity, micros);
return micros;
}
// Required for ModifierStore. And ModifierStore is required by
// SimpleModifier constructor's last parameter. We assert his will never get
// called though.
@Override
public Modifier getModifier(Modifier.Signum signum, StandardPlural plural) {
// TODO(units): investigate this method while investigating where
// LongNameHandler.getModifier() gets used. To be sure it remains
// unreachable:
return null;
}
// For a mixed unit, returns a Modifier that takes only one parameter: the
// smallest and final unit of the set. The bigger units' values and labels
// get baked into this Modifier, together with the unit label of the final
// unit.
private Modifier getMixedUnitModifier(DecimalQuantity quantity, MicroProps micros) {
// TODO(icu-units#21): mixed units without usage() is not yet supported.
// That should be the only reason why this happens, so delete this whole if
// once fixed:
if (micros.mixedMeasures.size() == 0) {
throw new UnsupportedOperationException();
}
// Algorithm:
//
// For the mixed-units measurement of: "3 yard, 1 foot, 2.6 inch", we should
// find "3 yard" and "1 foot" in micros.mixedMeasures.
//
// Obtain long-names with plural forms corresponding to measure values:
// * {0} yards, {0} foot, {0} inches
//
// Format the integer values appropriately and modify with the format
// strings:
// - 3 yards, 1 foot
//
// Use ListFormatter to combine, with one placeholder:
// - 3 yards, 1 foot and {0} inches /* TODO: how about the case of `1 inch` */
//
// Return a SimpleModifier for this pattern, letting the rest of the
// pipeline take care of the remaining inches.
List<String> outputMeasuresList = new ArrayList<>();
for (int i = 0; i < micros.mixedMeasures.size(); i++) {
DecimalQuantity fdec = new DecimalQuantity_DualStorageBCD(micros.mixedMeasures.get(i).getNumber());
StandardPlural pluralForm = fdec.getStandardPlural(rules);
String simpleFormat = LongNameHandler.getWithPlural(this.fMixedUnitData.get(i), pluralForm);
SimpleFormatter compiledFormatter = SimpleFormatter.compileMinMaxArguments(simpleFormat, 0, 1);
FormattedStringBuilder appendable = new FormattedStringBuilder();
this.fIntegerFormatter.formatImpl(fdec, appendable);
outputMeasuresList.add(compiledFormatter.format(appendable.toString()));
// TODO: fix this issue https://github.com/icu-units/icu/issues/67
}
String[] finalSimpleFormats = this.fMixedUnitData.get(this.fMixedUnitData.size() - 1);
StandardPlural finalPlural = RoundingUtils.getPluralSafe(micros.rounder, rules, quantity);
String finalSimpleFormat = LongNameHandler.getWithPlural(finalSimpleFormats, finalPlural);
SimpleFormatter finalFormatter = SimpleFormatter.compileMinMaxArguments(finalSimpleFormat, 0, 1);
finalFormatter.format("{0}", outputMeasuresList.get(outputMeasuresList.size() -1));
// Combine list into a "premixed" pattern
String premixedFormatPattern = this.fListFormatter.format(outputMeasuresList);
SimpleFormatter premixedCompiled = SimpleFormatter.compileMinMaxArguments(premixedFormatPattern, 0, 1);
// Return a SimpleModifier for the "premixed" pattern
Modifier.Parameters params = new Modifier.Parameters();
params.obj = this;
params.signum = Modifier.Signum.POS_ZERO;
params.plural = finalPlural;
return new SimpleModifier(premixedCompiled.getTextWithNoArguments(), null, false, params);
/*TODO: it was SimpleModifier(premixedCompiled, kUndefinedField, false, {this, SIGNUM_POS_ZERO, finalPlural});*/
}
}