mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-21 12:40:02 +00:00
ICU-8610 Adds skeleton support for measure units.
X-SVN-Rev: 41014
This commit is contained in:
parent
abb8788d23
commit
59e4fc5172
3 changed files with 91 additions and 14 deletions
|
@ -81,9 +81,7 @@ public class StringSegment implements CharSequence {
|
|||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
throw new AssertionError(); // Never used
|
||||
// Possible implementation:
|
||||
// return str.subSequence(start + this.start, end + this.start);
|
||||
return str.subSequence(start + this.start, end + this.start);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,12 +6,17 @@ import java.math.BigDecimal;
|
|||
import java.math.RoundingMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.ibm.icu.impl.PatternProps;
|
||||
import com.ibm.icu.impl.StringSegment;
|
||||
import com.ibm.icu.impl.number.MacroProps;
|
||||
import com.ibm.icu.number.NumberFormatter.UnitWidth;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.Currency.CurrencyUsage;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.NoUnit;
|
||||
|
||||
/**
|
||||
* @author sffc
|
||||
|
@ -20,7 +25,7 @@ import com.ibm.icu.util.Currency.CurrencyUsage;
|
|||
class NumberSkeletonImpl {
|
||||
|
||||
static enum StemType {
|
||||
ROUNDER, FRACTION_ROUNDER, MAYBE_INCREMENT_ROUNDER, CURRENCY_ROUNDER
|
||||
OTHER, ROUNDER, FRACTION_ROUNDER, MAYBE_INCREMENT_ROUNDER, CURRENCY_ROUNDER, MEASURE_UNIT, UNIT_WIDTH
|
||||
}
|
||||
|
||||
static class SkeletonDataStructure {
|
||||
|
@ -62,6 +67,12 @@ class NumberSkeletonImpl {
|
|||
"round-currency-standard",
|
||||
Rounder.currency(CurrencyUsage.STANDARD));
|
||||
skeletonData.put(StemType.ROUNDER, "round-currency-cash", Rounder.currency(CurrencyUsage.CASH));
|
||||
|
||||
skeletonData.put(StemType.UNIT_WIDTH, "unit-width-narrow", UnitWidth.NARROW);
|
||||
skeletonData.put(StemType.UNIT_WIDTH, "unit-width-short", UnitWidth.SHORT);
|
||||
skeletonData.put(StemType.UNIT_WIDTH, "unit-width-full-name", UnitWidth.FULL_NAME);
|
||||
skeletonData.put(StemType.UNIT_WIDTH, "unit-width-iso-code", UnitWidth.ISO_CODE);
|
||||
skeletonData.put(StemType.UNIT_WIDTH, "unit-width-hidden", UnitWidth.HIDDEN);
|
||||
}
|
||||
|
||||
private static final Map<String, UnlocalizedNumberFormatter> cache = new ConcurrentHashMap<String, UnlocalizedNumberFormatter>();
|
||||
|
@ -171,6 +182,7 @@ class NumberSkeletonImpl {
|
|||
// Check for stems that require an option
|
||||
switch (stem) {
|
||||
case MAYBE_INCREMENT_ROUNDER:
|
||||
case MEASURE_UNIT:
|
||||
throw new SkeletonSyntaxException("Stem requires an option", segment);
|
||||
default:
|
||||
break;
|
||||
|
@ -192,6 +204,10 @@ class NumberSkeletonImpl {
|
|||
checkNull(macros.rounder, content);
|
||||
macros.rounder = (Rounder) value;
|
||||
break;
|
||||
case UNIT_WIDTH:
|
||||
checkNull(macros.unitWidth, content);
|
||||
macros.unitWidth = (UnitWidth) value;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
}
|
||||
|
@ -201,6 +217,8 @@ class NumberSkeletonImpl {
|
|||
// Second try: literal stems that require an option
|
||||
if (content.equals("round-increment")) {
|
||||
return StemType.MAYBE_INCREMENT_ROUNDER;
|
||||
} else if (content.equals("measure-unit")) {
|
||||
return StemType.MEASURE_UNIT;
|
||||
}
|
||||
|
||||
// Second try: stem "blueprint" syntax
|
||||
|
@ -249,6 +267,14 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
}
|
||||
|
||||
// Measure unit option
|
||||
switch (stem) {
|
||||
case MEASURE_UNIT:
|
||||
// The measure unit option is required.
|
||||
parseMeasureUnitOption(content, macros);
|
||||
return StemType.OTHER;
|
||||
}
|
||||
|
||||
// Unknown option
|
||||
throw new SkeletonSyntaxException("Unknown option", content);
|
||||
}
|
||||
|
@ -260,6 +286,14 @@ class NumberSkeletonImpl {
|
|||
generateRoundingValue(macros, sb);
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.unit != null) {
|
||||
generateUnitValue(macros, sb);
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.unitWidth != null) {
|
||||
generateUnitWidthValue(macros, sb);
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
// Remove the trailing space
|
||||
if (sb.length() > 0) {
|
||||
|
@ -390,15 +424,11 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
|
||||
private static void parseIncrementOption(CharSequence content, MacroProps macros) {
|
||||
// Clunkilly convert the CharSequence to a char array for the BigDecimal constructor.
|
||||
// We can't use content.toString() because that doesn't create a clean string.
|
||||
char[] chars = new char[content.length()];
|
||||
for (int i = 0; i < content.length(); i++) {
|
||||
chars[i] = content.charAt(i);
|
||||
}
|
||||
// Call content.subSequence() because content.toString() doesn't create a clean string.
|
||||
String str = content.subSequence(0, content.length()).toString();
|
||||
BigDecimal increment;
|
||||
try {
|
||||
increment = new BigDecimal(chars);
|
||||
increment = new BigDecimal(str);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SkeletonSyntaxException("Invalid rounding increment", content, e);
|
||||
}
|
||||
|
@ -425,6 +455,29 @@ class NumberSkeletonImpl {
|
|||
sb.append(mode.toString());
|
||||
}
|
||||
|
||||
private static void parseMeasureUnitOption(CharSequence content, MacroProps macros) {
|
||||
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
|
||||
// http://unicode.org/reports/tr35/#Validity_Data
|
||||
int firstHyphen = 0;
|
||||
while (firstHyphen < content.length() && content.charAt(firstHyphen) != '-') {
|
||||
firstHyphen++;
|
||||
}
|
||||
String type = content.subSequence(0, firstHyphen).toString();
|
||||
String subType = content.subSequence(firstHyphen + 1, content.length()).toString();
|
||||
Set<MeasureUnit> units = MeasureUnit.getAvailable(type);
|
||||
for (MeasureUnit unit : units) {
|
||||
if (subType.equals(unit.getSubtype())) {
|
||||
macros.unit = unit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new SkeletonSyntaxException("Unknown unit", content);
|
||||
}
|
||||
|
||||
private static void generateMeasureUnitOption(MeasureUnit unit, StringBuilder sb) {
|
||||
sb.append(unit.getType() + "-" + unit.getSubtype());
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
private static void generateRoundingValue(MacroProps macros, StringBuilder sb) {
|
||||
|
@ -474,6 +527,32 @@ class NumberSkeletonImpl {
|
|||
}
|
||||
}
|
||||
|
||||
private static void generateUnitValue(MacroProps macros, StringBuilder sb) {
|
||||
// Check for literals
|
||||
String literal = skeletonData.valueToStem(macros.unit);
|
||||
if (literal != null) {
|
||||
sb.append(literal);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the stem
|
||||
if (macros.unit instanceof Currency) {
|
||||
// TODO
|
||||
} else if (macros.unit instanceof NoUnit) {
|
||||
// TODO
|
||||
} else {
|
||||
sb.append("measure-unit/");
|
||||
generateMeasureUnitOption(macros.unit, sb);
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateUnitWidthValue(MacroProps macros, StringBuilder sb) {
|
||||
// There should be a literal.
|
||||
String literal = skeletonData.valueToStem(macros.unitWidth);
|
||||
assert literal != null;
|
||||
sb.append(literal);
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
private static void checkNull(Object value, CharSequence content) {
|
||||
|
|
|
@ -377,7 +377,7 @@ public class NumberFormatterApiTest {
|
|||
public void unitMeasure() {
|
||||
assertFormatDescending(
|
||||
"Meters Short",
|
||||
"U:length:meter",
|
||||
"measure-unit/length-meter",
|
||||
NumberFormatter.with().unit(MeasureUnit.METER),
|
||||
ULocale.ENGLISH,
|
||||
"87,650 m",
|
||||
|
@ -392,7 +392,7 @@ public class NumberFormatterApiTest {
|
|||
|
||||
assertFormatDescending(
|
||||
"Meters Long",
|
||||
"U:length:meter unit-width=FULL_NAME",
|
||||
"measure-unit/length-meter unit-width-full-name",
|
||||
NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME),
|
||||
ULocale.ENGLISH,
|
||||
"87,650 meters",
|
||||
|
@ -407,7 +407,7 @@ public class NumberFormatterApiTest {
|
|||
|
||||
assertFormatDescending(
|
||||
"Compact Meters Long",
|
||||
"CC U:length:meter unit-width=FULL_NAME",
|
||||
"compact-long measure-unit/length-meter unit-width-full-name",
|
||||
NumberFormatter.with().notation(Notation.compactLong()).unit(MeasureUnit.METER)
|
||||
.unitWidth(UnitWidth.FULL_NAME),
|
||||
ULocale.ENGLISH,
|
||||
|
|
Loading…
Add table
Reference in a new issue