• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2019 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 package ohos.global.icu.impl;
5 
6 import java.text.AttributedCharacterIterator;
7 import java.text.AttributedString;
8 import java.text.FieldPosition;
9 import java.text.Format.Field;
10 
11 import ohos.global.icu.text.ConstrainedFieldPosition;
12 import ohos.global.icu.text.ListFormatter;
13 import ohos.global.icu.text.NumberFormat;
14 import ohos.global.icu.text.UFormat;
15 import ohos.global.icu.text.UnicodeSet;
16 
17 /**
18  * Implementation of FormattedValue based on FormattedStringBuilder.
19  *
20  * The implementation currently revolves around numbers and number fields.
21  * However, it can be generalized in the future when there is a need.
22  *
23  * In C++, this implements FormattedValue. In Java, it is a stateless
24  * collection of static functions to avoid having to use nested objects.
25  *
26  * @author sffc (Shane Carr)
27  * @hide exposed on OHOS
28  */
29 public class FormattedValueStringBuilderImpl {
30 
31     /**
32      * Placeholder field used for calculating spans.
33      * Does not currently support nested fields beyond one level.
34      * @hide exposed on OHOS
35      */
36     public static class SpanFieldPlaceholder {
37         public UFormat.SpanField spanField;
38         public Field normalField;
39         public Object value;
40     }
41 
42     /**
43      * Finds the index at which a span field begins.
44      *
45      * @param value The value of the span field to search for.
46      * @return The index, or -1 if not found.
47      */
findSpan(FormattedStringBuilder self, Object value)48     public static int findSpan(FormattedStringBuilder self, Object value) {
49         for (int i = self.zero; i < self.zero + self.length; i++) {
50             if (!(self.fields[i] instanceof SpanFieldPlaceholder)) {
51                 continue;
52             }
53             if (((SpanFieldPlaceholder) self.fields[i]).value.equals(value)) {
54                 return i - self.zero;
55             }
56         }
57         return -1;
58     }
59 
nextFieldPosition(FormattedStringBuilder self, FieldPosition fp)60     public static boolean nextFieldPosition(FormattedStringBuilder self, FieldPosition fp) {
61         java.text.Format.Field rawField = fp.getFieldAttribute();
62 
63         if (rawField == null) {
64             // Backwards compatibility: read from fp.getField()
65             if (fp.getField() == NumberFormat.INTEGER_FIELD) {
66                 rawField = NumberFormat.Field.INTEGER;
67             } else if (fp.getField() == NumberFormat.FRACTION_FIELD) {
68                 rawField = NumberFormat.Field.FRACTION;
69             } else {
70                 // No field is set
71                 return false;
72             }
73         }
74 
75         if (!(rawField instanceof NumberFormat.Field)) {
76             throw new IllegalArgumentException(
77                     "You must pass an instance of ohos.global.icu.text.NumberFormat.Field as your FieldPosition attribute.  You passed: "
78                             + rawField.getClass().toString());
79         }
80 
81         ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition();
82         cfpos.constrainField(rawField);
83         cfpos.setState(rawField, null, fp.getBeginIndex(), fp.getEndIndex());
84         if (nextPosition(self, cfpos, null)) {
85             fp.setBeginIndex(cfpos.getStart());
86             fp.setEndIndex(cfpos.getLimit());
87             return true;
88         }
89 
90         // Special case: fraction should start after integer if fraction is not present
91         if (rawField == NumberFormat.Field.FRACTION && fp.getEndIndex() == 0) {
92             boolean inside = false;
93             int i = self.zero;
94             for (; i < self.zero + self.length; i++) {
95                 if (isIntOrGroup(self.fields[i]) || self.fields[i] == NumberFormat.Field.DECIMAL_SEPARATOR) {
96                     inside = true;
97                 } else if (inside) {
98                     break;
99                 }
100             }
101             fp.setBeginIndex(i - self.zero);
102             fp.setEndIndex(i - self.zero);
103         }
104 
105         return false;
106     }
107 
toCharacterIterator(FormattedStringBuilder self, Field numericField)108     public static AttributedCharacterIterator toCharacterIterator(FormattedStringBuilder self, Field numericField) {
109         ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition();
110         AttributedString as = new AttributedString(self.toString());
111         while (nextPosition(self, cfpos, numericField)) {
112             // Backwards compatibility: field value = field
113             Object value = cfpos.getFieldValue();
114             if (value == null) {
115                 value = cfpos.getField();
116             }
117             as.addAttribute(cfpos.getField(), value, cfpos.getStart(), cfpos.getLimit());
118         }
119         return as.getIterator();
120     }
121 
122     static class NullField extends Field {
123         private static final long serialVersionUID = 1L;
124         static final NullField END = new NullField("end");
NullField(String name)125         private NullField(String name) {
126             super(name);
127         }
128     }
129 
130     /**
131      * Implementation of nextPosition consistent with the contract of FormattedValue.
132      *
133      * @param cfpos
134      *            The argument passed to the public API.
135      * @param numericField
136      *            Optional. If non-null, apply this field to the entire numeric portion of the string.
137      * @return See FormattedValue#nextPosition.
138      */
nextPosition(FormattedStringBuilder self, ConstrainedFieldPosition cfpos, Field numericField)139     public static boolean nextPosition(FormattedStringBuilder self, ConstrainedFieldPosition cfpos, Field numericField) {
140         int fieldStart = -1;
141         Object currField = null;
142         for (int i = self.zero + cfpos.getLimit(); i <= self.zero + self.length; i++) {
143             Object _field = (i < self.zero + self.length) ? self.fields[i] : NullField.END;
144             // Case 1: currently scanning a field.
145             if (currField != null) {
146                 if (currField != _field) {
147                     int end = i - self.zero;
148                     // Handle span fields; don't trim them
149                     if (currField instanceof SpanFieldPlaceholder) {
150                         assert handleSpan(currField, cfpos, fieldStart, end);
151                         return true;
152                     }
153                     // Grouping separators can be whitespace; don't throw them out!
154                     if (isTrimmable(currField)) {
155                         end = trimBack(self, end);
156                     }
157                     if (end <= fieldStart) {
158                         // Entire field position is ignorable; skip.
159                         fieldStart = -1;
160                         currField = null;
161                         i--;  // look at this index again
162                         continue;
163                     }
164                     int start = fieldStart;
165                     if (isTrimmable(currField)) {
166                         start = trimFront(self, start);
167                     }
168                     cfpos.setState((Field) currField, null, start, end);
169                     return true;
170                 }
171                 continue;
172             }
173             // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
174             if (cfpos.matchesField(NumberFormat.Field.INTEGER, null)
175                     && i > self.zero
176                     // don't return the same field twice in a row:
177                     && i - self.zero > cfpos.getLimit()
178                     && isIntOrGroup(self.fields[i - 1])
179                     && !isIntOrGroup(_field)) {
180                 int j = i - 1;
181                 for (; j >= self.zero && isIntOrGroup(self.fields[j]); j--) {}
182                 cfpos.setState(NumberFormat.Field.INTEGER, null, j - self.zero + 1, i - self.zero);
183                 return true;
184             }
185             // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
186             if (numericField != null
187                     && cfpos.matchesField(numericField, null)
188                     && i > self.zero
189                     // don't return the same field twice in a row:
190                     && (i - self.zero > cfpos.getLimit() || cfpos.getField() != numericField)
191                     && isNumericField(self.fields[i - 1])
192                     && !isNumericField(_field)) {
193                 int j = i - 1;
194                 for (; j >= self.zero && isNumericField(self.fields[j]); j--) {}
195                 cfpos.setState(numericField, null, j - self.zero + 1, i - self.zero);
196                 return true;
197             }
198             // Special case: emit normalField if we are pointing at the end of spanField.
199             if (i > self.zero
200                     && self.fields[i-1] instanceof SpanFieldPlaceholder) {
201                 int j = i - 1;
202                 for (; j >= self.zero && self.fields[j] == self.fields[i-1]; j--) {}
203                 if (handleSpan(self.fields[i-1], cfpos, j - self.zero + 1, i - self.zero)) {
204                     return true;
205                 }
206             }
207             // Special case: skip over INTEGER; will be coalesced later.
208             if (_field == NumberFormat.Field.INTEGER) {
209                 _field = null;
210             }
211             // Case 2: no field starting at this position.
212             if (_field == null || _field == NullField.END) {
213                 continue;
214             }
215             // Case 3: check for field starting at this position
216             // Case 3a: SpanField placeholder
217             if (_field instanceof SpanFieldPlaceholder) {
218                 SpanFieldPlaceholder ph = (SpanFieldPlaceholder) _field;
219                 if (cfpos.matchesField(ph.normalField, null) || cfpos.matchesField(ph.spanField, ph.value)) {
220                     fieldStart = i - self.zero;
221                     currField = _field;
222                 }
223             }
224             // Case 3b: No SpanField
225             else if (cfpos.matchesField((Field) _field, null)) {
226                 fieldStart = i - self.zero;
227                 currField = _field;
228             }
229         }
230 
231         assert currField == null;
232         return false;
233     }
234 
isIntOrGroup(Object field)235     private static boolean isIntOrGroup(Object field) {
236         return field == NumberFormat.Field.INTEGER || field == NumberFormat.Field.GROUPING_SEPARATOR;
237     }
238 
isNumericField(Object field)239     private static boolean isNumericField(Object field) {
240         return field == null || NumberFormat.Field.class.isAssignableFrom(field.getClass());
241     }
242 
isTrimmable(Object field)243     private static boolean isTrimmable(Object field) {
244         return field != NumberFormat.Field.GROUPING_SEPARATOR
245                 && !(field instanceof ListFormatter.Field);
246     }
247 
trimBack(FormattedStringBuilder self, int limit)248     private static int trimBack(FormattedStringBuilder self, int limit) {
249         return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES)
250                 .spanBack(self, limit, UnicodeSet.SpanCondition.CONTAINED);
251     }
252 
trimFront(FormattedStringBuilder self, int start)253     private static int trimFront(FormattedStringBuilder self, int start) {
254         return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES)
255                 .span(self, start, UnicodeSet.SpanCondition.CONTAINED);
256     }
257 
handleSpan(Object field, ConstrainedFieldPosition cfpos, int start, int limit)258     private static boolean handleSpan(Object field, ConstrainedFieldPosition cfpos, int start, int limit) {
259         SpanFieldPlaceholder ph = (SpanFieldPlaceholder) field;
260         if (cfpos.matchesField(ph.spanField, ph.value)
261                 && cfpos.getLimit() < limit) {
262             cfpos.setState(ph.spanField, ph.value, start, limit);
263             return true;
264         }
265         if (cfpos.matchesField(ph.normalField, null)
266                 && (cfpos.getLimit() < limit || cfpos.getField() != ph.normalField)) {
267             cfpos.setState(ph.normalField, null, start, limit);
268             return true;
269         }
270         return false;
271     }
272 }
273