1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 // This file contains one implementation of FormattedValue.
9 // Other independent implementations should go into their own cpp file for
10 // better dependency modularization.
12 #include "unicode/ustring.h"
13 #include "formattedval_impl.h"
14 #include "number_types.h"
15 #include "formatted_string_builder.h"
16 #include "number_utils.h"
17 #include "static_unicode_sets.h"
22 typedef FormattedStringBuilder::Field Field
;
25 FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField
)
26 : fNumericField(numericField
) {
29 FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() {
33 UnicodeString
FormattedValueStringBuilderImpl::toString(UErrorCode
&) const {
34 return fString
.toUnicodeString();
37 UnicodeString
FormattedValueStringBuilderImpl::toTempString(UErrorCode
&) const {
38 return fString
.toTempUnicodeString();
41 Appendable
& FormattedValueStringBuilderImpl::appendTo(Appendable
& appendable
, UErrorCode
&) const {
42 appendable
.appendString(fString
.chars(), fString
.length());
46 UBool
FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition
& cfpos
, UErrorCode
& status
) const {
47 // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
48 return nextPositionImpl(cfpos
, fNumericField
, status
) ? TRUE
: FALSE
;
51 UBool
FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition
& fp
, UErrorCode
& status
) const {
52 int32_t rawField
= fp
.getField();
54 if (rawField
== FieldPosition::DONT_CARE
) {
58 if (rawField
< 0 || rawField
>= UNUM_FIELD_COUNT
) {
59 status
= U_ILLEGAL_ARGUMENT_ERROR
;
63 ConstrainedFieldPosition cfpos
;
64 cfpos
.constrainField(UFIELD_CATEGORY_NUMBER
, rawField
);
65 cfpos
.setState(UFIELD_CATEGORY_NUMBER
, rawField
, fp
.getBeginIndex(), fp
.getEndIndex());
66 if (nextPositionImpl(cfpos
, 0, status
)) {
67 fp
.setBeginIndex(cfpos
.getStart());
68 fp
.setEndIndex(cfpos
.getLimit());
72 // Special case: fraction should start after integer if fraction is not present
73 if (rawField
== UNUM_FRACTION_FIELD
&& fp
.getEndIndex() == 0) {
75 int32_t i
= fString
.fZero
;
76 for (; i
< fString
.fZero
+ fString
.fLength
; i
++) {
77 if (isIntOrGroup(fString
.getFieldPtr()[i
]) || fString
.getFieldPtr()[i
] == UNUM_DECIMAL_SEPARATOR_FIELD
) {
83 fp
.setBeginIndex(i
- fString
.fZero
);
84 fp
.setEndIndex(i
- fString
.fZero
);
90 void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler
& fpih
,
91 UErrorCode
& status
) const {
92 ConstrainedFieldPosition cfpos
;
93 while (nextPositionImpl(cfpos
, 0, status
)) {
94 fpih
.addAttribute(cfpos
.getField(), cfpos
.getStart(), cfpos
.getLimit());
98 // Signal the end of the string using a field that doesn't exist and that is
99 // different from UNUM_FIELD_COUNT, which is used for "null number field".
100 static constexpr Field kEndField
= 0xff;
102 bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition
& cfpos
, Field numericField
, UErrorCode
& /*status*/) const {
103 auto numericCAF
= StringBuilderFieldUtils::expand(numericField
);
104 int32_t fieldStart
= -1;
105 Field currField
= UNUM_FIELD_COUNT
;
106 for (int32_t i
= fString
.fZero
+ cfpos
.getLimit(); i
<= fString
.fZero
+ fString
.fLength
; i
++) {
107 Field _field
= (i
< fString
.fZero
+ fString
.fLength
) ? fString
.getFieldPtr()[i
] : kEndField
;
108 // Case 1: currently scanning a field.
109 if (currField
!= UNUM_FIELD_COUNT
) {
110 if (currField
!= _field
) {
111 int32_t end
= i
- fString
.fZero
;
112 // Grouping separators can be whitespace; don't throw them out!
113 if (currField
!= UNUM_GROUPING_SEPARATOR_FIELD
) {
114 end
= trimBack(i
- fString
.fZero
);
116 if (end
<= fieldStart
) {
117 // Entire field position is ignorable; skip.
119 currField
= UNUM_FIELD_COUNT
;
120 i
--; // look at this index again
123 int32_t start
= fieldStart
;
124 if (currField
!= UNUM_GROUPING_SEPARATOR_FIELD
) {
125 start
= trimFront(start
);
127 auto caf
= StringBuilderFieldUtils::expand(currField
);
128 cfpos
.setState(caf
.category
, caf
.field
, start
, end
);
133 // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
134 if (cfpos
.matchesField(UFIELD_CATEGORY_NUMBER
, UNUM_INTEGER_FIELD
)
136 // don't return the same field twice in a row:
137 && i
- fString
.fZero
> cfpos
.getLimit()
138 && isIntOrGroup(fString
.getFieldPtr()[i
- 1])
139 && !isIntOrGroup(_field
)) {
141 for (; j
>= fString
.fZero
&& isIntOrGroup(fString
.getFieldPtr()[j
]); j
--) {}
143 UFIELD_CATEGORY_NUMBER
,
145 j
- fString
.fZero
+ 1,
149 // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
150 if (numericField
!= 0
151 && cfpos
.matchesField(numericCAF
.category
, numericCAF
.field
)
153 // don't return the same field twice in a row:
154 && (i
- fString
.fZero
> cfpos
.getLimit()
155 || cfpos
.getCategory() != numericCAF
.category
156 || cfpos
.getField() != numericCAF
.field
)
157 && isNumericField(fString
.getFieldPtr()[i
- 1])
158 && !isNumericField(_field
)) {
160 for (; j
>= fString
.fZero
&& isNumericField(fString
.getFieldPtr()[j
]); j
--) {}
164 j
- fString
.fZero
+ 1,
168 // Special case: skip over INTEGER; will be coalesced later.
169 if (_field
== UNUM_INTEGER_FIELD
) {
170 _field
= UNUM_FIELD_COUNT
;
172 // Case 2: no field starting at this position.
173 if (_field
== UNUM_FIELD_COUNT
|| _field
== kEndField
) {
176 // Case 3: check for field starting at this position
177 auto caf
= StringBuilderFieldUtils::expand(_field
);
178 if (cfpos
.matchesField(caf
.category
, caf
.field
)) {
179 fieldStart
= i
- fString
.fZero
;
184 U_ASSERT(currField
== UNUM_FIELD_COUNT
);
188 bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field
) {
189 return field
== UNUM_INTEGER_FIELD
190 || field
== UNUM_GROUPING_SEPARATOR_FIELD
;
193 bool FormattedValueStringBuilderImpl::isNumericField(Field field
) {
194 return StringBuilderFieldUtils::isNumericField(field
);
197 int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit
) const {
198 return unisets::get(unisets::DEFAULT_IGNORABLES
)->spanBack(
199 fString
.getCharPtr() + fString
.fZero
,
201 USET_SPAN_CONTAINED
);
204 int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start
) const {
205 return start
+ unisets::get(unisets::DEFAULT_IGNORABLES
)->span(
206 fString
.getCharPtr() + fString
.fZero
+ start
,
207 fString
.fLength
- start
,
208 USET_SPAN_CONTAINED
);
214 #endif /* #if !UCONFIG_NO_FORMATTING */