ICU-8610 Refactoring and renaming entities in Java implementation. Adding lots of comments. Should be ready to start C++ port.
X-SVN-Rev: 41141
This commit is contained in:
parent
fa6c8972ea
commit
c725920cff
@ -32,29 +32,35 @@ import com.ibm.icu.util.StringTrieBuilder;
|
||||
*/
|
||||
class NumberSkeletonImpl {
|
||||
|
||||
static enum StemType {
|
||||
OTHER,
|
||||
COMPACT_NOTATION,
|
||||
SCIENTIFIC_NOTATION,
|
||||
SIMPLE_NOTATION,
|
||||
NO_UNIT,
|
||||
CURRENCY,
|
||||
MEASURE_UNIT,
|
||||
PER_MEASURE_UNIT,
|
||||
ROUNDER,
|
||||
FRACTION_ROUNDER,
|
||||
MAYBE_INCREMENT_ROUNDER,
|
||||
CURRENCY_ROUNDER,
|
||||
GROUPING,
|
||||
INTEGER_WIDTH,
|
||||
LATIN,
|
||||
NUMBERING_SYSTEM,
|
||||
UNIT_WIDTH,
|
||||
SIGN_DISPLAY,
|
||||
DECIMAL_DISPLAY
|
||||
/**
|
||||
* While parsing a skeleton, this enum records what type of option we expect to find next.
|
||||
*/
|
||||
static enum ParseState {
|
||||
// Section 0: We expect whitespace or a stem, but not an option:
|
||||
STATE_NULL,
|
||||
|
||||
// Section 1: We might accept an option, but it is not required:
|
||||
STATE_SCIENTIFIC,
|
||||
STATE_ROUNDER,
|
||||
STATE_FRACTION_ROUNDER,
|
||||
|
||||
// Section 2: An option is required:
|
||||
STATE_INCREMENT_ROUNDER,
|
||||
STATE_MEASURE_UNIT,
|
||||
STATE_PER_MEASURE_UNIT,
|
||||
STATE_CURRENCY_UNIT,
|
||||
STATE_INTEGER_WIDTH,
|
||||
STATE_NUMBERING_SYSTEM,
|
||||
}
|
||||
|
||||
static enum ActualStem {
|
||||
/**
|
||||
* All possible stem literals have an entry in the StemEnum. The enum name is the kebab case stem
|
||||
* string literal written in upper snake case.
|
||||
*
|
||||
* @see StemToObject
|
||||
* @see #SERIALIZED_STEM_TRIE
|
||||
*/
|
||||
static enum StemEnum {
|
||||
// Section 1: Stems that do not require an option:
|
||||
STEM_COMPACT_SHORT,
|
||||
STEM_COMPACT_LONG,
|
||||
@ -98,9 +104,16 @@ class NumberSkeletonImpl {
|
||||
STEM_NUMBERING_SYSTEM,
|
||||
};
|
||||
|
||||
static final ActualStem[] ACTUAL_STEM_VALUES = ActualStem.values();
|
||||
/** For mapping from ordinal back to StemEnum in Java. */
|
||||
static final StemEnum[] STEM_ENUM_VALUES = StemEnum.values();
|
||||
|
||||
private static Notation stemToNotation(ActualStem stem) {
|
||||
/**
|
||||
* Utility class for methods that convert from StemEnum to corresponding objects or enums. This
|
||||
* applies to only the "Section 1" stems, those that are well-defined without an option.
|
||||
*/
|
||||
static final class StemToObject {
|
||||
|
||||
private static Notation notation(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_COMPACT_SHORT:
|
||||
return Notation.compactShort();
|
||||
@ -117,7 +130,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static MeasureUnit stemToUnit(ActualStem stem) {
|
||||
private static MeasureUnit unit(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_BASE_UNIT:
|
||||
return NoUnit.BASE;
|
||||
@ -130,7 +143,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static Rounder stemToRounder(ActualStem stem) {
|
||||
private static Rounder rounder(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_ROUND_INTEGER:
|
||||
return Rounder.integer();
|
||||
@ -145,7 +158,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static GroupingStrategy stemToGroupingStrategy(ActualStem stem) {
|
||||
private static GroupingStrategy groupingStrategy(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_GROUP_OFF:
|
||||
return GroupingStrategy.OFF;
|
||||
@ -162,7 +175,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static UnitWidth stemToUnitWidth(ActualStem stem) {
|
||||
private static UnitWidth unitWidth(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_UNIT_WIDTH_NARROW:
|
||||
return UnitWidth.NARROW;
|
||||
@ -179,7 +192,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static SignDisplay stemToSignDisplay(ActualStem stem) {
|
||||
private static SignDisplay signDisplay(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_SIGN_AUTO:
|
||||
return SignDisplay.AUTO;
|
||||
@ -200,7 +213,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static DecimalSeparatorDisplay stemToDecimalSeparatorDisplay(ActualStem stem) {
|
||||
private static DecimalSeparatorDisplay decimalSeparatorDisplay(StemEnum stem) {
|
||||
switch (stem) {
|
||||
case STEM_DECIMAL_AUTO:
|
||||
return DecimalSeparatorDisplay.AUTO;
|
||||
@ -210,8 +223,15 @@ class NumberSkeletonImpl {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void groupingStrategyToStemString(GroupingStrategy value, StringBuilder sb) {
|
||||
/**
|
||||
* Utility class for methods that convert from enums to stem strings. More complex object conversions
|
||||
* take place in ObjectToStemString.
|
||||
*/
|
||||
static final class EnumToStemString {
|
||||
|
||||
private static void groupingStrategy(GroupingStrategy value, StringBuilder sb) {
|
||||
switch (value) {
|
||||
case OFF:
|
||||
sb.append("group-off");
|
||||
@ -233,7 +253,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static void unitWidthToStemString(UnitWidth value, StringBuilder sb) {
|
||||
private static void unitWidth(UnitWidth value, StringBuilder sb) {
|
||||
switch (value) {
|
||||
case NARROW:
|
||||
sb.append("unit-width-narrow");
|
||||
@ -255,7 +275,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static void signDisplayToStemString(SignDisplay value, StringBuilder sb) {
|
||||
private static void signDisplay(SignDisplay value, StringBuilder sb) {
|
||||
switch (value) {
|
||||
case AUTO:
|
||||
sb.append("sign-auto");
|
||||
@ -283,7 +303,7 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static void decimalSeparatorDisplayToStemString(DecimalSeparatorDisplay value, StringBuilder sb) {
|
||||
private static void decimalSeparatorDisplay(DecimalSeparatorDisplay value, StringBuilder sb) {
|
||||
switch (value) {
|
||||
case AUTO:
|
||||
sb.append("decimal-auto");
|
||||
@ -295,58 +315,61 @@ class NumberSkeletonImpl {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A data structure for mapping from stem strings to the stem enum. Built at startup. */
|
||||
static final String SERIALIZED_STEM_TRIE = buildStemTrie();
|
||||
|
||||
static String buildStemTrie() {
|
||||
CharsTrieBuilder b = new CharsTrieBuilder();
|
||||
|
||||
// Section 1:
|
||||
b.add("compact-short", ActualStem.STEM_COMPACT_SHORT.ordinal());
|
||||
b.add("compact-long", ActualStem.STEM_COMPACT_LONG.ordinal());
|
||||
b.add("scientific", ActualStem.STEM_SCIENTIFIC.ordinal());
|
||||
b.add("engineering", ActualStem.STEM_ENGINEERING.ordinal());
|
||||
b.add("notation-simple", ActualStem.STEM_NOTATION_SIMPLE.ordinal());
|
||||
b.add("base-unit", ActualStem.STEM_BASE_UNIT.ordinal());
|
||||
b.add("percent", ActualStem.STEM_PERCENT.ordinal());
|
||||
b.add("permille", ActualStem.STEM_PERMILLE.ordinal());
|
||||
b.add("round-integer", ActualStem.STEM_ROUND_INTEGER.ordinal());
|
||||
b.add("round-unlimited", ActualStem.STEM_ROUND_UNLIMITED.ordinal());
|
||||
b.add("round-currency-standard", ActualStem.STEM_ROUND_CURRENCY_STANDARD.ordinal());
|
||||
b.add("round-currency-cash", ActualStem.STEM_ROUND_CURRENCY_CASH.ordinal());
|
||||
b.add("group-off", ActualStem.STEM_GROUP_OFF.ordinal());
|
||||
b.add("group-min2", ActualStem.STEM_GROUP_MIN2.ordinal());
|
||||
b.add("group-auto", ActualStem.STEM_GROUP_AUTO.ordinal());
|
||||
b.add("group-on-aligned", ActualStem.STEM_GROUP_ON_ALIGNED.ordinal());
|
||||
b.add("group-thousands", ActualStem.STEM_GROUP_THOUSANDS.ordinal());
|
||||
b.add("latin", ActualStem.STEM_LATIN.ordinal());
|
||||
b.add("unit-width-narrow", ActualStem.STEM_UNIT_WIDTH_NARROW.ordinal());
|
||||
b.add("unit-width-short", ActualStem.STEM_UNIT_WIDTH_SHORT.ordinal());
|
||||
b.add("unit-width-full-name", ActualStem.STEM_UNIT_WIDTH_FULL_NAME.ordinal());
|
||||
b.add("unit-width-iso-code", ActualStem.STEM_UNIT_WIDTH_ISO_CODE.ordinal());
|
||||
b.add("unit-width-hidden", ActualStem.STEM_UNIT_WIDTH_HIDDEN.ordinal());
|
||||
b.add("sign-auto", ActualStem.STEM_SIGN_AUTO.ordinal());
|
||||
b.add("sign-always", ActualStem.STEM_SIGN_ALWAYS.ordinal());
|
||||
b.add("sign-never", ActualStem.STEM_SIGN_NEVER.ordinal());
|
||||
b.add("sign-accounting", ActualStem.STEM_SIGN_ACCOUNTING.ordinal());
|
||||
b.add("sign-accounting-always", ActualStem.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal());
|
||||
b.add("sign-except-zero", ActualStem.STEM_SIGN_EXCEPT_ZERO.ordinal());
|
||||
b.add("sign-accounting-except-zero", ActualStem.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal());
|
||||
b.add("decimal-auto", ActualStem.STEM_DECIMAL_AUTO.ordinal());
|
||||
b.add("decimal-always", ActualStem.STEM_DECIMAL_ALWAYS.ordinal());
|
||||
b.add("compact-short", StemEnum.STEM_COMPACT_SHORT.ordinal());
|
||||
b.add("compact-long", StemEnum.STEM_COMPACT_LONG.ordinal());
|
||||
b.add("scientific", StemEnum.STEM_SCIENTIFIC.ordinal());
|
||||
b.add("engineering", StemEnum.STEM_ENGINEERING.ordinal());
|
||||
b.add("notation-simple", StemEnum.STEM_NOTATION_SIMPLE.ordinal());
|
||||
b.add("base-unit", StemEnum.STEM_BASE_UNIT.ordinal());
|
||||
b.add("percent", StemEnum.STEM_PERCENT.ordinal());
|
||||
b.add("permille", StemEnum.STEM_PERMILLE.ordinal());
|
||||
b.add("round-integer", StemEnum.STEM_ROUND_INTEGER.ordinal());
|
||||
b.add("round-unlimited", StemEnum.STEM_ROUND_UNLIMITED.ordinal());
|
||||
b.add("round-currency-standard", StemEnum.STEM_ROUND_CURRENCY_STANDARD.ordinal());
|
||||
b.add("round-currency-cash", StemEnum.STEM_ROUND_CURRENCY_CASH.ordinal());
|
||||
b.add("group-off", StemEnum.STEM_GROUP_OFF.ordinal());
|
||||
b.add("group-min2", StemEnum.STEM_GROUP_MIN2.ordinal());
|
||||
b.add("group-auto", StemEnum.STEM_GROUP_AUTO.ordinal());
|
||||
b.add("group-on-aligned", StemEnum.STEM_GROUP_ON_ALIGNED.ordinal());
|
||||
b.add("group-thousands", StemEnum.STEM_GROUP_THOUSANDS.ordinal());
|
||||
b.add("latin", StemEnum.STEM_LATIN.ordinal());
|
||||
b.add("unit-width-narrow", StemEnum.STEM_UNIT_WIDTH_NARROW.ordinal());
|
||||
b.add("unit-width-short", StemEnum.STEM_UNIT_WIDTH_SHORT.ordinal());
|
||||
b.add("unit-width-full-name", StemEnum.STEM_UNIT_WIDTH_FULL_NAME.ordinal());
|
||||
b.add("unit-width-iso-code", StemEnum.STEM_UNIT_WIDTH_ISO_CODE.ordinal());
|
||||
b.add("unit-width-hidden", StemEnum.STEM_UNIT_WIDTH_HIDDEN.ordinal());
|
||||
b.add("sign-auto", StemEnum.STEM_SIGN_AUTO.ordinal());
|
||||
b.add("sign-always", StemEnum.STEM_SIGN_ALWAYS.ordinal());
|
||||
b.add("sign-never", StemEnum.STEM_SIGN_NEVER.ordinal());
|
||||
b.add("sign-accounting", StemEnum.STEM_SIGN_ACCOUNTING.ordinal());
|
||||
b.add("sign-accounting-always", StemEnum.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal());
|
||||
b.add("sign-except-zero", StemEnum.STEM_SIGN_EXCEPT_ZERO.ordinal());
|
||||
b.add("sign-accounting-except-zero", StemEnum.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal());
|
||||
b.add("decimal-auto", StemEnum.STEM_DECIMAL_AUTO.ordinal());
|
||||
b.add("decimal-always", StemEnum.STEM_DECIMAL_ALWAYS.ordinal());
|
||||
|
||||
// Section 2:
|
||||
b.add("round-increment", ActualStem.STEM_ROUND_INCREMENT.ordinal());
|
||||
b.add("measure-unit", ActualStem.STEM_MEASURE_UNIT.ordinal());
|
||||
b.add("per-measure-unit", ActualStem.STEM_PER_MEASURE_UNIT.ordinal());
|
||||
b.add("currency", ActualStem.STEM_CURRENCY.ordinal());
|
||||
b.add("integer-width", ActualStem.STEM_INTEGER_WIDTH.ordinal());
|
||||
b.add("numbering-system", ActualStem.STEM_NUMBERING_SYSTEM.ordinal());
|
||||
b.add("round-increment", StemEnum.STEM_ROUND_INCREMENT.ordinal());
|
||||
b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal());
|
||||
b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal());
|
||||
b.add("currency", StemEnum.STEM_CURRENCY.ordinal());
|
||||
b.add("integer-width", StemEnum.STEM_INTEGER_WIDTH.ordinal());
|
||||
b.add("numbering-system", StemEnum.STEM_NUMBERING_SYSTEM.ordinal());
|
||||
|
||||
// TODO: Use SLOW or FAST here?
|
||||
return b.buildCharSequence(StringTrieBuilder.Option.FAST).toString();
|
||||
}
|
||||
|
||||
/** Kebab case versions of the rounding mode enum. */
|
||||
static final String[] ROUNDING_MODE_STRINGS = {
|
||||
"up",
|
||||
"down",
|
||||
@ -357,6 +380,9 @@ class NumberSkeletonImpl {
|
||||
"half-even",
|
||||
"unnecessary" };
|
||||
|
||||
///// ENTRYPOINT FUNCTIONS /////
|
||||
|
||||
/** Cache for parsed skeleton strings. */
|
||||
private static final CacheBase<String, UnlocalizedNumberFormatter, Void> cache = new SoftCache<String, UnlocalizedNumberFormatter, Void>() {
|
||||
@Override
|
||||
protected UnlocalizedNumberFormatter createInstance(String skeletonString, Void unused) {
|
||||
@ -403,8 +429,11 @@ class NumberSkeletonImpl {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/////
|
||||
///// MAIN PARSING FUNCTIONS /////
|
||||
|
||||
/**
|
||||
* Converts from a skeleton string to a MacroProps. This method contains the primary parse loop.
|
||||
*/
|
||||
private static MacroProps parseSkeleton(String skeletonString) {
|
||||
// Add a trailing whitespace to the end of the skeleton string to make code cleaner.
|
||||
skeletonString += " ";
|
||||
@ -412,8 +441,9 @@ class NumberSkeletonImpl {
|
||||
MacroProps macros = new MacroProps();
|
||||
StringSegment segment = new StringSegment(skeletonString, false);
|
||||
CharsTrie stemTrie = new CharsTrie(SERIALIZED_STEM_TRIE, 0);
|
||||
StemType stem = null;
|
||||
ParseState stem = ParseState.STATE_NULL;
|
||||
int offset = 0;
|
||||
// Primary skeleton parse loop:
|
||||
while (offset < segment.length()) {
|
||||
int cp = segment.codePointAt(offset);
|
||||
boolean isTokenSeparator = PatternProps.isWhiteSpace(cp);
|
||||
@ -422,7 +452,7 @@ class NumberSkeletonImpl {
|
||||
if (!isTokenSeparator && !isOptionSeparator) {
|
||||
// Non-separator token; consume it.
|
||||
offset += Character.charCount(cp);
|
||||
if (stem == null) {
|
||||
if (stem == ParseState.STATE_NULL) {
|
||||
// We are currently consuming a stem.
|
||||
// Go to the next state in the stem trie.
|
||||
stemTrie.nextForCodePoint(cp);
|
||||
@ -435,58 +465,75 @@ class NumberSkeletonImpl {
|
||||
// Otherwise, make sure it is a valid repeating separator.
|
||||
if (offset != 0) {
|
||||
segment.setLength(offset);
|
||||
if (stem == null) {
|
||||
if (stem == ParseState.STATE_NULL) {
|
||||
// The first separator after the start of a token. Parse it as a stem.
|
||||
stem = parseStem2(segment, stemTrie, macros);
|
||||
stem = parseStem(segment, stemTrie, macros);
|
||||
stemTrie.reset();
|
||||
} else {
|
||||
// A separator after the first separator of a token. Parse it as an option.
|
||||
stem = parseOption(stem, segment, macros);
|
||||
}
|
||||
segment.resetLength();
|
||||
segment.adjustOffset(offset + 1);
|
||||
|
||||
// Consume the segment:
|
||||
segment.adjustOffset(offset);
|
||||
offset = 0;
|
||||
|
||||
} else if (stem != null) {
|
||||
} else if (stem != ParseState.STATE_NULL) {
|
||||
// A separator ('/' or whitespace) following an option separator ('/')
|
||||
segment.setLength(Character.charCount(cp)); // for error message
|
||||
throw new SkeletonSyntaxException("Unexpected separator character", segment);
|
||||
|
||||
} else {
|
||||
// Two spaces in a row; this is OK.
|
||||
segment.adjustOffset(Character.charCount(cp));
|
||||
}
|
||||
|
||||
// Make sure we aren't in a state requiring an option, and then reset the state.
|
||||
if (isTokenSeparator && stem != null) {
|
||||
// Does the current stem forbid options?
|
||||
if (isOptionSeparator && stem == ParseState.STATE_NULL) {
|
||||
segment.setLength(Character.charCount(cp)); // for error message
|
||||
throw new SkeletonSyntaxException("Unexpected option separator", segment);
|
||||
}
|
||||
|
||||
// Does the current stem require an option?
|
||||
if (isTokenSeparator && stem != ParseState.STATE_NULL) {
|
||||
switch (stem) {
|
||||
case MAYBE_INCREMENT_ROUNDER:
|
||||
case MEASURE_UNIT:
|
||||
case PER_MEASURE_UNIT:
|
||||
case CURRENCY:
|
||||
case INTEGER_WIDTH:
|
||||
case NUMBERING_SYSTEM:
|
||||
case STATE_INCREMENT_ROUNDER:
|
||||
case STATE_MEASURE_UNIT:
|
||||
case STATE_PER_MEASURE_UNIT:
|
||||
case STATE_CURRENCY_UNIT:
|
||||
case STATE_INTEGER_WIDTH:
|
||||
case STATE_NUMBERING_SYSTEM:
|
||||
segment.setLength(Character.charCount(cp)); // for error message
|
||||
throw new SkeletonSyntaxException("Stem requires an option", segment);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
stem = null;
|
||||
stem = ParseState.STATE_NULL;
|
||||
}
|
||||
|
||||
// Consume the separator:
|
||||
segment.adjustOffset(Character.charCount(cp));
|
||||
}
|
||||
assert stem == null;
|
||||
assert stem == ParseState.STATE_NULL;
|
||||
return macros;
|
||||
}
|
||||
|
||||
private static StemType parseStem2(StringSegment segment, CharsTrie stemTrie, MacroProps macros) {
|
||||
/**
|
||||
* Given that the current segment represents an stem, parse it and save the result.
|
||||
*
|
||||
* @return The next state after parsing this stem, corresponding to what subset of options to expect.
|
||||
*/
|
||||
private static ParseState parseStem(StringSegment segment, CharsTrie stemTrie, MacroProps macros) {
|
||||
// First check for "blueprint" stems, which start with a "signal char"
|
||||
switch (segment.charAt(0)) {
|
||||
case '.':
|
||||
checkNull(macros.rounder, segment);
|
||||
parseFractionStem(segment, macros);
|
||||
return StemType.FRACTION_ROUNDER;
|
||||
BlueprintHelpers.parseFractionStem(segment, macros);
|
||||
return ParseState.STATE_FRACTION_ROUNDER;
|
||||
case '@':
|
||||
checkNull(macros.rounder, segment);
|
||||
parseDigitsStem(segment, macros);
|
||||
return StemType.OTHER;
|
||||
BlueprintHelpers.parseDigitsStem(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
}
|
||||
|
||||
// Now look at the stemsTrie, which is already be pointing at our stem.
|
||||
@ -497,8 +544,8 @@ class NumberSkeletonImpl {
|
||||
throw new SkeletonSyntaxException("Unknown stem", segment);
|
||||
}
|
||||
|
||||
ActualStem stemEnum = ACTUAL_STEM_VALUES[stemTrie.getValue()];
|
||||
switch (stemEnum) {
|
||||
StemEnum stem = STEM_ENUM_VALUES[stemTrie.getValue()];
|
||||
switch (stem) {
|
||||
|
||||
// Stems with meaning on their own, not requiring an option:
|
||||
|
||||
@ -508,33 +555,33 @@ class NumberSkeletonImpl {
|
||||
case STEM_ENGINEERING:
|
||||
case STEM_NOTATION_SIMPLE:
|
||||
checkNull(macros.notation, segment);
|
||||
macros.notation = stemToNotation(stemEnum);
|
||||
switch (stemEnum) {
|
||||
macros.notation = StemToObject.notation(stem);
|
||||
switch (stem) {
|
||||
case STEM_SCIENTIFIC:
|
||||
case STEM_ENGINEERING:
|
||||
return StemType.SCIENTIFIC_NOTATION; // allows for scientific options
|
||||
return ParseState.STATE_SCIENTIFIC; // allows for scientific options
|
||||
default:
|
||||
return StemType.OTHER;
|
||||
return ParseState.STATE_NULL;
|
||||
}
|
||||
|
||||
case STEM_BASE_UNIT:
|
||||
case STEM_PERCENT:
|
||||
case STEM_PERMILLE:
|
||||
checkNull(macros.unit, segment);
|
||||
macros.unit = stemToUnit(stemEnum);
|
||||
return StemType.OTHER;
|
||||
macros.unit = StemToObject.unit(stem);
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
case STEM_ROUND_INTEGER:
|
||||
case STEM_ROUND_UNLIMITED:
|
||||
case STEM_ROUND_CURRENCY_STANDARD:
|
||||
case STEM_ROUND_CURRENCY_CASH:
|
||||
checkNull(macros.rounder, segment);
|
||||
macros.rounder = stemToRounder(stemEnum);
|
||||
switch (stemEnum) {
|
||||
macros.rounder = StemToObject.rounder(stem);
|
||||
switch (stem) {
|
||||
case STEM_ROUND_INTEGER:
|
||||
return StemType.FRACTION_ROUNDER; // allows for "round-integer/@##"
|
||||
return ParseState.STATE_FRACTION_ROUNDER; // allows for "round-integer/@##"
|
||||
default:
|
||||
return StemType.ROUNDER; // allows for rounding mode options
|
||||
return ParseState.STATE_ROUNDER; // allows for rounding mode options
|
||||
}
|
||||
|
||||
case STEM_GROUP_OFF:
|
||||
@ -543,13 +590,13 @@ class NumberSkeletonImpl {
|
||||
case STEM_GROUP_ON_ALIGNED:
|
||||
case STEM_GROUP_THOUSANDS:
|
||||
checkNull(macros.grouping, segment);
|
||||
macros.grouping = stemToGroupingStrategy(stemEnum);
|
||||
return StemType.OTHER;
|
||||
macros.grouping = StemToObject.groupingStrategy(stem);
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
case STEM_LATIN:
|
||||
checkNull(macros.symbols, segment);
|
||||
macros.symbols = NumberingSystem.LATIN;
|
||||
return StemType.OTHER;
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
case STEM_UNIT_WIDTH_NARROW:
|
||||
case STEM_UNIT_WIDTH_SHORT:
|
||||
@ -557,8 +604,8 @@ class NumberSkeletonImpl {
|
||||
case STEM_UNIT_WIDTH_ISO_CODE:
|
||||
case STEM_UNIT_WIDTH_HIDDEN:
|
||||
checkNull(macros.unitWidth, segment);
|
||||
macros.unitWidth = stemToUnitWidth(stemEnum);
|
||||
return StemType.OTHER;
|
||||
macros.unitWidth = StemToObject.unitWidth(stem);
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
case STEM_SIGN_AUTO:
|
||||
case STEM_SIGN_ALWAYS:
|
||||
@ -568,99 +615,104 @@ class NumberSkeletonImpl {
|
||||
case STEM_SIGN_EXCEPT_ZERO:
|
||||
case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
|
||||
checkNull(macros.sign, segment);
|
||||
macros.sign = stemToSignDisplay(stemEnum);
|
||||
return StemType.OTHER;
|
||||
macros.sign = StemToObject.signDisplay(stem);
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
case STEM_DECIMAL_AUTO:
|
||||
case STEM_DECIMAL_ALWAYS:
|
||||
checkNull(macros.decimal, segment);
|
||||
macros.decimal = stemToDecimalSeparatorDisplay(stemEnum);
|
||||
return StemType.OTHER;
|
||||
macros.decimal = StemToObject.decimalSeparatorDisplay(stem);
|
||||
return ParseState.STATE_NULL;
|
||||
|
||||
// Stems requiring an option:
|
||||
|
||||
case STEM_ROUND_INCREMENT:
|
||||
checkNull(macros.rounder, segment);
|
||||
return StemType.MAYBE_INCREMENT_ROUNDER;
|
||||
return ParseState.STATE_INCREMENT_ROUNDER;
|
||||
|
||||
case STEM_MEASURE_UNIT:
|
||||
checkNull(macros.unit, segment);
|
||||
return StemType.MEASURE_UNIT;
|
||||
return ParseState.STATE_MEASURE_UNIT;
|
||||
|
||||
case STEM_PER_MEASURE_UNIT:
|
||||
checkNull(macros.perUnit, segment);
|
||||
return StemType.PER_MEASURE_UNIT;
|
||||
return ParseState.STATE_PER_MEASURE_UNIT;
|
||||
|
||||
case STEM_CURRENCY:
|
||||
checkNull(macros.unit, segment);
|
||||
return StemType.CURRENCY;
|
||||
return ParseState.STATE_CURRENCY_UNIT;
|
||||
|
||||
case STEM_INTEGER_WIDTH:
|
||||
checkNull(macros.integerWidth, segment);
|
||||
return StemType.INTEGER_WIDTH;
|
||||
return ParseState.STATE_INTEGER_WIDTH;
|
||||
|
||||
case STEM_NUMBERING_SYSTEM:
|
||||
checkNull(macros.symbols, segment);
|
||||
return StemType.NUMBERING_SYSTEM;
|
||||
return ParseState.STATE_NUMBERING_SYSTEM;
|
||||
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private static StemType parseOption(StemType stem, StringSegment segment, MacroProps macros) {
|
||||
/**
|
||||
* Given that the current segment represents an option, parse it and save the result.
|
||||
*
|
||||
* @return The next state after parsing this option, corresponding to what subset of options to
|
||||
* expect next.
|
||||
*/
|
||||
private static ParseState parseOption(ParseState stem, StringSegment segment, MacroProps macros) {
|
||||
|
||||
///// Required options: /////
|
||||
|
||||
switch (stem) {
|
||||
case CURRENCY:
|
||||
parseCurrencyOption(segment, macros);
|
||||
return StemType.OTHER;
|
||||
case MEASURE_UNIT:
|
||||
parseMeasureUnitOption(segment, macros);
|
||||
return StemType.OTHER;
|
||||
case PER_MEASURE_UNIT:
|
||||
parseMeasurePerUnitOption(segment, macros);
|
||||
return StemType.OTHER;
|
||||
case MAYBE_INCREMENT_ROUNDER:
|
||||
parseIncrementOption(segment, macros);
|
||||
return StemType.ROUNDER;
|
||||
case INTEGER_WIDTH:
|
||||
parseIntegerWidthOption(segment, macros);
|
||||
return StemType.OTHER;
|
||||
case NUMBERING_SYSTEM:
|
||||
parseNumberingSystemOption(segment, macros);
|
||||
return StemType.OTHER;
|
||||
case STATE_CURRENCY_UNIT:
|
||||
BlueprintHelpers.parseCurrencyOption(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
case STATE_MEASURE_UNIT:
|
||||
BlueprintHelpers.parseMeasureUnitOption(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
case STATE_PER_MEASURE_UNIT:
|
||||
BlueprintHelpers.parseMeasurePerUnitOption(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
case STATE_INCREMENT_ROUNDER:
|
||||
BlueprintHelpers.parseIncrementOption(segment, macros);
|
||||
return ParseState.STATE_ROUNDER;
|
||||
case STATE_INTEGER_WIDTH:
|
||||
BlueprintHelpers.parseIntegerWidthOption(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
case STATE_NUMBERING_SYSTEM:
|
||||
BlueprintHelpers.parseNumberingSystemOption(segment, macros);
|
||||
return ParseState.STATE_NULL;
|
||||
}
|
||||
|
||||
///// Non-required options: /////
|
||||
|
||||
// Scientific options
|
||||
switch (stem) {
|
||||
case SCIENTIFIC_NOTATION:
|
||||
if (parseExponentWidthOption(segment, macros)) {
|
||||
return StemType.SCIENTIFIC_NOTATION;
|
||||
case STATE_SCIENTIFIC:
|
||||
if (BlueprintHelpers.parseExponentWidthOption(segment, macros)) {
|
||||
return ParseState.STATE_SCIENTIFIC;
|
||||
}
|
||||
if (parseExponentSignOption(segment, macros)) {
|
||||
return StemType.SCIENTIFIC_NOTATION;
|
||||
if (BlueprintHelpers.parseExponentSignOption(segment, macros)) {
|
||||
return ParseState.STATE_SCIENTIFIC;
|
||||
}
|
||||
}
|
||||
|
||||
// Frac-sig option
|
||||
switch (stem) {
|
||||
case FRACTION_ROUNDER:
|
||||
if (parseFracSigOption(segment, macros)) {
|
||||
return StemType.ROUNDER;
|
||||
case STATE_FRACTION_ROUNDER:
|
||||
if (BlueprintHelpers.parseFracSigOption(segment, macros)) {
|
||||
return ParseState.STATE_ROUNDER;
|
||||
}
|
||||
}
|
||||
|
||||
// Rounding mode option
|
||||
switch (stem) {
|
||||
case ROUNDER:
|
||||
case FRACTION_ROUNDER:
|
||||
case CURRENCY_ROUNDER:
|
||||
if (parseRoundingModeOption(segment, macros)) {
|
||||
return StemType.ROUNDER;
|
||||
case STATE_ROUNDER:
|
||||
case STATE_FRACTION_ROUNDER:
|
||||
if (BlueprintHelpers.parseRoundingModeOption(segment, macros)) {
|
||||
return ParseState.STATE_ROUNDER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,36 +720,38 @@ class NumberSkeletonImpl {
|
||||
throw new SkeletonSyntaxException("Invalid option", segment);
|
||||
}
|
||||
|
||||
///// MAIN SKELETON GENERATION FUNCTION /////
|
||||
|
||||
private static void generateSkeleton(MacroProps macros, StringBuilder sb) {
|
||||
// Supported options
|
||||
if (macros.notation != null && generateNotationValue(macros, sb)) {
|
||||
if (macros.notation != null && GeneratorHelpers.notation(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.unit != null && generateUnitValue(macros, sb)) {
|
||||
if (macros.unit != null && GeneratorHelpers.unit(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.perUnit != null && generatePerUnitValue(macros, sb)) {
|
||||
if (macros.perUnit != null && GeneratorHelpers.perUnit(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.rounder != null && generateRoundingValue(macros, sb)) {
|
||||
if (macros.rounder != null && GeneratorHelpers.rounding(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.grouping != null && generateGroupingValue(macros, sb)) {
|
||||
if (macros.grouping != null && GeneratorHelpers.grouping(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.integerWidth != null && generateIntegerWidthValue(macros, sb)) {
|
||||
if (macros.integerWidth != null && GeneratorHelpers.integerWidth(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.symbols != null && generateSymbolsValue(macros, sb)) {
|
||||
if (macros.symbols != null && GeneratorHelpers.symbols(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.unitWidth != null && generateUnitWidthValue(macros, sb)) {
|
||||
if (macros.unitWidth != null && GeneratorHelpers.unitWidth(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.sign != null && generateSignValue(macros, sb)) {
|
||||
if (macros.sign != null && GeneratorHelpers.sign(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
if (macros.decimal != null && generateDecimalValue(macros, sb)) {
|
||||
if (macros.decimal != null && GeneratorHelpers.decimal(macros, sb)) {
|
||||
sb.append(' ');
|
||||
}
|
||||
|
||||
@ -725,8 +779,9 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/////
|
||||
///// BLUEPRINT HELPER FUNCTIONS (stem and options that cannot be interpreted literally) /////
|
||||
|
||||
static final class BlueprintHelpers {
|
||||
private static boolean parseExponentWidthOption(StringSegment segment, MacroProps macros) {
|
||||
if (segment.charAt(0) != '+') {
|
||||
return false;
|
||||
@ -758,10 +813,11 @@ class NumberSkeletonImpl {
|
||||
// TODO: Make this more efficient (avoid object allocation)? It shouldn't be very hot code.
|
||||
CharsTrie tempStemTrie = new CharsTrie(SERIALIZED_STEM_TRIE, 0);
|
||||
BytesTrie.Result result = tempStemTrie.next(segment, 0, segment.length());
|
||||
if (result != BytesTrie.Result.INTERMEDIATE_VALUE && result != BytesTrie.Result.FINAL_VALUE) {
|
||||
if (result != BytesTrie.Result.INTERMEDIATE_VALUE
|
||||
&& result != BytesTrie.Result.FINAL_VALUE) {
|
||||
return false;
|
||||
}
|
||||
SignDisplay sign = stemToSignDisplay(ACTUAL_STEM_VALUES[tempStemTrie.getValue()]);
|
||||
SignDisplay sign = StemToObject.signDisplay(STEM_ENUM_VALUES[tempStemTrie.getValue()]);
|
||||
if (sign == null) {
|
||||
return false;
|
||||
}
|
||||
@ -769,10 +825,6 @@ class NumberSkeletonImpl {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void generateCurrencyOption(Currency currency, StringBuilder sb) {
|
||||
sb.append(currency.getCurrencyCode());
|
||||
}
|
||||
|
||||
private static void parseCurrencyOption(StringSegment segment, MacroProps macros) {
|
||||
String currencyCode = segment.subSequence(0, segment.length()).toString();
|
||||
try {
|
||||
@ -782,6 +834,10 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateCurrencyOption(Currency currency, StringBuilder sb) {
|
||||
sb.append(currency.getCurrencyCode());
|
||||
}
|
||||
|
||||
private static void parseMeasureUnitOption(StringSegment segment, MacroProps macros) {
|
||||
// NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
|
||||
// http://unicode.org/reports/tr35/#Validity_Data
|
||||
@ -809,7 +865,8 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
|
||||
private static void parseMeasurePerUnitOption(StringSegment segment, MacroProps macros) {
|
||||
// A little bit of a hack: safe the current unit (numerator), call the main measure unit parsing
|
||||
// A little bit of a hack: safe the current unit (numerator), call the main measure unit
|
||||
// parsing
|
||||
// code, put back the numerator unit, and put the new unit into per-unit.
|
||||
MeasureUnit numerator = macros.unit;
|
||||
parseMeasureUnitOption(segment, macros);
|
||||
@ -945,7 +1002,8 @@ class NumberSkeletonImpl {
|
||||
offset++;
|
||||
} else if (minSig > 1) {
|
||||
// @@#, @@##, @@@#
|
||||
throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment);
|
||||
throw new SkeletonSyntaxException("Invalid digits option for fraction rounder",
|
||||
segment);
|
||||
} else {
|
||||
maxSig = minSig;
|
||||
for (; offset < segment.length(); offset++) {
|
||||
@ -1065,10 +1123,13 @@ class NumberSkeletonImpl {
|
||||
private static void generateNumberingSystemOption(NumberingSystem ns, StringBuilder sb) {
|
||||
sb.append(ns.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/////
|
||||
///// STEM GENERATION HELPER FUNCTIONS /////
|
||||
|
||||
private static boolean generateNotationValue(MacroProps macros, StringBuilder sb) {
|
||||
static final class GeneratorHelpers {
|
||||
|
||||
private static boolean notation(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.notation instanceof CompactNotation) {
|
||||
if (macros.notation == Notation.compactLong()) {
|
||||
sb.append("compact-long");
|
||||
@ -1091,11 +1152,11 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
if (impl.minExponentDigits > 1) {
|
||||
sb.append('/');
|
||||
generateExponentWidthOption(impl.minExponentDigits, sb);
|
||||
BlueprintHelpers.generateExponentWidthOption(impl.minExponentDigits, sb);
|
||||
}
|
||||
if (impl.exponentSignDisplay != SignDisplay.AUTO) {
|
||||
sb.append('/');
|
||||
signDisplayToStemString(impl.exponentSignDisplay, sb);
|
||||
EnumToStemString.signDisplay(impl.exponentSignDisplay, sb);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@ -1105,10 +1166,10 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean generateUnitValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean unit(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.unit instanceof Currency) {
|
||||
sb.append("currency/");
|
||||
generateCurrencyOption((Currency) macros.unit, sb);
|
||||
BlueprintHelpers.generateCurrencyOption((Currency) macros.unit, sb);
|
||||
return true;
|
||||
} else if (macros.unit instanceof NoUnit) {
|
||||
if (macros.unit == NoUnit.PERCENT) {
|
||||
@ -1124,45 +1185,45 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
} else {
|
||||
sb.append("measure-unit/");
|
||||
generateMeasureUnitOption(macros.unit, sb);
|
||||
BlueprintHelpers.generateMeasureUnitOption(macros.unit, sb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean generatePerUnitValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean perUnit(MacroProps macros, StringBuilder sb) {
|
||||
// Per-units are currently expected to be only MeasureUnits.
|
||||
if (macros.unit instanceof Currency || macros.unit instanceof NoUnit) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Cannot generate number skeleton with per-unit that is not a standard measure unit");
|
||||
} else {
|
||||
sb.append("per-measure-unit/");
|
||||
generateMeasureUnitOption(macros.perUnit, sb);
|
||||
BlueprintHelpers.generateMeasureUnitOption(macros.perUnit, sb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean generateRoundingValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean rounding(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.rounder instanceof Rounder.InfiniteRounderImpl) {
|
||||
sb.append("round-unlimited");
|
||||
} else if (macros.rounder instanceof Rounder.FractionRounderImpl) {
|
||||
Rounder.FractionRounderImpl impl = (Rounder.FractionRounderImpl) macros.rounder;
|
||||
generateFractionStem(impl.minFrac, impl.maxFrac, sb);
|
||||
BlueprintHelpers.generateFractionStem(impl.minFrac, impl.maxFrac, sb);
|
||||
} else if (macros.rounder instanceof Rounder.SignificantRounderImpl) {
|
||||
Rounder.SignificantRounderImpl impl = (Rounder.SignificantRounderImpl) macros.rounder;
|
||||
generateDigitsStem(impl.minSig, impl.maxSig, sb);
|
||||
BlueprintHelpers.generateDigitsStem(impl.minSig, impl.maxSig, sb);
|
||||
} else if (macros.rounder instanceof Rounder.FracSigRounderImpl) {
|
||||
Rounder.FracSigRounderImpl impl = (Rounder.FracSigRounderImpl) macros.rounder;
|
||||
generateFractionStem(impl.minFrac, impl.maxFrac, sb);
|
||||
BlueprintHelpers.generateFractionStem(impl.minFrac, impl.maxFrac, sb);
|
||||
sb.append('/');
|
||||
if (impl.minSig == -1) {
|
||||
generateDigitsStem(1, impl.maxSig, sb);
|
||||
BlueprintHelpers.generateDigitsStem(1, impl.maxSig, sb);
|
||||
} else {
|
||||
generateDigitsStem(impl.minSig, -1, sb);
|
||||
BlueprintHelpers.generateDigitsStem(impl.minSig, -1, sb);
|
||||
}
|
||||
} else if (macros.rounder instanceof Rounder.IncrementRounderImpl) {
|
||||
Rounder.IncrementRounderImpl impl = (Rounder.IncrementRounderImpl) macros.rounder;
|
||||
sb.append("round-increment/");
|
||||
generateIncrementOption(impl.increment, sb);
|
||||
BlueprintHelpers.generateIncrementOption(impl.increment, sb);
|
||||
} else if (macros.rounder instanceof Rounder.InfiniteRounderImpl) {
|
||||
sb.append("round-unlimited");
|
||||
} else {
|
||||
@ -1178,19 +1239,20 @@ class NumberSkeletonImpl {
|
||||
// Generate the options
|
||||
if (macros.rounder.mathContext != Rounder.DEFAULT_MATH_CONTEXT) {
|
||||
sb.append('/');
|
||||
generateRoundingModeOption(macros.rounder.mathContext.getRoundingMode(), sb);
|
||||
BlueprintHelpers.generateRoundingModeOption(macros.rounder.mathContext.getRoundingMode(),
|
||||
sb);
|
||||
}
|
||||
|
||||
// NOTE: Always return true for rounding because the default value depends on other options.
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean generateGroupingValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean grouping(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.grouping instanceof GroupingStrategy) {
|
||||
if (macros.grouping == GroupingStrategy.AUTO) {
|
||||
return false; // Default value
|
||||
}
|
||||
groupingStrategyToStemString((GroupingStrategy) macros.grouping, sb);
|
||||
EnumToStemString.groupingStrategy((GroupingStrategy) macros.grouping, sb);
|
||||
return true;
|
||||
} else {
|
||||
throw new UnsupportedOperationException(
|
||||
@ -1198,23 +1260,25 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean generateIntegerWidthValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean integerWidth(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.integerWidth.equals(IntegerWidth.DEFAULT)) {
|
||||
return false; // Default
|
||||
}
|
||||
sb.append("integer-width/");
|
||||
generateIntegerWidthOption(macros.integerWidth.minInt, macros.integerWidth.maxInt, sb);
|
||||
BlueprintHelpers.generateIntegerWidthOption(macros.integerWidth.minInt,
|
||||
macros.integerWidth.maxInt,
|
||||
sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean generateSymbolsValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean symbols(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.symbols instanceof NumberingSystem) {
|
||||
NumberingSystem ns = (NumberingSystem) macros.symbols;
|
||||
if (ns.getName().equals("latn")) {
|
||||
sb.append("latin");
|
||||
} else {
|
||||
sb.append("numbering-system/");
|
||||
generateNumberingSystemOption(ns, sb);
|
||||
BlueprintHelpers.generateNumberingSystemOption(ns, sb);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@ -1224,31 +1288,33 @@ class NumberSkeletonImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean generateUnitWidthValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean unitWidth(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.unitWidth == UnitWidth.SHORT) {
|
||||
return false; // Default value
|
||||
}
|
||||
unitWidthToStemString(macros.unitWidth, sb);
|
||||
EnumToStemString.unitWidth(macros.unitWidth, sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean generateSignValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean sign(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.sign == SignDisplay.AUTO) {
|
||||
return false; // Default value
|
||||
}
|
||||
signDisplayToStemString(macros.sign, sb);
|
||||
EnumToStemString.signDisplay(macros.sign, sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean generateDecimalValue(MacroProps macros, StringBuilder sb) {
|
||||
private static boolean decimal(MacroProps macros, StringBuilder sb) {
|
||||
if (macros.decimal == DecimalSeparatorDisplay.AUTO) {
|
||||
return false; // Default value
|
||||
}
|
||||
decimalSeparatorDisplayToStemString(macros.decimal, sb);
|
||||
EnumToStemString.decimalSeparatorDisplay(macros.decimal, sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
/////
|
||||
}
|
||||
|
||||
///// OTHER UTILITY FUNCTIONS /////
|
||||
|
||||
private static void checkNull(Object value, CharSequence content) {
|
||||
if (value != null) {
|
||||
|
@ -3,7 +3,6 @@
|
||||
package com.ibm.icu.dev.test.number;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@ -129,7 +128,6 @@ public class NumberSkeletonTest {
|
||||
"scientific/ee",
|
||||
"round-increment/xxx",
|
||||
"round-increment/0.1.2",
|
||||
"group-thousands/foo",
|
||||
"currency/dummy",
|
||||
"measure-unit/foo",
|
||||
"integer-width/xxx",
|
||||
@ -161,6 +159,25 @@ public class NumberSkeletonTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unexpectedTokens() {
|
||||
String[] cases = {
|
||||
"group-thousands/foo",
|
||||
"round-integer//ceiling group-off",
|
||||
"round-integer//ceiling group-off",
|
||||
"round-integer/ group-off",
|
||||
"round-integer// group-off" };
|
||||
|
||||
for (String cas : cases) {
|
||||
try {
|
||||
NumberFormatter.fromSkeleton(cas);
|
||||
fail();
|
||||
} catch (SkeletonSyntaxException expected) {
|
||||
assertTrue(expected.getMessage(), expected.getMessage().contains("Unexpected"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stemsRequiringOption() {
|
||||
String[] stems = { "round-increment", "currency", "measure-unit", "integer-width", };
|
||||
@ -202,24 +219,14 @@ public class NumberSkeletonTest {
|
||||
{ "round-integer group-off", "5142" },
|
||||
{ "round-integer group-off", "5142" },
|
||||
{ "round-integer/ceiling group-off", "5143" },
|
||||
{ "round-integer//ceiling group-off", null },
|
||||
{ "round-integer/ceiling group-off", "5143" },
|
||||
{ "round-integer//ceiling group-off", null },
|
||||
{ "round-integer/ group-off", null },
|
||||
{ "round-integer// group-off", null } };
|
||||
{ "round-integer/ceiling group-off", "5143" }, };
|
||||
|
||||
for (String[] cas : cases) {
|
||||
String skeleton = cas[0];
|
||||
String expected = cas[1];
|
||||
|
||||
try {
|
||||
String actual = NumberFormatter.fromSkeleton(skeleton).locale(ULocale.ENGLISH)
|
||||
.format(5142.3).toString();
|
||||
String actual = NumberFormatter.fromSkeleton(skeleton).locale(ULocale.ENGLISH).format(5142.3)
|
||||
.toString();
|
||||
assertEquals(skeleton, expected, actual);
|
||||
} catch (SkeletonSyntaxException e) {
|
||||
// Expected failure?
|
||||
assertNull(skeleton, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user