ICU-13117 More renaming and refactoring in Java with no behavior changes.
X-SVN-Rev: 40392
This commit is contained in:
parent
1f1a485c3c
commit
c444c0c561
@ -270,6 +270,7 @@ public class AffixUtils {
|
||||
* @param output The NumberStringBuilder to mutate with the result.
|
||||
* @param position The index into the NumberStringBuilder to insert the the string.
|
||||
* @param provider An object to generate locale symbols.
|
||||
* @return The length of the string added to affixPattern.
|
||||
*/
|
||||
public static int unescape(
|
||||
CharSequence affixPattern,
|
||||
@ -294,6 +295,32 @@ public class AffixUtils {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sames as {@link #unescape}, but only calculates the code point count. More efficient than {@link #unescape}
|
||||
* if you only need the length but not the string itself.
|
||||
*
|
||||
* @param affixPattern The original string to be unescaped.
|
||||
* @param provider An object to generate locale symbols.
|
||||
* @return The number of code points in the unescaped string.
|
||||
*/
|
||||
public static int unescapedCodePointCount(CharSequence affixPattern, SymbolProvider provider) {
|
||||
int length = 0;
|
||||
long tag = 0L;
|
||||
while (hasNext(tag, affixPattern)) {
|
||||
tag = nextToken(tag, affixPattern);
|
||||
int typeOrCp = getTypeOrCp(tag);
|
||||
if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
|
||||
length += 1;
|
||||
} else if (typeOrCp < 0) {
|
||||
CharSequence symbol = provider.getSymbol(typeOrCp);
|
||||
length += Character.codePointCount(symbol, 0, symbol.length());
|
||||
} else {
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given affix pattern contains at least one token of the given type, which is
|
||||
* one of the constants "TYPE_" in {@link AffixUtils}.
|
||||
|
@ -122,36 +122,6 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
|
||||
*/
|
||||
public StandardPlural getStandardPlural(PluralRules rules);
|
||||
|
||||
// /**
|
||||
// * @return The number of fraction digits, always in the closed interval [minFrac, maxFrac].
|
||||
// * @see #setIntegerFractionLength(int, int, int, int)
|
||||
// */
|
||||
// public int fractionCount();
|
||||
//
|
||||
// /**
|
||||
// * @return The number of integer digits, always in the closed interval [minInt, maxInt].
|
||||
// * @see #setIntegerFractionLength(int, int, int, int)
|
||||
// */
|
||||
// public int integerCount();
|
||||
//
|
||||
// /**
|
||||
// * @param index The index of the fraction digit relative to the decimal place, or 1 minus the
|
||||
// * digit's power of ten.
|
||||
// * @return The digit at the specified index. Undefined if index is greater than maxInt or less
|
||||
// * than 0.
|
||||
// * @see #fractionCount()
|
||||
// */
|
||||
// public byte getFractionDigit(int index);
|
||||
//
|
||||
// /**
|
||||
// * @param index The index of the integer digit relative to the decimal place, or the digit's power
|
||||
// * of ten.
|
||||
// * @return The digit at the specified index. Undefined if index is greater than maxInt or less
|
||||
// * than 0.
|
||||
// * @see #integerCount()
|
||||
// */
|
||||
// public byte getIntegerDigit(int index);
|
||||
|
||||
/**
|
||||
* Gets the digit at the specified magnitude. For example, if the represented number is 12.3,
|
||||
* getDigit(-1) returns 3, since 3 is the digit corresponding to 10^-1.
|
||||
@ -177,6 +147,11 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
|
||||
*/
|
||||
public int getLowerDisplayMagnitude();
|
||||
|
||||
/**
|
||||
* Returns the string in "plain" format (no exponential notation) using ASCII digits.
|
||||
*/
|
||||
public String toPlainString();
|
||||
|
||||
/**
|
||||
* Like clone, but without the restrictions of the Cloneable interface clone.
|
||||
*
|
||||
@ -184,6 +159,11 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
|
||||
*/
|
||||
public DecimalQuantity createCopy();
|
||||
|
||||
/**
|
||||
* Sets this instance to be equal to another instance.
|
||||
*
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
public void copyFrom(DecimalQuantity other);
|
||||
|
||||
/** This method is for internal testing only. */
|
||||
|
@ -44,7 +44,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
* @see #INFINITY_FLAG
|
||||
* @see #NAN_FLAG
|
||||
*/
|
||||
protected int flags;
|
||||
protected byte flags;
|
||||
|
||||
protected static final int NEGATIVE_FLAG = 1;
|
||||
protected static final int INFINITY_FLAG = 2;
|
||||
@ -168,12 +168,12 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void roundToIncrement(BigDecimal roundingInterval, MathContext mathContext) {
|
||||
public void roundToIncrement(BigDecimal roundingIncrement, MathContext mathContext) {
|
||||
// TODO: Avoid converting back and forth to BigDecimal.
|
||||
BigDecimal temp = toBigDecimal();
|
||||
temp =
|
||||
temp.divide(roundingInterval, 0, mathContext.getRoundingMode())
|
||||
.multiply(roundingInterval)
|
||||
temp.divide(roundingIncrement, 0, mathContext.getRoundingMode())
|
||||
.multiply(roundingIncrement)
|
||||
.round(mathContext);
|
||||
if (temp.signum() == 0) {
|
||||
setBcdToZero(); // keeps negative flag for -0.0
|
||||
@ -467,34 +467,35 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
int delta = origDelta;
|
||||
setBcdToZero();
|
||||
|
||||
// Call the slow oracle function
|
||||
String temp = Double.toString(n);
|
||||
// Call the slow oracle function (Double.toString in Java, sprintf in C++).
|
||||
String dstr = Double.toString(n);
|
||||
|
||||
if (temp.indexOf('E') != -1) {
|
||||
if (dstr.indexOf('E') != -1) {
|
||||
// Case 1: Exponential notation.
|
||||
assert temp.indexOf('.') == 1;
|
||||
int expPos = temp.indexOf('E');
|
||||
_setToLong(Long.parseLong(temp.charAt(0) + temp.substring(2, expPos)));
|
||||
scale += Integer.parseInt(temp.substring(expPos + 1)) - (expPos - 1) + 1;
|
||||
} else if (temp.charAt(0) == '0') {
|
||||
assert dstr.indexOf('.') == 1;
|
||||
int expPos = dstr.indexOf('E');
|
||||
_setToLong(Long.parseLong(dstr.charAt(0) + dstr.substring(2, expPos)));
|
||||
scale += Integer.parseInt(dstr.substring(expPos + 1)) - (expPos - 1) + 1;
|
||||
} else if (dstr.charAt(0) == '0') {
|
||||
// Case 2: Fraction-only number.
|
||||
assert temp.indexOf('.') == 1;
|
||||
_setToLong(Long.parseLong(temp.substring(2)));
|
||||
scale += 2 - temp.length();
|
||||
} else if (temp.charAt(temp.length() - 1) == '0') {
|
||||
assert dstr.indexOf('.') == 1;
|
||||
_setToLong(Long.parseLong(dstr.substring(2)));
|
||||
scale += 2 - dstr.length();
|
||||
} else if (dstr.charAt(dstr.length() - 1) == '0') {
|
||||
// Case 3: Integer-only number.
|
||||
// Note: this path should not normally happen, because integer-only numbers are captured
|
||||
// before the approximate double logic is performed.
|
||||
assert temp.indexOf('.') == temp.length() - 2;
|
||||
assert temp.length() - 2 <= 18;
|
||||
_setToLong(Long.parseLong(temp.substring(0, temp.length() - 2)));
|
||||
assert dstr.indexOf('.') == dstr.length() - 2;
|
||||
assert dstr.length() - 2 <= 18;
|
||||
_setToLong(Long.parseLong(dstr.substring(0, dstr.length() - 2)));
|
||||
// no need to adjust scale
|
||||
} else {
|
||||
// Case 4: Number with both a fraction and an integer.
|
||||
int decimalPos = temp.indexOf('.');
|
||||
_setToLong(Long.parseLong(temp.substring(0, decimalPos) + temp.substring(decimalPos + 1)));
|
||||
scale += decimalPos - temp.length() + 1;
|
||||
int decimalPos = dstr.indexOf('.');
|
||||
_setToLong(Long.parseLong(dstr.substring(0, decimalPos) + dstr.substring(decimalPos + 1)));
|
||||
scale += decimalPos - dstr.length() + 1;
|
||||
}
|
||||
|
||||
scale += delta;
|
||||
compact();
|
||||
explicitExactDouble = true;
|
||||
@ -640,6 +641,9 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
return diff;
|
||||
}
|
||||
|
||||
private static final int SECTION_LOWER_EDGE = -1;
|
||||
private static final int SECTION_UPPER_EDGE = -2;
|
||||
|
||||
@Override
|
||||
public void roundToMagnitude(int magnitude, MathContext mathContext) {
|
||||
// The position in the BCD at which rounding will be performed; digits to the right of position
|
||||
@ -689,7 +693,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
int p = safeSubtract(position, 2);
|
||||
int minP = Math.max(0, precision - 14);
|
||||
if (leadingDigit == 0) {
|
||||
section = -1;
|
||||
section = SECTION_LOWER_EDGE;
|
||||
for (; p >= minP; p--) {
|
||||
if (getDigitPos(p) != 0) {
|
||||
section = RoundingUtils.SECTION_LOWER;
|
||||
@ -711,7 +715,7 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
}
|
||||
}
|
||||
} else if (leadingDigit == 9) {
|
||||
section = -2;
|
||||
section = SECTION_UPPER_EDGE;
|
||||
for (; p >= minP; p--) {
|
||||
if (getDigitPos(p) != 9) {
|
||||
section = RoundingUtils.SECTION_UPPER;
|
||||
@ -747,8 +751,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
}
|
||||
|
||||
// Good to continue rounding.
|
||||
if (section == -1) section = RoundingUtils.SECTION_LOWER;
|
||||
if (section == -2) section = RoundingUtils.SECTION_UPPER;
|
||||
if (section == SECTION_LOWER_EDGE) section = RoundingUtils.SECTION_LOWER;
|
||||
if (section == SECTION_UPPER_EDGE) section = RoundingUtils.SECTION_UPPER;
|
||||
}
|
||||
|
||||
boolean roundDown =
|
||||
@ -841,6 +845,20 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPlainString() {
|
||||
// NOTE: This logic is duplicated between here and DecimalQuantity_SimpleStorage.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isNegative()) {
|
||||
sb.append('-');
|
||||
}
|
||||
for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
|
||||
sb.append(getDigit(m));
|
||||
if (m == 0) sb.append('.');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single digit from the BCD list. No internal state is changed by calling this method.
|
||||
*
|
||||
|
@ -144,11 +144,9 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
@Override
|
||||
protected void setBcdToZero() {
|
||||
if (usingBytes) {
|
||||
for (int i = 0; i < precision; i++) {
|
||||
bcdBytes[i] = (byte) 0;
|
||||
}
|
||||
bcdBytes = null;
|
||||
usingBytes = false;
|
||||
}
|
||||
usingBytes = false;
|
||||
bcdLong = 0L;
|
||||
scale = 0;
|
||||
precision = 0;
|
||||
@ -166,7 +164,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
for (; n != 0; n /= 10, i--) {
|
||||
result = (result >>> 4) + (((long) n % 10) << 60);
|
||||
}
|
||||
usingBytes = false;
|
||||
assert !usingBytes;
|
||||
bcdLong = result >>> (i * 4);
|
||||
scale = 0;
|
||||
precision = 16 - i;
|
||||
@ -181,7 +179,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
for (; n != 0L; n /= 10L, i++) {
|
||||
bcdBytes[i] = (byte) (n % 10);
|
||||
}
|
||||
usingBytes = true;
|
||||
assert usingBytes;
|
||||
scale = 0;
|
||||
precision = i;
|
||||
} else {
|
||||
@ -191,7 +189,7 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
result = (result >>> 4) + ((n % 10) << 60);
|
||||
}
|
||||
assert i >= 0;
|
||||
usingBytes = false;
|
||||
assert !usingBytes;
|
||||
bcdLong = result >>> (i * 4);
|
||||
scale = 0;
|
||||
precision = 16 - i;
|
||||
@ -209,7 +207,6 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
bcdBytes[i] = temp[1].byteValue();
|
||||
n = temp[0];
|
||||
}
|
||||
usingBytes = true;
|
||||
scale = 0;
|
||||
precision = i;
|
||||
}
|
||||
@ -218,17 +215,11 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
protected BigDecimal bcdToBigDecimal() {
|
||||
if (usingBytes) {
|
||||
// Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isNegative()) sb.append('-');
|
||||
assert precision > 0;
|
||||
for (int i = precision - 1; i >= 0; i--) {
|
||||
sb.append(getDigitPos(i));
|
||||
BigDecimal result = new BigDecimal(toNumberString());
|
||||
if (isNegative()) {
|
||||
result = result.negate();
|
||||
}
|
||||
if (scale != 0) {
|
||||
sb.append('E');
|
||||
sb.append(scale);
|
||||
}
|
||||
return new BigDecimal(sb.toString());
|
||||
return result;
|
||||
} else {
|
||||
long tempLong = 0L;
|
||||
for (int shift = (precision - 1); shift >= 0; shift--) {
|
||||
@ -289,13 +280,15 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
|
||||
private void ensureCapacity(int capacity) {
|
||||
if (capacity == 0) return;
|
||||
if (bcdBytes == null) {
|
||||
int oldCapacity = usingBytes ? bcdBytes.length : 0;
|
||||
if (!usingBytes) {
|
||||
bcdBytes = new byte[capacity];
|
||||
} else if (bcdBytes.length < capacity) {
|
||||
} else if (oldCapacity < capacity) {
|
||||
byte[] bcd1 = new byte[capacity * 2];
|
||||
System.arraycopy(bcdBytes, 0, bcd1, 0, bcdBytes.length);
|
||||
System.arraycopy(bcdBytes, 0, bcd1, 0, oldCapacity);
|
||||
bcdBytes = bcd1;
|
||||
}
|
||||
usingBytes = true;
|
||||
}
|
||||
|
||||
/** Switches the internal storage mechanism between the 64-bit long and the byte array. */
|
||||
@ -306,8 +299,8 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
for (int i = precision - 1; i >= 0; i--) {
|
||||
bcdLong <<= 4;
|
||||
bcdLong |= bcdBytes[i];
|
||||
bcdBytes[i] = 0;
|
||||
}
|
||||
bcdBytes = null;
|
||||
usingBytes = false;
|
||||
} else {
|
||||
// Change from long to bytes
|
||||
@ -316,19 +309,18 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
bcdBytes[i] = (byte) (bcdLong & 0xf);
|
||||
bcdLong >>>= 4;
|
||||
}
|
||||
usingBytes = true;
|
||||
assert usingBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copyBcdFrom(DecimalQuantity _other) {
|
||||
DecimalQuantity_DualStorageBCD other = (DecimalQuantity_DualStorageBCD) _other;
|
||||
setBcdToZero();
|
||||
if (other.usingBytes) {
|
||||
usingBytes = true;
|
||||
ensureCapacity(other.precision);
|
||||
System.arraycopy(other.bcdBytes, 0, bcdBytes, 0, other.precision);
|
||||
} else {
|
||||
usingBytes = false;
|
||||
bcdLong = other.bcdLong;
|
||||
}
|
||||
}
|
||||
@ -387,29 +379,33 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean usingBytes() {
|
||||
public boolean isUsingBytes() {
|
||||
return usingBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (usingBytes) {
|
||||
for (int i = precision - 1; i >= 0; i--) {
|
||||
sb.append(bcdBytes[i]);
|
||||
}
|
||||
} else {
|
||||
sb.append(Long.toHexString(bcdLong));
|
||||
}
|
||||
return String.format(
|
||||
"<DecimalQuantity4 %s:%d:%d:%s %s %s%s%d>",
|
||||
(lOptPos > 1000 ? "max" : String.valueOf(lOptPos)),
|
||||
"<DecimalQuantity %s:%d:%d:%s %s %s>",
|
||||
(lOptPos > 1000 ? "999" : String.valueOf(lOptPos)),
|
||||
lReqPos,
|
||||
rReqPos,
|
||||
(rOptPos < -1000 ? "min" : String.valueOf(rOptPos)),
|
||||
(rOptPos < -1000 ? "-999" : String.valueOf(rOptPos)),
|
||||
(usingBytes ? "bytes" : "long"),
|
||||
sb,
|
||||
"E",
|
||||
scale);
|
||||
toNumberString());
|
||||
}
|
||||
|
||||
public String toNumberString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (usingBytes) {
|
||||
for (int i = precision - 1; i >= 0; i--) {
|
||||
sb.append(bcdBytes[i]);
|
||||
}
|
||||
} else {
|
||||
sb.append(Long.toHexString(bcdLong));
|
||||
}
|
||||
sb.append("E");
|
||||
sb.append(scale);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
@ -855,6 +855,20 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPlainString() {
|
||||
// NOTE: This logic is duplicated between here and DecimalQuantity_AbstractBCD.
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (isNegative()) {
|
||||
sb.append('-');
|
||||
}
|
||||
for (int m = getUpperDisplayMagnitude(); m >= getLowerDisplayMagnitude(); m--) {
|
||||
sb.append(getDigit(m));
|
||||
if (m == 0) sb.append('.');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static int toRange(int i, int lo, int hi) {
|
||||
if (i < lo) {
|
||||
return lo;
|
||||
|
@ -36,6 +36,11 @@ public interface Modifier {
|
||||
*/
|
||||
public int getPrefixLength();
|
||||
|
||||
/**
|
||||
* Returns the number of code points in the modifier, prefix plus suffix.
|
||||
*/
|
||||
public int getCodePointCount();
|
||||
|
||||
/**
|
||||
* Whether this modifier is strong. If a modifier is strong, it should always be applied immediately and not allowed
|
||||
* to bubble up. With regard to padding, strong modifiers are considered to be on the inside of the prefix and
|
||||
|
@ -78,10 +78,6 @@ public class PatternStringParser {
|
||||
parseToExistingProperties(pattern, properties, PatternStringParser.IGNORE_ROUNDING_NEVER);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION ///
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Contains raw information about the parsed decimal format pattern string.
|
||||
*/
|
||||
@ -198,6 +194,10 @@ public class PatternStringParser {
|
||||
public long paddingEndpoints = 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/// BEGIN RECURSIVE DESCENT PARSER IMPLEMENTATION ///
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/** An internal class used for tracking the cursor during parsing of a pattern string. */
|
||||
private static class ParserState {
|
||||
final String pattern;
|
||||
@ -267,6 +267,9 @@ public class PatternStringParser {
|
||||
if (state.peek() != '*') {
|
||||
return;
|
||||
}
|
||||
if (result.paddingLocation != null) {
|
||||
throw state.toParseException("Cannot have multiple pad specifiers");
|
||||
}
|
||||
result.paddingLocation = paddingLocation;
|
||||
state.next(); // consume the '*'
|
||||
result.paddingEndpoints |= state.offset;
|
||||
@ -519,7 +522,6 @@ public class PatternStringParser {
|
||||
// Note that most data from "negative" is ignored per the specification of DecimalFormat.
|
||||
|
||||
ParsedSubpatternInfo positive = patternInfo.positive;
|
||||
ParsedSubpatternInfo negative = patternInfo.negative;
|
||||
|
||||
boolean ignoreRounding;
|
||||
if (_ignoreRounding == PatternStringParser.IGNORE_ROUNDING_NEVER) {
|
||||
@ -627,7 +629,7 @@ public class PatternStringParser {
|
||||
String posSuffix = patternInfo.getString(0);
|
||||
|
||||
// Padding settings
|
||||
if (positive.paddingEndpoints != 0) {
|
||||
if (positive.paddingLocation != null) {
|
||||
// The width of the positive prefix and suffix templates are included in the padding
|
||||
int paddingWidth = positive.widthExceptAffixes + AffixUtils.estimateLength(posPrefix)
|
||||
+ AffixUtils.estimateLength(posSuffix);
|
||||
@ -657,7 +659,7 @@ public class PatternStringParser {
|
||||
// negative prefix pattern, to prevent default values from overriding the pattern.
|
||||
properties.setPositivePrefixPattern(posPrefix);
|
||||
properties.setPositiveSuffixPattern(posSuffix);
|
||||
if (negative != null) {
|
||||
if (patternInfo.negative != null) {
|
||||
properties.setNegativePrefixPattern(patternInfo
|
||||
.getString(AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN | AffixPatternProvider.Flags.PREFIX));
|
||||
properties.setNegativeSuffixPattern(patternInfo.getString(AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN));
|
||||
|
@ -55,8 +55,9 @@ public class PatternStringUtils {
|
||||
String nsp = properties.getNegativeSuffixPattern();
|
||||
|
||||
// Prefixes
|
||||
if (ppp != null)
|
||||
if (ppp != null) {
|
||||
sb.append(ppp);
|
||||
}
|
||||
AffixUtils.escape(pp, sb);
|
||||
int afterPrefixPos = sb.length();
|
||||
|
||||
@ -99,7 +100,7 @@ public class PatternStringUtils {
|
||||
digitsStringScale = -roundingInterval.scale();
|
||||
// TODO: Check for DoS here?
|
||||
String str = roundingInterval.scaleByPowerOfTen(roundingInterval.scale()).toPlainString();
|
||||
if (str.charAt(0) == '\'') {
|
||||
if (str.charAt(0) == '-') {
|
||||
// TODO: Unsupported operation exception or fail silently?
|
||||
digitsString.append(str, 1, str.length());
|
||||
} else {
|
||||
@ -147,8 +148,9 @@ public class PatternStringUtils {
|
||||
|
||||
// Suffixes
|
||||
int beforeSuffixPos = sb.length();
|
||||
if (psp != null)
|
||||
if (psp != null) {
|
||||
sb.append(psp);
|
||||
}
|
||||
AffixUtils.escape(ps, sb);
|
||||
|
||||
// Resolve Padding
|
||||
|
@ -69,6 +69,11 @@ public class ConstantAffixModifier implements Modifier {
|
||||
return prefix.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
return prefix.codePointCount(0, prefix.length()) + suffix.codePointCount(0, suffix.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrong() {
|
||||
return strong;
|
||||
|
@ -41,6 +41,12 @@ public class ConstantMultiFieldModifier implements Modifier {
|
||||
return prefixChars.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
return Character.codePointCount(prefixChars, 0, prefixChars.length)
|
||||
+ Character.codePointCount(suffixChars, 0, suffixChars.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrong() {
|
||||
return strong;
|
||||
|
@ -53,6 +53,18 @@ public class SimpleModifier implements Modifier {
|
||||
return prefixLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
int count = 0;
|
||||
if (prefixLength > 0) {
|
||||
count += Character.codePointCount(compiledPattern, 2, 2 + prefixLength);
|
||||
}
|
||||
if (suffixLength > 0) {
|
||||
count += Character.codePointCount(compiledPattern, 1 + suffixOffset, 1 + suffixOffset + suffixLength);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrong() {
|
||||
return strong;
|
||||
|
@ -489,6 +489,8 @@ public class PluralRules implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to FixedDecimal, allowing for other implementations.
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@ -526,54 +528,23 @@ public class PluralRules implements Serializable {
|
||||
@Deprecated
|
||||
public static class FixedDecimal extends Number implements Comparable<FixedDecimal>, IFixedDecimal {
|
||||
private static final long serialVersionUID = -4756200506571685661L;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final double source;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final int visibleDecimalDigitCount;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final int visibleDecimalDigitCountWithoutTrailingZeros;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final long decimalDigits;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final long decimalDigitsWithoutTrailingZeros;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final long integerValue;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final boolean hasIntegerValue;
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final boolean isNegative;
|
||||
|
||||
final double source;
|
||||
|
||||
final int visibleDecimalDigitCount;
|
||||
|
||||
final int visibleDecimalDigitCountWithoutTrailingZeros;
|
||||
|
||||
final long decimalDigits;
|
||||
|
||||
final long decimalDigitsWithoutTrailingZeros;
|
||||
|
||||
final long integerValue;
|
||||
|
||||
final boolean hasIntegerValue;
|
||||
|
||||
final boolean isNegative;
|
||||
|
||||
private final int baseFactor;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ import newapi.impl.CompactData;
|
||||
import newapi.impl.MicroProps;
|
||||
import newapi.impl.MicroPropsGenerator;
|
||||
import newapi.impl.MutablePatternModifier;
|
||||
import newapi.impl.MutablePatternModifier.ImmutableMurkyModifier;
|
||||
import newapi.impl.MutablePatternModifier.ImmutablePatternModifier;
|
||||
|
||||
public class CompactNotation extends Notation {
|
||||
|
||||
@ -50,7 +50,7 @@ public class CompactNotation extends Notation {
|
||||
private static class CompactImpl implements MicroPropsGenerator {
|
||||
|
||||
private static class CompactModInfo {
|
||||
public ImmutableMurkyModifier mod;
|
||||
public ImmutablePatternModifier mod;
|
||||
public int numDigits;
|
||||
}
|
||||
|
||||
|
@ -31,11 +31,6 @@ public final class NumberFormatter {
|
||||
AUTO, ALWAYS, NEVER, ACCOUNTING, ACCOUNTING_ALWAYS,
|
||||
}
|
||||
|
||||
public static UnlocalizedNumberFormatter fromSkeleton(String skeleton) {
|
||||
// FIXME
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static UnlocalizedNumberFormatter with() {
|
||||
return BASE;
|
||||
}
|
||||
|
@ -37,14 +37,14 @@ import newapi.impl.Padder;
|
||||
*/
|
||||
class NumberFormatterImpl {
|
||||
|
||||
/** Builds a "safe" MicroPropsGenerator, which is thread-safe and can be used repeatedly. */
|
||||
public static NumberFormatterImpl fromMacros(MacroProps macros) {
|
||||
// Build a "safe" MicroPropsGenerator, which is thread-safe and can be used repeatedly.
|
||||
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, true);
|
||||
return new NumberFormatterImpl(microPropsGenerator);
|
||||
}
|
||||
|
||||
/** Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once. */
|
||||
public static MicroProps applyStatic(MacroProps macros, DecimalQuantity inValue, NumberStringBuilder outString) {
|
||||
// Build an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
|
||||
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, false);
|
||||
MicroProps micros = microPropsGenerator.processQuantity(inValue);
|
||||
microsToString(micros, inValue, outString);
|
||||
@ -55,8 +55,8 @@ class NumberFormatterImpl {
|
||||
|
||||
final MicroPropsGenerator microPropsGenerator;
|
||||
|
||||
private NumberFormatterImpl(MicroPropsGenerator microsGenerator) {
|
||||
this.microPropsGenerator = microsGenerator;
|
||||
private NumberFormatterImpl(MicroPropsGenerator microPropsGenerator) {
|
||||
this.microPropsGenerator = microPropsGenerator;
|
||||
}
|
||||
|
||||
public MicroProps apply(DecimalQuantity inValue, NumberStringBuilder outString) {
|
||||
@ -79,15 +79,14 @@ class NumberFormatterImpl {
|
||||
* If true, the returned MicroPropsGenerator will be thread-safe. If false, the returned value will
|
||||
* <em>not</em> be thread-safe, intended for a single "one-shot" use only. Building the thread-safe
|
||||
* object is more expensive.
|
||||
* @return
|
||||
*/
|
||||
private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, boolean safe) {
|
||||
|
||||
String innerPattern = null;
|
||||
LongNameHandler longNames = null;
|
||||
Rounder defaultRounding = Rounder.unlimited();
|
||||
Rounder defaultRounder = Rounder.unlimited();
|
||||
Currency currency = DEFAULT_CURRENCY;
|
||||
UnitWidth unitWidth = null;
|
||||
UnitWidth unitWidth = (macros.unitWidth == null) ? UnitWidth.SHORT : macros.unitWidth;
|
||||
boolean perMille = false;
|
||||
PluralRules rules = macros.rules;
|
||||
|
||||
@ -121,29 +120,27 @@ class NumberFormatterImpl {
|
||||
} else {
|
||||
innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.CURRENCYSTYLE);
|
||||
}
|
||||
defaultRounding = Rounder.currency(CurrencyUsage.STANDARD);
|
||||
defaultRounder = Rounder.currency(CurrencyUsage.STANDARD);
|
||||
currency = (Currency) macros.unit;
|
||||
micros.useCurrency = true;
|
||||
unitWidth = (macros.unitWidth == null) ? UnitWidth.SHORT : macros.unitWidth;
|
||||
} else if (macros.unit instanceof Currency) {
|
||||
// Currency long name
|
||||
innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.NUMBERSTYLE);
|
||||
longNames = LongNameHandler.getCurrencyLongNameModifiers(macros.loc, (Currency) macros.unit);
|
||||
defaultRounding = Rounder.currency(CurrencyUsage.STANDARD);
|
||||
longNames = LongNameHandler.forCurrencyLongNames(macros.loc, (Currency) macros.unit);
|
||||
defaultRounder = Rounder.currency(CurrencyUsage.STANDARD);
|
||||
currency = (Currency) macros.unit;
|
||||
micros.useCurrency = true;
|
||||
unitWidth = UnitWidth.FULL_NAME;
|
||||
} else {
|
||||
// MeasureUnit
|
||||
innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.NUMBERSTYLE);
|
||||
unitWidth = (macros.unitWidth == null) ? UnitWidth.SHORT : macros.unitWidth;
|
||||
longNames = LongNameHandler.getMeasureUnitModifiers(macros.loc, macros.unit, unitWidth);
|
||||
longNames = LongNameHandler.forMeasureUnit(macros.loc, macros.unit, unitWidth);
|
||||
}
|
||||
|
||||
// Parse the pattern, which is used for grouping and affixes only.
|
||||
ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(innerPattern);
|
||||
|
||||
// Symbols
|
||||
// NOTE: C++ has a special class, SymbolsWrapper, in MacroProps. Java has all the resolution logic here directly.
|
||||
if (macros.symbols == null) {
|
||||
micros.symbols = DecimalFormatSymbols.getInstance(macros.loc);
|
||||
} else if (macros.symbols instanceof DecimalFormatSymbols) {
|
||||
@ -173,7 +170,7 @@ class NumberFormatterImpl {
|
||||
} else if (macros.notation instanceof CompactNotation) {
|
||||
micros.rounding = Rounder.COMPACT_STRATEGY;
|
||||
} else {
|
||||
micros.rounding = Rounder.normalizeType(defaultRounding, currency);
|
||||
micros.rounding = Rounder.normalizeType(defaultRounder, currency);
|
||||
}
|
||||
|
||||
// Grouping strategy
|
||||
@ -186,6 +183,13 @@ class NumberFormatterImpl {
|
||||
micros.grouping = Grouper.normalizeType(Grouper.defaults(), patternInfo);
|
||||
}
|
||||
|
||||
// Padding strategy
|
||||
if (macros.padder != null) {
|
||||
micros.padding = macros.padder;
|
||||
} else {
|
||||
micros.padding = Padder.none();
|
||||
}
|
||||
|
||||
// Inner modifier (scientific notation)
|
||||
if (macros.notation instanceof ScientificNotation) {
|
||||
chain = ((ScientificNotation) macros.notation).withLocaleData(micros.symbols, safe, chain);
|
||||
@ -220,19 +224,12 @@ class NumberFormatterImpl {
|
||||
// Lazily create PluralRules
|
||||
rules = PluralRules.forLocale(macros.loc);
|
||||
}
|
||||
chain = longNames.withLocaleData(rules, safe, chain);
|
||||
chain = longNames.withLocaleData(rules, chain);
|
||||
} else {
|
||||
// No outer modifier required
|
||||
micros.modOuter = ConstantAffixModifier.EMPTY;
|
||||
}
|
||||
|
||||
// Padding strategy
|
||||
if (macros.padder != null) {
|
||||
micros.padding = macros.padder;
|
||||
} else {
|
||||
micros.padding = Padder.none();
|
||||
}
|
||||
|
||||
// Compact notation
|
||||
// NOTE: Compact notation can (but might not) override the middle modifier and rounding.
|
||||
// It therefore needs to go at the end of the chain.
|
||||
@ -272,7 +269,14 @@ class NumberFormatterImpl {
|
||||
int length = writeNumber(micros, quantity, string);
|
||||
// NOTE: When range formatting is added, these modifiers can bubble up.
|
||||
// For now, apply them all here at once.
|
||||
length += micros.padding.applyModsAndMaybePad(micros, string, 0, length);
|
||||
// Always apply the inner modifier (which is "strong").
|
||||
length += micros.modInner.apply(string, 0, length);
|
||||
if (micros.padding.isValid()) {
|
||||
micros.padding.padAndApply(micros.modMiddle, micros.modOuter, string, 0, length);
|
||||
} else {
|
||||
length += micros.modMiddle.apply(string, 0, length);
|
||||
length += micros.modOuter.apply(string, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
private static int writeNumber(MicroProps micros, DecimalQuantity quantity, NumberStringBuilder string) {
|
||||
|
@ -364,6 +364,7 @@ public abstract class NumberFormatterSettings<T extends NumberFormatterSettings<
|
||||
return create(KEY_THRESHOLD, threshold);
|
||||
}
|
||||
|
||||
/** Non-public method */
|
||||
public String toSkeleton() {
|
||||
return SkeletonBuilder.macrosToSkeleton(resolve());
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ import com.ibm.icu.text.NumberFormat;
|
||||
import newapi.NumberFormatter.SignDisplay;
|
||||
import newapi.Rounder.SignificantRounderImpl;
|
||||
import newapi.impl.MicroProps;
|
||||
import newapi.impl.MultiplierProducer;
|
||||
import newapi.impl.MicroPropsGenerator;
|
||||
import newapi.impl.MultiplierProducer;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ScientificNotation extends Notation implements Cloneable {
|
||||
@ -145,6 +145,12 @@ public class ScientificNotation extends Notation implements Cloneable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
// This method is not used for strong modifiers.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrong() {
|
||||
return true;
|
||||
@ -193,6 +199,12 @@ public class ScientificNotation extends Notation implements Cloneable {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
// This method is not used for strong modifiers.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrong() {
|
||||
return true;
|
||||
|
@ -6,37 +6,41 @@ import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.ibm.icu.impl.CurrencyData;
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.SimpleFormatterImpl;
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.UResource;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.Modifier;
|
||||
import com.ibm.icu.impl.number.modifiers.SimpleModifier;
|
||||
import com.ibm.icu.text.NumberFormat.Field;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.ICUException;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
|
||||
public class LongNameHandler implements MicroPropsGenerator {
|
||||
|
||||
private final Map<StandardPlural, Modifier> data;
|
||||
/* unsafe */ PluralRules rules;
|
||||
/* unsafe */ MicroPropsGenerator parent;
|
||||
private final Map<StandardPlural, SimpleModifier> modifiers;
|
||||
private PluralRules rules;
|
||||
private MicroPropsGenerator parent;
|
||||
|
||||
private LongNameHandler(Map<StandardPlural, Modifier> data) {
|
||||
this.data = data;
|
||||
private LongNameHandler(Map<StandardPlural, SimpleModifier> modifiers) {
|
||||
this.modifiers = modifiers;
|
||||
}
|
||||
|
||||
/** For use by the "safe" code path */
|
||||
private LongNameHandler(LongNameHandler other) {
|
||||
this.data = other.data;
|
||||
this.modifiers = other.modifiers;
|
||||
}
|
||||
|
||||
public static LongNameHandler getCurrencyLongNameModifiers(ULocale loc, Currency currency) {
|
||||
public static LongNameHandler forCurrencyLongNames(ULocale loc, Currency currency) {
|
||||
Map<String, String> data = CurrencyData.provider.getInstance(loc, true).getUnitPatterns();
|
||||
Map<StandardPlural, Modifier> result = new EnumMap<StandardPlural, Modifier>(StandardPlural.class);
|
||||
Map<StandardPlural, SimpleModifier> result = new EnumMap<StandardPlural, SimpleModifier>(StandardPlural.class);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> e : data.entrySet()) {
|
||||
String pluralKeyword = e.getKey();
|
||||
@ -45,23 +49,28 @@ public class LongNameHandler implements MicroPropsGenerator {
|
||||
String simpleFormat = e.getValue(); // e.g., "{0} {1}"
|
||||
simpleFormat = simpleFormat.replace("{1}", longName);
|
||||
String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
|
||||
Modifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);
|
||||
SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);
|
||||
result.put(plural, mod);
|
||||
}
|
||||
return new LongNameHandler(result);
|
||||
}
|
||||
|
||||
public static LongNameHandler getMeasureUnitModifiers(ULocale loc, MeasureUnit unit, UnitWidth width) {
|
||||
Map<StandardPlural, String> simpleFormats = MeasureData.getMeasureData(loc, unit, width);
|
||||
Map<StandardPlural, Modifier> result = new EnumMap<StandardPlural, Modifier>(StandardPlural.class);
|
||||
public static LongNameHandler forMeasureUnit(ULocale loc, MeasureUnit unit, UnitWidth width) {
|
||||
Map<StandardPlural, String> simpleFormats = getMeasureData(loc, unit, width);
|
||||
Map<StandardPlural, SimpleModifier> result = new EnumMap<StandardPlural, SimpleModifier>(StandardPlural.class);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (StandardPlural plural : StandardPlural.VALUES) {
|
||||
if (simpleFormats.get(plural) == null) {
|
||||
plural = StandardPlural.OTHER;
|
||||
}
|
||||
String simpleFormat = simpleFormats.get(plural);
|
||||
if (simpleFormat == null) {
|
||||
simpleFormat = simpleFormats.get(StandardPlural.OTHER);
|
||||
}
|
||||
if (simpleFormat == null) {
|
||||
// There should always be data in the "other" plural variant.
|
||||
throw new ICUException("Could not find data in 'other' plural variant for unit " + unit);
|
||||
}
|
||||
String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
|
||||
Modifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);
|
||||
// TODO: What field to use for units?
|
||||
SimpleModifier mod = new SimpleModifier(compiled, null, false);
|
||||
result.put(plural, mod);
|
||||
}
|
||||
return new LongNameHandler(result);
|
||||
@ -72,26 +81,14 @@ public class LongNameHandler implements MicroPropsGenerator {
|
||||
*
|
||||
* @param rules
|
||||
* The PluralRules instance to reference.
|
||||
* @param safe
|
||||
* If true, creates a new object to insert into the quantity chain. If false, re-uses <em>this</em>
|
||||
* object in the quantity chain.
|
||||
* @param parent
|
||||
* The old head of the quantity chain.
|
||||
* @return The new head of the quantity chain.
|
||||
*/
|
||||
public MicroPropsGenerator withLocaleData(PluralRules rules, boolean safe, MicroPropsGenerator parent) {
|
||||
if (safe) {
|
||||
// Safe code path: return a new object
|
||||
LongNameHandler copy = new LongNameHandler(this);
|
||||
copy.rules = rules;
|
||||
copy.parent = parent;
|
||||
return copy;
|
||||
} else {
|
||||
// Unsafe code path: re-use this object!
|
||||
this.rules = rules;
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
public MicroPropsGenerator withLocaleData(PluralRules rules, MicroPropsGenerator parent) {
|
||||
this.rules = rules;
|
||||
this.parent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,7 +97,60 @@ public class LongNameHandler implements MicroPropsGenerator {
|
||||
// TODO: Avoid the copy here?
|
||||
DecimalQuantity copy = quantity.createCopy();
|
||||
micros.rounding.apply(copy);
|
||||
micros.modOuter = data.get(copy.getStandardPlural(rules));
|
||||
micros.modOuter = modifiers.get(copy.getStandardPlural(rules));
|
||||
return micros;
|
||||
}
|
||||
|
||||
///////////////////////////////////////
|
||||
/// BEGIN MEASURE UNIT DATA LOADING ///
|
||||
///////////////////////////////////////
|
||||
|
||||
private static final class MeasureUnitSink extends UResource.Sink {
|
||||
|
||||
Map<StandardPlural, String> output;
|
||||
|
||||
public MeasureUnitSink(Map<StandardPlural, String> output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table pluralsTable = value.getTable();
|
||||
for (int i = 0; pluralsTable.getKeyAndValue(i, key, value); ++i) {
|
||||
if (key.contentEquals("dnam") || key.contentEquals("per")) {
|
||||
continue;
|
||||
}
|
||||
StandardPlural plural = StandardPlural.fromString(key);
|
||||
if (output.containsKey(plural)) {
|
||||
continue;
|
||||
}
|
||||
String formatString = value.getString();
|
||||
output.put(plural, formatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<StandardPlural, String> getMeasureData(ULocale locale, MeasureUnit unit, UnitWidth width) {
|
||||
ICUResourceBundle resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME,
|
||||
locale);
|
||||
StringBuilder key = new StringBuilder();
|
||||
key.append("units");
|
||||
if (width == UnitWidth.NARROW) {
|
||||
key.append("Narrow");
|
||||
} else if (width == UnitWidth.SHORT) {
|
||||
key.append("Short");
|
||||
}
|
||||
key.append("/");
|
||||
key.append(unit.getType());
|
||||
key.append("/");
|
||||
key.append(unit.getSubtype());
|
||||
Map<StandardPlural, String> output = new EnumMap<StandardPlural, String>(StandardPlural.class);
|
||||
MeasureUnitSink sink = new MeasureUnitSink(output);
|
||||
resource.getAllItemsWithFallback(key.toString(), sink);
|
||||
return output;
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
/// END MEASURE UNIT DATA LOADING ///
|
||||
/////////////////////////////////////
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package newapi.impl;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.UResource;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
|
||||
public class MeasureData {
|
||||
|
||||
private static final class ShanesMeasureUnitSink extends UResource.Sink {
|
||||
|
||||
Map<StandardPlural, String> output;
|
||||
|
||||
public ShanesMeasureUnitSink(Map<StandardPlural, String> output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table pluralsTable = value.getTable();
|
||||
for (int i1 = 0; pluralsTable.getKeyAndValue(i1, key, value); ++i1) {
|
||||
if (key.contentEquals("dnam") || key.contentEquals("per")) {
|
||||
continue;
|
||||
}
|
||||
StandardPlural plural = StandardPlural.fromString(key);
|
||||
if (output.containsKey(plural)) {
|
||||
continue;
|
||||
}
|
||||
String formatString = value.getString();
|
||||
output.put(plural, formatString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<StandardPlural, String> getMeasureData(
|
||||
ULocale locale, MeasureUnit unit, UnitWidth width) {
|
||||
ICUResourceBundle resource =
|
||||
(ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
|
||||
StringBuilder key = new StringBuilder();
|
||||
key.append("units");
|
||||
if (width == UnitWidth.NARROW) {
|
||||
key.append("Narrow");
|
||||
} else if (width == UnitWidth.SHORT) {
|
||||
key.append("Short");
|
||||
}
|
||||
key.append("/");
|
||||
key.append(unit.getType());
|
||||
key.append("/");
|
||||
key.append(unit.getSubtype());
|
||||
Map<StandardPlural, String> output = new EnumMap<StandardPlural, String>(StandardPlural.class);
|
||||
ShanesMeasureUnitSink sink = new ShanesMeasureUnitSink(output);
|
||||
resource.getAllItemsWithFallback(key.toString(), sink);
|
||||
return output;
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ public class MicroProps implements Cloneable, MicroPropsGenerator {
|
||||
public int multiplier;
|
||||
public boolean useCurrency;
|
||||
|
||||
// Internal fields:
|
||||
private final boolean immutable;
|
||||
private volatile boolean exhausted;
|
||||
|
||||
|
@ -6,16 +6,16 @@ import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.AffixUtils;
|
||||
import com.ibm.icu.impl.number.AffixUtils.SymbolProvider;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.Modifier;
|
||||
import com.ibm.icu.impl.number.NumberStringBuilder;
|
||||
import com.ibm.icu.impl.number.ParameterizedModifier;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.modifiers.ConstantMultiFieldModifier;
|
||||
import com.ibm.icu.impl.number.modifiers.CurrencySpacingEnabledModifier;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.util.Currency;
|
||||
|
||||
import newapi.NumberFormatter;
|
||||
import newapi.NumberFormatter.SignDisplay;
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
|
||||
@ -53,9 +53,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
// Symbol details
|
||||
DecimalFormatSymbols symbols;
|
||||
UnitWidth unitWidth;
|
||||
String currency1;
|
||||
String currency2;
|
||||
String[] currency3;
|
||||
Currency currency;
|
||||
PluralRules rules;
|
||||
|
||||
// Number details
|
||||
@ -84,7 +82,8 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
|
||||
/**
|
||||
* Sets a reference to the parsed decimal format pattern, usually obtained from
|
||||
* {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of {@link AffixPatternProvider} is accepted.
|
||||
* {@link PatternStringParser#parseToPatternInfo(String)}, but any implementation of {@link AffixPatternProvider} is
|
||||
* accepted.
|
||||
*/
|
||||
public void setPatternInfo(AffixPatternProvider patternInfo) {
|
||||
this.patternInfo = patternInfo;
|
||||
@ -109,8 +108,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
* @param symbols
|
||||
* The desired instance of DecimalFormatSymbols.
|
||||
* @param currency
|
||||
* The currency to be used when substituting currency values into the affixes. Cannot be null, but a
|
||||
* bogus currency like "XXX" can be used.
|
||||
* The currency to be used when substituting currency values into the affixes.
|
||||
* @param unitWidth
|
||||
* The width used to render currencies.
|
||||
* @param rules
|
||||
@ -120,19 +118,9 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
public void setSymbols(DecimalFormatSymbols symbols, Currency currency, UnitWidth unitWidth, PluralRules rules) {
|
||||
assert (rules != null) == needsPlurals();
|
||||
this.symbols = symbols;
|
||||
this.currency = currency;
|
||||
this.unitWidth = unitWidth;
|
||||
this.rules = rules;
|
||||
|
||||
currency1 = currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
|
||||
currency2 = currency.getCurrencyCode();
|
||||
|
||||
if (rules != null) {
|
||||
currency3 = new String[StandardPlural.COUNT];
|
||||
for (StandardPlural plural : StandardPlural.VALUES) {
|
||||
currency3[plural.ordinal()] = currency.getName(symbols.getULocale(), Currency.PLURAL_LONG_NAME,
|
||||
plural.getKeyword(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +156,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
*
|
||||
* @return An immutable that supports both positive and negative numbers.
|
||||
*/
|
||||
public ImmutableMurkyModifier createImmutable() {
|
||||
public ImmutablePatternModifier createImmutable() {
|
||||
return createImmutableAndChain(null);
|
||||
}
|
||||
|
||||
@ -181,32 +169,43 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
* The QuantityChain to which to chain this immutable.
|
||||
* @return An immutable that supports both positive and negative numbers.
|
||||
*/
|
||||
public ImmutableMurkyModifier createImmutableAndChain(MicroPropsGenerator parent) {
|
||||
public ImmutablePatternModifier createImmutableAndChain(MicroPropsGenerator parent) {
|
||||
NumberStringBuilder a = new NumberStringBuilder();
|
||||
NumberStringBuilder b = new NumberStringBuilder();
|
||||
if (needsPlurals()) {
|
||||
// Slower path when we require the plural keyword.
|
||||
Modifier[] mods = new Modifier[ImmutableMurkyModifierWithPlurals.getModsLength()];
|
||||
ParameterizedModifier pm = new ParameterizedModifier();
|
||||
for (StandardPlural plural : StandardPlural.VALUES) {
|
||||
setNumberProperties(false, plural);
|
||||
Modifier positive = createConstantModifier(a, b);
|
||||
pm.setModifier(false, plural, createConstantModifier(a, b));
|
||||
setNumberProperties(true, plural);
|
||||
Modifier negative = createConstantModifier(a, b);
|
||||
mods[ImmutableMurkyModifierWithPlurals.getModIndex(false, plural)] = positive;
|
||||
mods[ImmutableMurkyModifierWithPlurals.getModIndex(true, plural)] = negative;
|
||||
pm.setModifier(true, plural, createConstantModifier(a, b));
|
||||
}
|
||||
return new ImmutableMurkyModifierWithPlurals(mods, rules, parent);
|
||||
pm.freeze();
|
||||
return new ImmutablePatternModifier(pm, rules, parent);
|
||||
} else {
|
||||
// Faster path when plural keyword is not needed.
|
||||
setNumberProperties(false, null);
|
||||
Modifier positive = createConstantModifier(a, b);
|
||||
setNumberProperties(true, null);
|
||||
Modifier negative = createConstantModifier(a, b);
|
||||
return new ImmutableMurkyModifierWithoutPlurals(positive, negative, parent);
|
||||
ParameterizedModifier pm = new ParameterizedModifier(positive, negative);
|
||||
return new ImmutablePatternModifier(pm, null, parent);
|
||||
}
|
||||
}
|
||||
|
||||
private Modifier createConstantModifier(NumberStringBuilder a, NumberStringBuilder b) {
|
||||
/**
|
||||
* Uses the current properties to create a single {@link ConstantMultiFieldModifier} with currency spacing support
|
||||
* if required.
|
||||
*
|
||||
* @param a
|
||||
* A working NumberStringBuilder object; passed from the outside to prevent the need to create many new
|
||||
* instances if this method is called in a loop.
|
||||
* @param b
|
||||
* Another working NumberStringBuilder object.
|
||||
* @return The constant modifier object.
|
||||
*/
|
||||
private ConstantMultiFieldModifier createConstantModifier(NumberStringBuilder a, NumberStringBuilder b) {
|
||||
insertPrefix(a.clear(), 0);
|
||||
insertSuffix(b.clear(), 0);
|
||||
if (patternInfo.hasCurrencySign()) {
|
||||
@ -216,79 +215,38 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
}
|
||||
}
|
||||
|
||||
public static interface ImmutableMurkyModifier extends MicroPropsGenerator {
|
||||
public void applyToMicros(MicroProps micros, DecimalQuantity quantity);
|
||||
}
|
||||
|
||||
public static class ImmutableMurkyModifierWithoutPlurals implements ImmutableMurkyModifier {
|
||||
final Modifier positive;
|
||||
final Modifier negative;
|
||||
final MicroPropsGenerator parent;
|
||||
|
||||
public ImmutableMurkyModifierWithoutPlurals(Modifier positive, Modifier negative, MicroPropsGenerator parent) {
|
||||
this.positive = positive;
|
||||
this.negative = negative;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MicroProps processQuantity(DecimalQuantity quantity) {
|
||||
assert parent != null;
|
||||
MicroProps micros = parent.processQuantity(quantity);
|
||||
applyToMicros(micros, quantity);
|
||||
return micros;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToMicros(MicroProps micros, DecimalQuantity quantity) {
|
||||
if (quantity.isNegative()) {
|
||||
micros.modMiddle = negative;
|
||||
} else {
|
||||
micros.modMiddle = positive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImmutableMurkyModifierWithPlurals implements ImmutableMurkyModifier {
|
||||
final Modifier[] mods;
|
||||
public static class ImmutablePatternModifier implements MicroPropsGenerator {
|
||||
final ParameterizedModifier pm;
|
||||
final PluralRules rules;
|
||||
final MicroPropsGenerator parent;
|
||||
|
||||
public ImmutableMurkyModifierWithPlurals(Modifier[] mods, PluralRules rules, MicroPropsGenerator parent) {
|
||||
assert mods.length == getModsLength();
|
||||
assert rules != null;
|
||||
this.mods = mods;
|
||||
ImmutablePatternModifier(ParameterizedModifier pm, PluralRules rules, MicroPropsGenerator parent) {
|
||||
this.pm = pm;
|
||||
this.rules = rules;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public static int getModsLength() {
|
||||
return 2 * StandardPlural.COUNT;
|
||||
}
|
||||
|
||||
public static int getModIndex(boolean isNegative, StandardPlural plural) {
|
||||
return plural.ordinal() * 2 + (isNegative ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MicroProps processQuantity(DecimalQuantity quantity) {
|
||||
assert parent != null;
|
||||
MicroProps micros = parent.processQuantity(quantity);
|
||||
applyToMicros(micros, quantity);
|
||||
return micros;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyToMicros(MicroProps micros, DecimalQuantity quantity) {
|
||||
// TODO: Fix this. Avoid the copy.
|
||||
DecimalQuantity copy = quantity.createCopy();
|
||||
copy.roundToInfinity();
|
||||
StandardPlural plural = copy.getStandardPlural(rules);
|
||||
Modifier mod = mods[getModIndex(quantity.isNegative(), plural)];
|
||||
micros.modMiddle = mod;
|
||||
if (rules == null) {
|
||||
micros.modMiddle = pm.getModifier(quantity.isNegative());
|
||||
} else {
|
||||
// TODO: Fix this. Avoid the copy.
|
||||
DecimalQuantity copy = quantity.createCopy();
|
||||
copy.roundToInfinity();
|
||||
StandardPlural plural = copy.getStandardPlural(rules);
|
||||
micros.modMiddle = pm.getModifier(quantity.isNegative(), plural);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Used by the unsafe code path. */
|
||||
public MicroPropsGenerator addToChain(MicroPropsGenerator parent) {
|
||||
this.parent = parent;
|
||||
return this;
|
||||
@ -320,8 +278,23 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
|
||||
@Override
|
||||
public int getPrefixLength() {
|
||||
NumberStringBuilder dummy = new NumberStringBuilder();
|
||||
return insertPrefix(dummy, 0);
|
||||
// Enter and exit CharSequence Mode to get the length.
|
||||
enterCharSequenceMode(true);
|
||||
int result = AffixUtils.unescapedCodePointCount(this, this); // prefix length
|
||||
exitCharSequenceMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodePointCount() {
|
||||
// Enter and exit CharSequence Mode to get the length.
|
||||
enterCharSequenceMode(true);
|
||||
int result = AffixUtils.unescapedCodePointCount(this, this); // prefix length
|
||||
exitCharSequenceMode();
|
||||
enterCharSequenceMode(false);
|
||||
result += AffixUtils.unescapedCodePointCount(this, this); // suffix length
|
||||
exitCharSequenceMode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -343,6 +316,9 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string that substitutes a given symbol type in a pattern.
|
||||
*/
|
||||
@Override
|
||||
public CharSequence getSymbol(int type) {
|
||||
switch (type) {
|
||||
@ -355,23 +331,20 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
case AffixUtils.TYPE_PERMILLE:
|
||||
return symbols.getPerMillString();
|
||||
case AffixUtils.TYPE_CURRENCY_SINGLE:
|
||||
// FormatWidth ISO overrides the singular currency symbol
|
||||
// UnitWidth ISO overrides the singular currency symbol.
|
||||
if (unitWidth == UnitWidth.ISO_CODE) {
|
||||
return currency2;
|
||||
return currency.getCurrencyCode();
|
||||
} else {
|
||||
return currency1;
|
||||
return currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
|
||||
}
|
||||
case AffixUtils.TYPE_CURRENCY_DOUBLE:
|
||||
return currency2;
|
||||
return currency.getCurrencyCode();
|
||||
case AffixUtils.TYPE_CURRENCY_TRIPLE:
|
||||
// NOTE: This is the code path only for patterns containing "".
|
||||
// Most plural currencies are formatted in DataUtils.
|
||||
// NOTE: This is the code path only for patterns containing "¤¤¤".
|
||||
// Plural currencies set via the API are formatted in LongNameHandler.
|
||||
// This code path is used by DecimalFormat via CurrencyPluralInfo.
|
||||
assert plural != null;
|
||||
if (currency3 == null) {
|
||||
return currency2;
|
||||
} else {
|
||||
return currency3[plural.ordinal()];
|
||||
}
|
||||
return currency.getName(symbols.getULocale(), Currency.PLURAL_LONG_NAME, plural.getKeyword(), null);
|
||||
case AffixUtils.TYPE_CURRENCY_QUAD:
|
||||
return "\uFFFD";
|
||||
case AffixUtils.TYPE_CURRENCY_QUINT:
|
||||
@ -391,7 +364,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
&& (signDisplay == SignDisplay.ALWAYS || signDisplay == SignDisplay.ACCOUNTING_ALWAYS)
|
||||
&& patternInfo.positiveHasPlusSign() == false;
|
||||
|
||||
// Should we use the negative affix pattern? (If not, we will use the positive one)
|
||||
// Should we use the affix from the negative subpattern? (If not, we will use the positive subpattern.)
|
||||
boolean useNegativeAffixPattern = patternInfo.hasNegativeSubpattern()
|
||||
&& (isNegative || (patternInfo.negativeHasMinusSign() && plusReplacesMinusSign));
|
||||
|
||||
@ -428,13 +401,8 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
if (inCharSequenceMode) {
|
||||
return length;
|
||||
} else {
|
||||
NumberStringBuilder sb = new NumberStringBuilder(20);
|
||||
apply(sb, 0, 0);
|
||||
return sb.length();
|
||||
}
|
||||
assert inCharSequenceMode;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -459,7 +427,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, CharSeq
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
// Should never be called in normal circumstances
|
||||
// Never called by AffixUtils
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package newapi.impl;
|
||||
|
||||
import com.ibm.icu.impl.number.Modifier;
|
||||
import com.ibm.icu.impl.number.NumberStringBuilder;
|
||||
|
||||
public class Padder {
|
||||
@ -71,52 +72,46 @@ public class Padder {
|
||||
}
|
||||
}
|
||||
|
||||
public int applyModsAndMaybePad(MicroProps micros, NumberStringBuilder string, int leftIndex, int rightIndex) {
|
||||
// Apply modInner (scientific notation) before padding
|
||||
int innerLength = micros.modInner.apply(string, leftIndex, rightIndex);
|
||||
public boolean isValid() {
|
||||
return targetWidth > 0;
|
||||
}
|
||||
|
||||
// No padding; apply the mods and leave.
|
||||
if (targetWidth < 0) {
|
||||
return applyMicroMods(micros, string, leftIndex, rightIndex + innerLength);
|
||||
}
|
||||
|
||||
// Estimate the padding width needed.
|
||||
// TODO: Make this more efficient (less copying)
|
||||
// TODO: How to handle when padding is inserted between a currency sign and the number
|
||||
// when currency spacing is in play?
|
||||
NumberStringBuilder backup = new NumberStringBuilder(string);
|
||||
int length = innerLength + applyMicroMods(micros, string, leftIndex, rightIndex + innerLength);
|
||||
int requiredPadding = targetWidth - string.codePointCount();
|
||||
public int padAndApply(Modifier mod1, Modifier mod2, NumberStringBuilder string, int leftIndex, int rightIndex) {
|
||||
int modLength = mod1.getCodePointCount() + mod2.getCodePointCount();
|
||||
int requiredPadding = targetWidth - modLength - string.codePointCount();
|
||||
assert leftIndex == 0 && rightIndex == string.length(); // fix the previous line to remove this assertion
|
||||
|
||||
int length = 0;
|
||||
if (requiredPadding <= 0) {
|
||||
// Padding is not required.
|
||||
length += mod1.apply(string, leftIndex, rightIndex);
|
||||
length += mod2.apply(string, leftIndex, rightIndex + length);
|
||||
return length;
|
||||
}
|
||||
|
||||
length = innerLength;
|
||||
string.copyFrom(backup);
|
||||
if (position == PadPosition.AFTER_PREFIX) {
|
||||
length += addPaddingHelper(paddingString, requiredPadding, string, leftIndex);
|
||||
} else if (position == PadPosition.BEFORE_SUFFIX) {
|
||||
length += addPaddingHelper(paddingString, requiredPadding, string, rightIndex + length);
|
||||
}
|
||||
length += applyMicroMods(micros, string, leftIndex, rightIndex + length);
|
||||
length += mod1.apply(string, leftIndex, rightIndex + length);
|
||||
length += mod2.apply(string, leftIndex, rightIndex + length);
|
||||
if (position == PadPosition.BEFORE_PREFIX) {
|
||||
length = addPaddingHelper(paddingString, requiredPadding, string, leftIndex);
|
||||
length += addPaddingHelper(paddingString, requiredPadding, string, leftIndex);
|
||||
} else if (position == PadPosition.AFTER_SUFFIX) {
|
||||
length = addPaddingHelper(paddingString, requiredPadding, string, rightIndex + length);
|
||||
length += addPaddingHelper(paddingString, requiredPadding, string, rightIndex + length);
|
||||
}
|
||||
|
||||
// The length might not be exactly right due to currency spacing.
|
||||
// Make an adjustment if needed.
|
||||
while (string.codePointCount() < targetWidth) {
|
||||
int insertIndex;
|
||||
int insertIndex = mod1.getPrefixLength() + mod2.getPrefixLength();
|
||||
switch (position) {
|
||||
case AFTER_PREFIX:
|
||||
insertIndex = leftIndex + length;
|
||||
insertIndex += leftIndex;
|
||||
break;
|
||||
case BEFORE_SUFFIX:
|
||||
insertIndex = rightIndex + length;
|
||||
insertIndex += rightIndex;
|
||||
break;
|
||||
default:
|
||||
// Should not happen since currency spacing is always on the inside.
|
||||
@ -128,12 +123,6 @@ public class Padder {
|
||||
return length;
|
||||
}
|
||||
|
||||
private static int applyMicroMods(MicroProps micros, NumberStringBuilder string, int leftIndex, int rightIndex) {
|
||||
int length = micros.modMiddle.apply(string, leftIndex, rightIndex);
|
||||
length += micros.modOuter.apply(string, leftIndex, rightIndex + length);
|
||||
return length;
|
||||
}
|
||||
|
||||
private static int addPaddingHelper(String paddingString, int requiredPadding, NumberStringBuilder string,
|
||||
int index) {
|
||||
for (int i = 0; i < requiredPadding; i++) {
|
||||
|
@ -9,17 +9,17 @@ import java.text.ParsePosition;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.dev.test.TestUtil;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.Parse.ParseMode;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PatternStringUtils;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.DecimalFormat_ICU58;
|
||||
import com.ibm.icu.util.CurrencyAmount;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.LocalizedNumberFormatter;
|
||||
import newapi.NumberPropertyMapper;
|
||||
import newapi.NumberFormatter;
|
||||
import newapi.impl.Padder.PadPosition;
|
||||
|
||||
public class NumberFormatDataDrivenTest {
|
||||
@ -556,7 +556,7 @@ public class NumberFormatDataDrivenTest {
|
||||
: PatternStringParser.IGNORE_ROUNDING_NEVER);
|
||||
propertiesFromTuple(tuple, properties);
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
LocalizedNumberFormatter fmt = NumberPropertyMapper.create(properties, symbols).locale(locale);
|
||||
LocalizedNumberFormatter fmt = NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(locale);
|
||||
Number number = toNumber(tuple.format);
|
||||
String expected = tuple.output;
|
||||
String actual = fmt.format(number).toString();
|
||||
|
@ -72,13 +72,13 @@ public class PluralRulesTest extends TestFmwk {
|
||||
}) {
|
||||
FixedDecimal fd = new FixedDecimal(testDouble[0]);
|
||||
assertEquals(testDouble[0] + "=doubleValue()", testDouble[0], fd.doubleValue());
|
||||
assertEquals(testDouble[0] + " decimalDigits", (int) testDouble[1], fd.decimalDigits);
|
||||
assertEquals(testDouble[0] + " visibleDecimalDigitCount", (int) testDouble[2], fd.visibleDecimalDigitCount);
|
||||
assertEquals(testDouble[0] + " decimalDigits", (int) testDouble[1], fd.getDecimalDigits());
|
||||
assertEquals(testDouble[0] + " visibleDecimalDigitCount", (int) testDouble[2], fd.getVisibleDecimalDigitCount());
|
||||
assertEquals(testDouble[0] + " decimalDigitsWithoutTrailingZeros", (int) testDouble[1],
|
||||
fd.decimalDigitsWithoutTrailingZeros);
|
||||
fd.getDecimalDigitsWithoutTrailingZeros());
|
||||
assertEquals(testDouble[0] + " visibleDecimalDigitCountWithoutTrailingZeros", (int) testDouble[2],
|
||||
fd.visibleDecimalDigitCountWithoutTrailingZeros);
|
||||
assertEquals(testDouble[0] + " integerValue", (long) testDouble[3], fd.integerValue);
|
||||
fd.getVisibleDecimalDigitCountWithoutTrailingZeros());
|
||||
assertEquals(testDouble[0] + " integerValue", (long) testDouble[3], fd.getIntegerValue());
|
||||
}
|
||||
|
||||
for (ULocale locale : new ULocale[] { ULocale.ENGLISH, new ULocale("cy"), new ULocale("ar") }) {
|
||||
@ -862,14 +862,14 @@ public class PluralRulesTest extends TestFmwk {
|
||||
enum StandardPluralCategories {
|
||||
zero, one, two, few, many, other;
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
private static final Set<StandardPluralCategories> ALL = Collections.unmodifiableSet(EnumSet
|
||||
.allOf(StandardPluralCategories.class));
|
||||
|
||||
/**
|
||||
* Return a mutable set
|
||||
*
|
||||
*
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
@ -882,6 +882,7 @@ public class PluralRulesTest extends TestFmwk {
|
||||
}
|
||||
|
||||
static final Comparator<Set<StandardPluralCategories>> SHORTEST_FIRST = new Comparator<Set<StandardPluralCategories>>() {
|
||||
@Override
|
||||
public int compare(Set<StandardPluralCategories> arg0, Set<StandardPluralCategories> arg1) {
|
||||
int diff = arg0.size() - arg1.size();
|
||||
if (diff != 0) {
|
||||
@ -927,6 +928,7 @@ public class PluralRulesTest extends TestFmwk {
|
||||
}
|
||||
|
||||
private static final Comparator<PluralRules> PLURAL_RULE_COMPARATOR = new Comparator<PluralRules>() {
|
||||
@Override
|
||||
public int compare(PluralRules o1, PluralRules o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
@ -1066,12 +1068,14 @@ public class PluralRulesTest extends TestFmwk {
|
||||
}
|
||||
|
||||
public static class FixedDecimalHandler implements SerializableTestUtility.Handler {
|
||||
@Override
|
||||
public Object[] getTestObjects() {
|
||||
FixedDecimal items[] = { new FixedDecimal(3d), new FixedDecimal(3d, 2), new FixedDecimal(3.1d, 1),
|
||||
new FixedDecimal(3.1d, 2), };
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameBehavior(Object a, Object b) {
|
||||
FixedDecimal a1 = (FixedDecimal) a;
|
||||
FixedDecimal b1 = (FixedDecimal) b;
|
||||
|
@ -13,7 +13,7 @@ import com.ibm.icu.impl.number.NumberStringBuilder;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
public class AffixPatternUtilsTest {
|
||||
public class AffixUtilsTest {
|
||||
|
||||
private static final SymbolProvider DEFAULT_SYMBOL_PROVIDER =
|
||||
new SymbolProvider() {
|
||||
@ -127,6 +127,9 @@ public class AffixPatternUtilsTest {
|
||||
|
||||
String actual = unescapeWithDefaults(input);
|
||||
assertEquals("Output on <" + input + ">", output, actual);
|
||||
|
||||
int ulength = AffixUtils.unescapedCodePointCount(input, DEFAULT_SYMBOL_PROVIDER);
|
||||
assertEquals("Unescaped length on <" + input + ">", output.length(), ulength);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +224,8 @@ public class AffixPatternUtilsTest {
|
||||
|
||||
private static String unescapeWithDefaults(String input) {
|
||||
NumberStringBuilder nsb = new NumberStringBuilder();
|
||||
AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER);
|
||||
int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER);
|
||||
assertEquals("Return value of unescape", nsb.length(), length);
|
||||
return nsb.toString();
|
||||
}
|
||||
}
|
@ -10,25 +10,27 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_SimpleStorage;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_64BitBCD;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_ByteArrayBCD;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_SimpleStorage;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.LocalizedNumberFormatter;
|
||||
import newapi.NumberPropertyMapper;
|
||||
import newapi.NumberFormatter;
|
||||
|
||||
/** TODO: This is a temporary name for this class. Suggestions for a better name? */
|
||||
public class DecimalQuantityTest extends TestFmwk {
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testBehavior() throws ParseException {
|
||||
|
||||
@ -38,24 +40,24 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
|
||||
|
||||
DecimalFormatProperties properties = new DecimalFormatProperties();
|
||||
formats.add(NumberPropertyMapper.create(properties, symbols).locale(ULocale.ENGLISH));
|
||||
formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
|
||||
|
||||
properties =
|
||||
new DecimalFormatProperties()
|
||||
.setMinimumSignificantDigits(3)
|
||||
.setMaximumSignificantDigits(3)
|
||||
.setCompactStyle(CompactStyle.LONG);
|
||||
formats.add(NumberPropertyMapper.create(properties, symbols).locale(ULocale.ENGLISH));
|
||||
formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
|
||||
|
||||
properties =
|
||||
new DecimalFormatProperties()
|
||||
.setMinimumExponentDigits(1)
|
||||
.setMaximumIntegerDigits(3)
|
||||
.setMaximumFractionDigits(1);
|
||||
formats.add(NumberPropertyMapper.create(properties, symbols).locale(ULocale.ENGLISH));
|
||||
formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
|
||||
|
||||
properties = new DecimalFormatProperties().setRoundingIncrement(new BigDecimal("0.5"));
|
||||
formats.add(NumberPropertyMapper.create(properties, symbols).locale(ULocale.ENGLISH));
|
||||
formats.add(NumberFormatter.fromDecimalFormat(properties, symbols, null).locale(ULocale.ENGLISH));
|
||||
|
||||
String[] cases = {
|
||||
"1.0",
|
||||
@ -159,20 +161,12 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
}
|
||||
|
||||
private static void testDecimalQuantityExpectedOutput(DecimalQuantity rq, String expected) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
DecimalQuantity q0 = rq.createCopy();
|
||||
// Force an accurate double
|
||||
q0.roundToInfinity();
|
||||
q0.setIntegerLength(1, Integer.MAX_VALUE);
|
||||
q0.setFractionLength(1, Integer.MAX_VALUE);
|
||||
for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) {
|
||||
sb.append(q0.getDigit(m));
|
||||
if (m == 0) sb.append('.');
|
||||
}
|
||||
if (q0.isNegative()) {
|
||||
sb.insert(0, '-');
|
||||
}
|
||||
String actual = sb.toString();
|
||||
String actual = q0.toPlainString();
|
||||
assertEquals("Unexpected output from simple string conversion (" + q0 + ")", expected, actual);
|
||||
}
|
||||
|
||||
@ -294,18 +288,18 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
|
||||
|
||||
fq.setToLong(1234123412341234L);
|
||||
assertFalse("Should not be using byte array", fq.usingBytes());
|
||||
assertBigDecimalEquals("Failed on initialize", "1234123412341234", fq.toBigDecimal());
|
||||
assertFalse("Should not be using byte array", fq.isUsingBytes());
|
||||
assertEquals("Failed on initialize", "1234123412341234E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
// Long -> Bytes
|
||||
fq.appendDigit((byte) 5, 0, true);
|
||||
assertTrue("Should be using byte array", fq.usingBytes());
|
||||
assertBigDecimalEquals("Failed on multiply", "12341234123412345", fq.toBigDecimal());
|
||||
assertTrue("Should be using byte array", fq.isUsingBytes());
|
||||
assertEquals("Failed on multiply", "12341234123412345E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
// Bytes -> Long
|
||||
fq.roundToMagnitude(5, MATH_CONTEXT_HALF_EVEN);
|
||||
assertFalse("Should not be using byte array", fq.usingBytes());
|
||||
assertBigDecimalEquals("Failed on round", "12341234123400000", fq.toBigDecimal());
|
||||
assertFalse("Should not be using byte array", fq.isUsingBytes());
|
||||
assertEquals("Failed on round", "123412341234E5", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
}
|
||||
|
||||
@ -313,45 +307,52 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
public void testAppend() {
|
||||
DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
|
||||
fq.appendDigit((byte) 1, 0, true);
|
||||
assertBigDecimalEquals("Failed on append", "1.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 2, 0, true);
|
||||
assertBigDecimalEquals("Failed on append", "12.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "12E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 3, 1, true);
|
||||
assertBigDecimalEquals("Failed on append", "1203.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1203E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 0, 1, true);
|
||||
assertBigDecimalEquals("Failed on append", "120300.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1203E2", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 4, 0, true);
|
||||
assertBigDecimalEquals("Failed on append", "1203004.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1203004E0", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 0, 0, true);
|
||||
assertBigDecimalEquals("Failed on append", "12030040.", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1203004E1", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 5, 0, false);
|
||||
assertBigDecimalEquals("Failed on append", "12030040.5", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "120300405E-1", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 6, 0, false);
|
||||
assertBigDecimalEquals("Failed on append", "12030040.56", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "1203004056E-2", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
fq.appendDigit((byte) 7, 3, false);
|
||||
assertBigDecimalEquals("Failed on append", "12030040.560007", fq.toBigDecimal());
|
||||
assertEquals("Failed on append", "12030040560007E-6", fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
StringBuilder expected = new StringBuilder("12030040.560007");
|
||||
StringBuilder baseExpected = new StringBuilder("12030040560007");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
fq.appendDigit((byte) 8, 0, false);
|
||||
expected.append("8");
|
||||
assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
|
||||
baseExpected.append('8');
|
||||
StringBuilder expected = new StringBuilder(baseExpected);
|
||||
expected.append("E");
|
||||
expected.append(-7 - i);
|
||||
assertEquals("Failed on append", expected.toString(), fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
}
|
||||
fq.appendDigit((byte) 9, 2, false);
|
||||
expected.append("009");
|
||||
assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
|
||||
baseExpected.append("009");
|
||||
StringBuilder expected = new StringBuilder(baseExpected);
|
||||
expected.append('E');
|
||||
expected.append("-19");
|
||||
assertEquals("Failed on append", expected.toString(), fq.toNumberString());
|
||||
assertNull("Failed health check", fq.checkHealth());
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testConvertToAccurateDouble() {
|
||||
// based on https://github.com/google/double-conversion/issues/28
|
||||
@ -423,12 +424,14 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
|
||||
private static void checkDoubleBehavior(double d, boolean explicitRequired, String alert) {
|
||||
DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD(d);
|
||||
if (explicitRequired)
|
||||
if (explicitRequired) {
|
||||
assertTrue(alert + "Should be using approximate double", !fq.explicitExactDouble);
|
||||
}
|
||||
assertEquals(alert + "Initial construction from hard double", d, fq.toDouble());
|
||||
fq.roundToInfinity();
|
||||
if (explicitRequired)
|
||||
if (explicitRequired) {
|
||||
assertTrue(alert + "Should not be using approximate double", fq.explicitExactDouble);
|
||||
}
|
||||
assertDoubleEquals(alert + "After conversion to exact BCD (double)", d, fq.toDouble());
|
||||
assertBigDecimalEquals(
|
||||
alert + "After conversion to exact BCD (BigDecimal)",
|
||||
@ -469,6 +472,30 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecimalQuantityBehaviorStandalone() {
|
||||
DecimalQuantity_DualStorageBCD fq = new DecimalQuantity_DualStorageBCD();
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 0E0>");
|
||||
fq.setToInt(51423);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E0>");
|
||||
fq.adjustMagnitude(-3);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 51423E-3>");
|
||||
fq.setToLong(999999999999000L);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 999:0:0:-999 long 999999999999E3>");
|
||||
fq.setIntegerLength(2, 5);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:0:-999 long 999999999999E3>");
|
||||
fq.setFractionLength(3, 6);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 999999999999E3>");
|
||||
fq.setToDouble(987.654321);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
|
||||
fq.roundToInfinity();
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987654321E-6>");
|
||||
fq.roundToIncrement(new BigDecimal("0.005"), MATH_CONTEXT_HALF_EVEN);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 987655E-3>");
|
||||
fq.roundToMagnitude(-2, MATH_CONTEXT_HALF_EVEN);
|
||||
assertToStringAndHealth(fq, "<DecimalQuantity 5:2:-3:-6 long 98766E-2>");
|
||||
}
|
||||
|
||||
static void assertDoubleEquals(String message, double d1, double d2) {
|
||||
boolean equal = (Math.abs(d1 - d2) < 1e-6) || (Math.abs((d1 - d2) / d1) < 1e-6);
|
||||
handleAssert(equal, message, d1, d2, null, false);
|
||||
@ -482,4 +509,11 @@ public class DecimalQuantityTest extends TestFmwk {
|
||||
boolean equal = d1.compareTo(d2) == 0;
|
||||
handleAssert(equal, message, d1, d2, null, false);
|
||||
}
|
||||
|
||||
static void assertToStringAndHealth(DecimalQuantity_DualStorageBCD fq, String expected) {
|
||||
String actual = fq.toString();
|
||||
assertEquals("DecimalQuantity toString", expected, actual);
|
||||
String health = fq.checkHealth();
|
||||
assertNull("DecimalQuantity health", health);
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@ public class ModifierTest {
|
||||
public void testConstantAffixModifier() {
|
||||
assertModifierEquals(ConstantAffixModifier.EMPTY, 0, false, "|", "n");
|
||||
|
||||
Modifier mod1 = new ConstantAffixModifier("a", "b", NumberFormat.Field.PERCENT, true);
|
||||
assertModifierEquals(mod1, 1, true, "a|b", "%n%");
|
||||
Modifier mod1 = new ConstantAffixModifier("a📻", "b", NumberFormat.Field.PERCENT, true);
|
||||
assertModifierEquals(mod1, 3, true, "a📻|b", "%%%n%");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -34,10 +34,10 @@ public class ModifierTest {
|
||||
Modifier mod1 = new ConstantMultiFieldModifier(prefix, suffix, true);
|
||||
assertModifierEquals(mod1, 0, true, "|", "n");
|
||||
|
||||
prefix.append("a", NumberFormat.Field.PERCENT);
|
||||
prefix.append("a📻", NumberFormat.Field.PERCENT);
|
||||
suffix.append("b", NumberFormat.Field.CURRENCY);
|
||||
Modifier mod2 = new ConstantMultiFieldModifier(prefix, suffix, true);
|
||||
assertModifierEquals(mod2, 1, true, "a|b", "%n$");
|
||||
assertModifierEquals(mod2, 3, true, "a📻|b", "%%%n$");
|
||||
|
||||
// Make sure the first modifier is still the same (that it stayed constant)
|
||||
assertModifierEquals(mod1, 0, true, "|", "n");
|
||||
@ -45,15 +45,15 @@ public class ModifierTest {
|
||||
|
||||
@Test
|
||||
public void testSimpleModifier() {
|
||||
String[] patterns = { "{0}", "X{0}Y", "XX{0}YYY", "{0}YY", "XXXX{0}" };
|
||||
Object[][] outputs = { { "", 0, 0 }, { "abcde", 0, 0 }, { "abcde", 2, 2 }, { "abcde", 1, 3 } };
|
||||
int[] prefixLens = { 0, 1, 2, 0, 4 };
|
||||
String[] patterns = { "{0}", "X{0}Y", "XX{0}YYY", "{0}YY", "XX📺XX{0}" };
|
||||
Object[][] outputs = { { "", 0, 0 }, { "a📻bcde", 0, 0 }, { "a📻bcde", 4, 4 }, { "a📻bcde", 3, 5 } };
|
||||
int[] prefixLens = { 0, 1, 2, 0, 6 };
|
||||
String[][] expectedCharFields = { { "|", "n" }, { "X|Y", "%n%" }, { "XX|YYY", "%%n%%%" }, { "|YY", "n%%" },
|
||||
{ "XXXX|", "%%%%n" } };
|
||||
String[][] expecteds = { { "", "XY", "XXYYY", "YY", "XXXX" },
|
||||
{ "abcde", "XYabcde", "XXYYYabcde", "YYabcde", "XXXXabcde" },
|
||||
{ "abcde", "abXYcde", "abXXYYYcde", "abYYcde", "abXXXXcde" },
|
||||
{ "abcde", "aXbcYde", "aXXbcYYYde", "abcYYde", "aXXXXbcde" } };
|
||||
{ "XX📺XX|", "%%%%%%n" } };
|
||||
String[][] expecteds = { { "", "XY", "XXYYY", "YY", "XX📺XX" },
|
||||
{ "a📻bcde", "XYa📻bcde", "XXYYYa📻bcde", "YYa📻bcde", "XX📺XXa📻bcde" },
|
||||
{ "a📻bcde", "a📻bXYcde", "a📻bXXYYYcde", "a📻bYYcde", "a📻bXX📺XXcde" },
|
||||
{ "a📻bcde", "a📻XbcYde", "a📻XXbcYYYde", "a📻bcYYde", "a📻XX📺XXbcde" } };
|
||||
for (int i = 0; i < patterns.length; i++) {
|
||||
String pattern = patterns[i];
|
||||
String compiledPattern = SimpleFormatterImpl
|
||||
@ -144,9 +144,14 @@ public class ModifierTest {
|
||||
boolean expectedStrong,
|
||||
String expectedChars,
|
||||
String expectedFields) {
|
||||
mod.apply(sb, 0, sb.length());
|
||||
int oldCount = sb.codePointCount();
|
||||
mod.apply(sb, 0, oldCount);
|
||||
assertEquals("Prefix length on " + sb, expectedPrefixLength, mod.getPrefixLength());
|
||||
assertEquals("Strong on " + sb, expectedStrong, mod.isStrong());
|
||||
if (!(mod instanceof CurrencySpacingEnabledModifier)) {
|
||||
assertEquals("Code point count equals actual code point count",
|
||||
sb.codePointCount() - oldCount, mod.getCodePointCount());
|
||||
}
|
||||
assertEquals("<NumberStringBuilder [" + expectedChars + "] [" + expectedFields + "]>", sb.toDebugString());
|
||||
}
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.dev.test.number;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.NumberStringBuilder;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.NumberFormatter.SignDisplay;
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
import newapi.impl.MutablePatternModifier;
|
||||
|
||||
public class MurkyModifierTest {
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
MutablePatternModifier murky = new MutablePatternModifier(false);
|
||||
murky.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b"));
|
||||
murky.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
murky.setSymbols(
|
||||
DecimalFormatSymbols.getInstance(ULocale.ENGLISH),
|
||||
Currency.getInstance("USD"),
|
||||
UnitWidth.SHORT,
|
||||
null);
|
||||
murky.setNumberProperties(false, null);
|
||||
assertEquals("a", getPrefix(murky));
|
||||
assertEquals("b", getSuffix(murky));
|
||||
murky.setPatternAttributes(SignDisplay.ALWAYS, false);
|
||||
assertEquals("+a", getPrefix(murky));
|
||||
assertEquals("b", getSuffix(murky));
|
||||
murky.setNumberProperties(true, null);
|
||||
assertEquals("-a", getPrefix(murky));
|
||||
assertEquals("b", getSuffix(murky));
|
||||
murky.setPatternAttributes(SignDisplay.NEVER, false);
|
||||
assertEquals("a", getPrefix(murky));
|
||||
assertEquals("b", getSuffix(murky));
|
||||
|
||||
murky.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"));
|
||||
murky.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
murky.setNumberProperties(false, null);
|
||||
assertEquals("a", getPrefix(murky));
|
||||
assertEquals("b", getSuffix(murky));
|
||||
murky.setPatternAttributes(SignDisplay.ALWAYS, false);
|
||||
assertEquals("c+", getPrefix(murky));
|
||||
assertEquals("d", getSuffix(murky));
|
||||
murky.setNumberProperties(true, null);
|
||||
assertEquals("c-", getPrefix(murky));
|
||||
assertEquals("d", getSuffix(murky));
|
||||
murky.setPatternAttributes(SignDisplay.NEVER, false);
|
||||
assertEquals("c-", getPrefix(murky)); // TODO: What should this behavior be?
|
||||
assertEquals("d", getSuffix(murky));
|
||||
}
|
||||
|
||||
private static String getPrefix(MutablePatternModifier murky) {
|
||||
NumberStringBuilder nsb = new NumberStringBuilder();
|
||||
murky.apply(nsb, 0, 0);
|
||||
return nsb.subSequence(0, murky.getPrefixLength()).toString();
|
||||
}
|
||||
|
||||
private static String getSuffix(MutablePatternModifier murky) {
|
||||
NumberStringBuilder nsb = new NumberStringBuilder();
|
||||
murky.apply(nsb, 0, 0);
|
||||
return nsb.subSequence(murky.getPrefixLength(), nsb.length()).toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.dev.test.number;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
import com.ibm.icu.impl.number.NumberStringBuilder;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.NumberFormatter.SignDisplay;
|
||||
import newapi.NumberFormatter.UnitWidth;
|
||||
import newapi.impl.MicroProps;
|
||||
import newapi.impl.MutablePatternModifier;
|
||||
|
||||
public class MutablePatternModifierTest {
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
MutablePatternModifier mod = new MutablePatternModifier(false);
|
||||
mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b"));
|
||||
mod.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
mod.setSymbols(
|
||||
DecimalFormatSymbols.getInstance(ULocale.ENGLISH),
|
||||
Currency.getInstance("USD"),
|
||||
UnitWidth.SHORT,
|
||||
null);
|
||||
|
||||
mod.setNumberProperties(false, null);
|
||||
assertEquals("a", getPrefix(mod));
|
||||
assertEquals("b", getSuffix(mod));
|
||||
mod.setPatternAttributes(SignDisplay.ALWAYS, false);
|
||||
assertEquals("+a", getPrefix(mod));
|
||||
assertEquals("b", getSuffix(mod));
|
||||
mod.setNumberProperties(true, null);
|
||||
assertEquals("-a", getPrefix(mod));
|
||||
assertEquals("b", getSuffix(mod));
|
||||
mod.setPatternAttributes(SignDisplay.NEVER, false);
|
||||
assertEquals("a", getPrefix(mod));
|
||||
assertEquals("b", getSuffix(mod));
|
||||
|
||||
mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"));
|
||||
mod.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
mod.setNumberProperties(false, null);
|
||||
assertEquals("a", getPrefix(mod));
|
||||
assertEquals("b", getSuffix(mod));
|
||||
mod.setPatternAttributes(SignDisplay.ALWAYS, false);
|
||||
assertEquals("c+", getPrefix(mod));
|
||||
assertEquals("d", getSuffix(mod));
|
||||
mod.setNumberProperties(true, null);
|
||||
assertEquals("c-", getPrefix(mod));
|
||||
assertEquals("d", getSuffix(mod));
|
||||
mod.setPatternAttributes(SignDisplay.NEVER, false);
|
||||
assertEquals("c-", getPrefix(mod)); // TODO: What should this behavior be?
|
||||
assertEquals("d", getSuffix(mod));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mutableEqualsImmutable() {
|
||||
MutablePatternModifier mod = new MutablePatternModifier(false);
|
||||
mod.setPatternInfo(PatternStringParser.parseToPatternInfo("a0b;c-0d"));
|
||||
mod.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
mod.setSymbols(DecimalFormatSymbols.getInstance(ULocale.ENGLISH), null, UnitWidth.SHORT, null);
|
||||
DecimalQuantity fq = new DecimalQuantity_DualStorageBCD(1);
|
||||
|
||||
NumberStringBuilder nsb1 = new NumberStringBuilder();
|
||||
MicroProps micros1 = new MicroProps(false);
|
||||
mod.addToChain(micros1);
|
||||
mod.processQuantity(fq);
|
||||
micros1.modMiddle.apply(nsb1, 0, 0);
|
||||
|
||||
NumberStringBuilder nsb2 = new NumberStringBuilder();
|
||||
MicroProps micros2 = new MicroProps(true);
|
||||
mod.createImmutable().applyToMicros(micros2, fq);
|
||||
micros2.modMiddle.apply(nsb2, 0, 0);
|
||||
|
||||
NumberStringBuilder nsb3 = new NumberStringBuilder();
|
||||
MicroProps micros3 = new MicroProps(false);
|
||||
mod.addToChain(micros3);
|
||||
mod.setPatternAttributes(SignDisplay.ALWAYS, false);
|
||||
mod.processQuantity(fq);
|
||||
micros3.modMiddle.apply(nsb3, 0, 0);
|
||||
|
||||
assertTrue(nsb1 + " vs. " + nsb2, nsb1.contentEquals(nsb2));
|
||||
assertFalse(nsb1 + " vs. " + nsb3, nsb1.contentEquals(nsb3));
|
||||
}
|
||||
|
||||
private static String getPrefix(MutablePatternModifier mod) {
|
||||
NumberStringBuilder nsb = new NumberStringBuilder();
|
||||
mod.apply(nsb, 0, 0);
|
||||
return nsb.subSequence(0, mod.getPrefixLength()).toString();
|
||||
}
|
||||
|
||||
private static String getSuffix(MutablePatternModifier mod) {
|
||||
NumberStringBuilder nsb = new NumberStringBuilder();
|
||||
mod.apply(nsb, 0, 0);
|
||||
return nsb.subSequence(mod.getPrefixLength(), nsb.length()).toString();
|
||||
}
|
||||
}
|
@ -338,6 +338,34 @@ public class NumberFormatterTest {
|
||||
ULocale.ENGLISH,
|
||||
-9876543.21,
|
||||
"-9,876,543.21 m");
|
||||
|
||||
// The locale string "सान" appears only in brx.txt:
|
||||
assertFormatSingle(
|
||||
"Interesting Data Fallback 1",
|
||||
"U:duration:day unit-width=FULL_NAME",
|
||||
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.FULL_NAME),
|
||||
ULocale.forLanguageTag("brx"),
|
||||
5.43,
|
||||
"5.43 सान");
|
||||
|
||||
// Requires following the alias from unitsNarrow to unitsShort:
|
||||
assertFormatSingle(
|
||||
"Interesting Data Fallback 2",
|
||||
"U:duration:day unit-width=NARROW",
|
||||
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.NARROW),
|
||||
ULocale.forLanguageTag("brx"),
|
||||
5.43,
|
||||
"5.43 d");
|
||||
|
||||
// en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
|
||||
// requiring fallback to the root.
|
||||
assertFormatSingle(
|
||||
"Interesting Data Fallback 3",
|
||||
"U:area:square-meter unit-width=NARROW",
|
||||
NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW),
|
||||
ULocale.forLanguageTag("en-GB"),
|
||||
5.43,
|
||||
"5.43 m²");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -870,7 +898,7 @@ public class NumberFormatterTest {
|
||||
ULocale.ENGLISH,
|
||||
"GBP 87,650.00",
|
||||
"GBP 8,765.00",
|
||||
"GBP 876.50",
|
||||
"GBP*876.50",
|
||||
"GBP**87.65",
|
||||
"GBP***8.76",
|
||||
"GBP***0.88",
|
||||
|
@ -7,9 +7,9 @@ import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PatternStringUtils;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
|
@ -29,11 +29,10 @@ import java.util.Set;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.dev.test.serializable.SerializableTestUtility;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.Parse.GroupingMode;
|
||||
import com.ibm.icu.impl.number.Parse.ParseMode;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
import com.ibm.icu.text.MeasureFormat.FormatWidth;
|
||||
@ -43,6 +42,8 @@ import com.ibm.icu.util.Currency.CurrencyUsage;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
import newapi.impl.Padder.PadPosition;
|
||||
|
||||
public class PropertiesTest {
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user