• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2017 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 package ohos.global.icu.impl.number;
5 
6 import ohos.global.icu.impl.FormattedStringBuilder;
7 import ohos.global.icu.text.DecimalFormatSymbols;
8 import ohos.global.icu.text.NumberFormat;
9 import ohos.global.icu.text.UnicodeSet;
10 
11 /** Identical to {@link ConstantMultiFieldModifier}, but supports currency spacing.
12  * @hide exposed on OHOS*/
13 public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier {
14 
15     // These are the default currency spacing UnicodeSets in CLDR.
16     // Pre-compute them for performance.
17     // The unit test testCurrencySpacingPatternStability() will start failing if these change in CLDR.
18     private static final UnicodeSet UNISET_DIGIT = new UnicodeSet("[:digit:]").freeze();
19     private static final UnicodeSet UNISET_NOTS = new UnicodeSet("[:^S:]").freeze();
20 
21     // Constants for better readability. Types are for compiler checking.
22     static final byte PREFIX = 0;
23     static final byte SUFFIX = 1;
24     static final short IN_CURRENCY = 0;
25     static final short IN_NUMBER = 1;
26 
27     private final UnicodeSet afterPrefixUnicodeSet;
28     private final String afterPrefixInsert;
29     private final UnicodeSet beforeSuffixUnicodeSet;
30     private final String beforeSuffixInsert;
31 
32     /** Safe code path */
CurrencySpacingEnabledModifier( FormattedStringBuilder prefix, FormattedStringBuilder suffix, boolean overwrite, boolean strong, DecimalFormatSymbols symbols)33     public CurrencySpacingEnabledModifier(
34             FormattedStringBuilder prefix,
35             FormattedStringBuilder suffix,
36             boolean overwrite,
37             boolean strong,
38             DecimalFormatSymbols symbols) {
39         super(prefix, suffix, overwrite, strong);
40 
41         // Check for currency spacing. Do not build the UnicodeSets unless there is
42         // a currency code point at a boundary.
43         if (prefix.length() > 0 && prefix.fieldAt(prefix.length() - 1) == NumberFormat.Field.CURRENCY) {
44             int prefixCp = prefix.getLastCodePoint();
45             UnicodeSet prefixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, PREFIX);
46             if (prefixUnicodeSet.contains(prefixCp)) {
47                 afterPrefixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, PREFIX);
48                 afterPrefixUnicodeSet.freeze(); // no-op if set is already frozen
49                 afterPrefixInsert = getInsertString(symbols, PREFIX);
50             } else {
51                 afterPrefixUnicodeSet = null;
52                 afterPrefixInsert = null;
53             }
54         } else {
55             afterPrefixUnicodeSet = null;
56             afterPrefixInsert = null;
57         }
58         if (suffix.length() > 0 && suffix.fieldAt(0) == NumberFormat.Field.CURRENCY) {
59             int suffixCp = suffix.getFirstCodePoint();
60             UnicodeSet suffixUnicodeSet = getUnicodeSet(symbols, IN_CURRENCY, SUFFIX);
61             if (suffixUnicodeSet.contains(suffixCp)) {
62                 beforeSuffixUnicodeSet = getUnicodeSet(symbols, IN_NUMBER, SUFFIX);
63                 beforeSuffixUnicodeSet.freeze(); // no-op if set is already frozen
64                 beforeSuffixInsert = getInsertString(symbols, SUFFIX);
65             } else {
66                 beforeSuffixUnicodeSet = null;
67                 beforeSuffixInsert = null;
68             }
69         } else {
70             beforeSuffixUnicodeSet = null;
71             beforeSuffixInsert = null;
72         }
73     }
74 
75     /** Safe code path */
76     @Override
apply(FormattedStringBuilder output, int leftIndex, int rightIndex)77     public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) {
78         // Currency spacing logic
79         int length = 0;
80         if (rightIndex - leftIndex > 0
81                 && afterPrefixUnicodeSet != null
82                 && afterPrefixUnicodeSet.contains(output.codePointAt(leftIndex))) {
83             // TODO: Should we use the CURRENCY field here?
84             length += output.insert(leftIndex, afterPrefixInsert, null);
85         }
86         if (rightIndex - leftIndex > 0
87                 && beforeSuffixUnicodeSet != null
88                 && beforeSuffixUnicodeSet.contains(output.codePointBefore(rightIndex))) {
89             // TODO: Should we use the CURRENCY field here?
90             length += output.insert(rightIndex + length, beforeSuffixInsert, null);
91         }
92 
93         // Call super for the remaining logic
94         length += super.apply(output, leftIndex, rightIndex + length);
95         return length;
96     }
97 
98     /** Unsafe code path */
applyCurrencySpacing( FormattedStringBuilder output, int prefixStart, int prefixLen, int suffixStart, int suffixLen, DecimalFormatSymbols symbols)99     public static int applyCurrencySpacing(
100             FormattedStringBuilder output,
101             int prefixStart,
102             int prefixLen,
103             int suffixStart,
104             int suffixLen,
105             DecimalFormatSymbols symbols) {
106         int length = 0;
107         boolean hasPrefix = (prefixLen > 0);
108         boolean hasSuffix = (suffixLen > 0);
109         boolean hasNumber = (suffixStart - prefixStart - prefixLen > 0); // could be empty string
110         if (hasPrefix && hasNumber) {
111             length += applyCurrencySpacingAffix(output, prefixStart + prefixLen, PREFIX, symbols);
112         }
113         if (hasSuffix && hasNumber) {
114             length += applyCurrencySpacingAffix(output, suffixStart + length, SUFFIX, symbols);
115         }
116         return length;
117     }
118 
119     /** Unsafe code path */
applyCurrencySpacingAffix( FormattedStringBuilder output, int index, byte affix, DecimalFormatSymbols symbols)120     private static int applyCurrencySpacingAffix(
121             FormattedStringBuilder output,
122             int index,
123             byte affix,
124             DecimalFormatSymbols symbols) {
125         // NOTE: For prefix, output.fieldAt(index-1) gets the last field type in the prefix.
126         // This works even if the last code point in the prefix is 2 code units because the
127         // field value gets populated to both indices in the field array.
128         Object affixField = (affix == PREFIX) ? output.fieldAt(index - 1)
129                 : output.fieldAt(index);
130         if (affixField != NumberFormat.Field.CURRENCY) {
131             return 0;
132         }
133         int affixCp = (affix == PREFIX) ? output.codePointBefore(index) : output.codePointAt(index);
134         UnicodeSet affixUniset = getUnicodeSet(symbols, IN_CURRENCY, affix);
135         if (!affixUniset.contains(affixCp)) {
136             return 0;
137         }
138         int numberCp = (affix == PREFIX) ? output.codePointAt(index) : output.codePointBefore(index);
139         UnicodeSet numberUniset = getUnicodeSet(symbols, IN_NUMBER, affix);
140         if (!numberUniset.contains(numberCp)) {
141             return 0;
142         }
143         String spacingString = getInsertString(symbols, affix);
144 
145         // NOTE: This next line *inserts* the spacing string, triggering an arraycopy.
146         // It would be more efficient if this could be done before affixes were attached,
147         // so that it could be prepended/appended instead of inserted.
148         // However, the build code path is more efficient, and this is the most natural
149         // place to put currency spacing in the non-build code path.
150         // TODO: Should we use the CURRENCY field here?
151         return output.insert(index, spacingString, null);
152     }
153 
getUnicodeSet(DecimalFormatSymbols symbols, short position, byte affix)154     private static UnicodeSet getUnicodeSet(DecimalFormatSymbols symbols, short position, byte affix) {
155         String pattern = symbols
156                 .getPatternForCurrencySpacing(
157                         position == IN_CURRENCY ? DecimalFormatSymbols.CURRENCY_SPC_CURRENCY_MATCH
158                                 : DecimalFormatSymbols.CURRENCY_SPC_SURROUNDING_MATCH,
159                         affix == SUFFIX);
160         if (pattern.equals("[:digit:]")) {
161             return UNISET_DIGIT;
162         } else if (pattern.equals("[:^S:]")) {
163             return UNISET_NOTS;
164         } else {
165             return new UnicodeSet(pattern);
166         }
167     }
168 
getInsertString(DecimalFormatSymbols symbols, byte affix)169     private static String getInsertString(DecimalFormatSymbols symbols, byte affix) {
170         return symbols.getPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT,
171                 affix == SUFFIX);
172     }
173 }
174