174 lines
7.6 KiB
Java
174 lines
7.6 KiB
Java
/* GENERATED SOURCE. DO NOT MODIFY. */
|
|
// © 2017 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
package android.icu.impl.number;
|
|
|
|
import android.icu.impl.FormattedStringBuilder;
|
|
import android.icu.text.DecimalFormatSymbols;
|
|
import android.icu.text.NumberFormat;
|
|
import android.icu.text.UnicodeSet;
|
|
|
|
/** Identical to {@link ConstantMultiFieldModifier}, but supports currency spacing.
|
|
* @hide Only a subset of ICU is exposed in Android*/
|
|
public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier {
|
|
|
|
// These are the default currency spacing UnicodeSets in CLDR.
|
|
// Pre-compute them for performance.
|
|
// The unit test testCurrencySpacingPatternStability() will start failing if these change in CLDR.
|
|
private static final UnicodeSet UNISET_DIGIT = new UnicodeSet("[:digit:]").freeze();
|
|
private static final UnicodeSet UNISET_NOTSZ = new UnicodeSet("[[:^S:]&[:^Z:]]").freeze();
|
|
|
|
// Constants for better readability. Types are for compiler checking.
|
|
static final byte PREFIX = 0;
|
|
static final byte SUFFIX = 1;
|
|
static final short IN_CURRENCY = 0;
|
|
static final short IN_NUMBER = 1;
|
|
|
|
private final UnicodeSet afterPrefixUnicodeSet;
|
|
private final String afterPrefixInsert;
|
|
private final UnicodeSet beforeSuffixUnicodeSet;
|
|
private final String beforeSuffixInsert;
|
|
|
|
/** Safe code path */
|
|
public CurrencySpacingEnabledModifier(
|
|
FormattedStringBuilder prefix,
|
|
FormattedStringBuilder suffix,
|
|
boolean overwrite,
|
|
boolean strong,
|
|
DecimalFormatSymbols symbols) {
|
|
super(prefix, suffix, overwrite, strong);
|
|
|
|
// Check for currency spacing. Do not build the UnicodeSets unless there is
|
|
// a currency code point at a boundary.
|
|
if (prefix.length() > 0 && prefix.fieldAt(prefix.length() - 1) == NumberFormat.Field.CURRENCY) {
|
|
int prefixCp = prefix.getLastCodePoint();
|
|
UnicodeSet prefixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, PREFIX);
|
|
if (prefixUnicodeSet.contains(prefixCp)) {
|
|
afterPrefixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, PREFIX);
|
|
afterPrefixUnicodeSet.freeze(); // no-op if set is already frozen
|
|
afterPrefixInsert = getInsertString(symbols, PREFIX);
|
|
} else {
|
|
afterPrefixUnicodeSet = null;
|
|
afterPrefixInsert = null;
|
|
}
|
|
} else {
|
|
afterPrefixUnicodeSet = null;
|
|
afterPrefixInsert = null;
|
|
}
|
|
if (suffix.length() > 0 && suffix.fieldAt(0) == NumberFormat.Field.CURRENCY) {
|
|
int suffixCp = suffix.getFirstCodePoint();
|
|
UnicodeSet suffixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, SUFFIX);
|
|
if (suffixUnicodeSet.contains(suffixCp)) {
|
|
beforeSuffixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, SUFFIX);
|
|
beforeSuffixUnicodeSet.freeze(); // no-op if set is already frozen
|
|
beforeSuffixInsert = getInsertString(symbols, SUFFIX);
|
|
} else {
|
|
beforeSuffixUnicodeSet = null;
|
|
beforeSuffixInsert = null;
|
|
}
|
|
} else {
|
|
beforeSuffixUnicodeSet = null;
|
|
beforeSuffixInsert = null;
|
|
}
|
|
}
|
|
|
|
/** Safe code path */
|
|
@Override
|
|
public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) {
|
|
// Currency spacing logic
|
|
int length = 0;
|
|
if (rightIndex - leftIndex > 0
|
|
&& afterPrefixUnicodeSet != null
|
|
&& afterPrefixUnicodeSet.contains(output.codePointAt(leftIndex))) {
|
|
// TODO: Should we use the CURRENCY field here?
|
|
length += output.insert(leftIndex, afterPrefixInsert, null);
|
|
}
|
|
if (rightIndex - leftIndex > 0
|
|
&& beforeSuffixUnicodeSet != null
|
|
&& beforeSuffixUnicodeSet.contains(output.codePointBefore(rightIndex))) {
|
|
// TODO: Should we use the CURRENCY field here?
|
|
length += output.insert(rightIndex + length, beforeSuffixInsert, null);
|
|
}
|
|
|
|
// Call super for the remaining logic
|
|
length += super.apply(output, leftIndex, rightIndex + length);
|
|
return length;
|
|
}
|
|
|
|
/** Unsafe code path */
|
|
public static int applyCurrencySpacing(
|
|
FormattedStringBuilder output,
|
|
int prefixStart,
|
|
int prefixLen,
|
|
int suffixStart,
|
|
int suffixLen,
|
|
DecimalFormatSymbols symbols) {
|
|
int length = 0;
|
|
boolean hasPrefix = (prefixLen > 0);
|
|
boolean hasSuffix = (suffixLen > 0);
|
|
boolean hasNumber = (suffixStart - prefixStart - prefixLen > 0); // could be empty string
|
|
if (hasPrefix && hasNumber) {
|
|
length += applyCurrencySpacingAffix(output, prefixStart + prefixLen, PREFIX, symbols);
|
|
}
|
|
if (hasSuffix && hasNumber) {
|
|
length += applyCurrencySpacingAffix(output, suffixStart + length, SUFFIX, symbols);
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/** Unsafe code path */
|
|
private static int applyCurrencySpacingAffix(
|
|
FormattedStringBuilder output,
|
|
int index,
|
|
byte affix,
|
|
DecimalFormatSymbols symbols) {
|
|
// NOTE: For prefix, output.fieldAt(index-1) gets the last field type in the prefix.
|
|
// This works even if the last code point in the prefix is 2 code units because the
|
|
// field value gets populated to both indices in the field array.
|
|
Object affixField = (affix == PREFIX) ? output.fieldAt(index - 1)
|
|
: output.fieldAt(index);
|
|
if (affixField != NumberFormat.Field.CURRENCY) {
|
|
return 0;
|
|
}
|
|
int affixCp = (affix == PREFIX) ? output.codePointBefore(index) : output.codePointAt(index);
|
|
UnicodeSet affixUniset = getUnicodeSet(symbols, IN_CURRENCY, affix);
|
|
if (!affixUniset.contains(affixCp)) {
|
|
return 0;
|
|
}
|
|
int numberCp = (affix == PREFIX) ? output.codePointAt(index) : output.codePointBefore(index);
|
|
UnicodeSet numberUniset = getUnicodeSet(symbols, IN_NUMBER, affix);
|
|
if (!numberUniset.contains(numberCp)) {
|
|
return 0;
|
|
}
|
|
String spacingString = getInsertString(symbols, affix);
|
|
|
|
// NOTE: This next line *inserts* the spacing string, triggering an arraycopy.
|
|
// It would be more efficient if this could be done before affixes were attached,
|
|
// so that it could be prepended/appended instead of inserted.
|
|
// However, the build code path is more efficient, and this is the most natural
|
|
// place to put currency spacing in the non-build code path.
|
|
// TODO: Should we use the CURRENCY field here?
|
|
return output.insert(index, spacingString, null);
|
|
}
|
|
|
|
private static UnicodeSet getUnicodeSet(DecimalFormatSymbols symbols, short position, byte affix) {
|
|
String pattern = symbols
|
|
.getPatternForCurrencySpacing(
|
|
position == IN_CURRENCY ? DecimalFormatSymbols.CURRENCY_SPC_CURRENCY_MATCH
|
|
: DecimalFormatSymbols.CURRENCY_SPC_SURROUNDING_MATCH,
|
|
affix == SUFFIX);
|
|
if (pattern.equals("[:digit:]")) {
|
|
return UNISET_DIGIT;
|
|
} else if (pattern.equals("[[:^S:]&[:^Z:]]")) {
|
|
return UNISET_NOTSZ;
|
|
} else {
|
|
return new UnicodeSet(pattern);
|
|
}
|
|
}
|
|
|
|
private static String getInsertString(DecimalFormatSymbols symbols, byte affix) {
|
|
return symbols.getPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT,
|
|
affix == SUFFIX);
|
|
}
|
|
}
|