1 // © 2018 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 // This file contains one implementation of FormattedValue.
9 // Other independent implementations should go into their own cpp file for
10 // better dependency modularization.
11 
12 #include "formattedval_impl.h"
13 #include "putilimp.h"
14 
15 U_NAMESPACE_BEGIN
16 
17 
FormattedValueFieldPositionIteratorImpl(int32_t initialFieldCapacity,UErrorCode & status)18 FormattedValueFieldPositionIteratorImpl::FormattedValueFieldPositionIteratorImpl(
19         int32_t initialFieldCapacity,
20         UErrorCode& status)
21         : fFields(initialFieldCapacity * 4, status) {
22 }
23 
24 FormattedValueFieldPositionIteratorImpl::~FormattedValueFieldPositionIteratorImpl() = default;
25 
toString(UErrorCode &) const26 UnicodeString FormattedValueFieldPositionIteratorImpl::toString(
27         UErrorCode&) const {
28     return fString;
29 }
30 
toTempString(UErrorCode &) const31 UnicodeString FormattedValueFieldPositionIteratorImpl::toTempString(
32         UErrorCode&) const {
33     // The alias must point to memory owned by this object;
34     // fastCopyFrom doesn't do this when using a stack buffer.
35     return UnicodeString(TRUE, fString.getBuffer(), fString.length());
36 }
37 
appendTo(Appendable & appendable,UErrorCode &) const38 Appendable& FormattedValueFieldPositionIteratorImpl::appendTo(
39         Appendable& appendable,
40         UErrorCode&) const {
41     appendable.appendString(fString.getBuffer(), fString.length());
42     return appendable;
43 }
44 
nextPosition(ConstrainedFieldPosition & cfpos,UErrorCode &) const45 UBool FormattedValueFieldPositionIteratorImpl::nextPosition(
46         ConstrainedFieldPosition& cfpos,
47         UErrorCode&) const {
48     U_ASSERT(fFields.size() % 4 == 0);
49     int32_t numFields = fFields.size() / 4;
50     int32_t i = static_cast<int32_t>(cfpos.getInt64IterationContext());
51     for (; i < numFields; i++) {
52         UFieldCategory category = static_cast<UFieldCategory>(fFields.elementAti(i * 4));
53         int32_t field = fFields.elementAti(i * 4 + 1);
54         if (cfpos.matchesField(category, field)) {
55             int32_t start = fFields.elementAti(i * 4 + 2);
56             int32_t limit = fFields.elementAti(i * 4 + 3);
57             cfpos.setState(category, field, start, limit);
58             break;
59         }
60     }
61     cfpos.setInt64IterationContext(i == numFields ? i : i + 1);
62     return i < numFields;
63 }
64 
65 
getHandler(UErrorCode & status)66 FieldPositionIteratorHandler FormattedValueFieldPositionIteratorImpl::getHandler(
67         UErrorCode& status) {
68     return FieldPositionIteratorHandler(&fFields, status);
69 }
70 
appendString(UnicodeString string,UErrorCode & status)71 void FormattedValueFieldPositionIteratorImpl::appendString(
72         UnicodeString string,
73         UErrorCode& status) {
74     if (U_FAILURE(status)) {
75         return;
76     }
77     fString.append(string);
78     // Make the string NUL-terminated
79     if (fString.getTerminatedBuffer() == nullptr) {
80         status = U_MEMORY_ALLOCATION_ERROR;
81         return;
82     }
83 }
84 
85 
addOverlapSpans(UFieldCategory spanCategory,int8_t firstIndex,UErrorCode & status)86 void FormattedValueFieldPositionIteratorImpl::addOverlapSpans(
87         UFieldCategory spanCategory,
88         int8_t firstIndex,
89         UErrorCode& status) {
90     // In order to avoid fancy data structures, this is an O(N^2) algorithm,
91     // which should be fine for all real-life applications of this function.
92     int32_t s1a = INT32_MAX;
93     int32_t s1b = 0;
94     int32_t s2a = INT32_MAX;
95     int32_t s2b = 0;
96     int32_t numFields = fFields.size() / 4;
97     for (int32_t i = 0; i<numFields; i++) {
98         int32_t field1 = fFields.elementAti(i * 4 + 1);
99         for (int32_t j = i + 1; j<numFields; j++) {
100             int32_t field2 = fFields.elementAti(j * 4 + 1);
101             if (field1 != field2) {
102                 continue;
103             }
104             // Found a duplicate
105             s1a = uprv_min(s1a, fFields.elementAti(i * 4 + 2));
106             s1b = uprv_max(s1b, fFields.elementAti(i * 4 + 3));
107             s2a = uprv_min(s2a, fFields.elementAti(j * 4 + 2));
108             s2b = uprv_max(s2b, fFields.elementAti(j * 4 + 3));
109             break;
110         }
111     }
112     if (s1a != INT32_MAX) {
113         // Success: add the two span fields
114         fFields.addElement(spanCategory, status);
115         fFields.addElement(firstIndex, status);
116         fFields.addElement(s1a, status);
117         fFields.addElement(s1b, status);
118         fFields.addElement(spanCategory, status);
119         fFields.addElement(1 - firstIndex, status);
120         fFields.addElement(s2a, status);
121         fFields.addElement(s2b, status);
122     }
123 }
124 
125 
sort()126 void FormattedValueFieldPositionIteratorImpl::sort() {
127     // Use bubble sort, O(N^2) but easy and no fancy data structures.
128     int32_t numFields = fFields.size() / 4;
129     while (true) {
130         bool isSorted = true;
131         for (int32_t i=0; i<numFields-1; i++) {
132             int32_t categ1 = fFields.elementAti(i*4 + 0);
133             int32_t field1 = fFields.elementAti(i*4 + 1);
134             int32_t start1 = fFields.elementAti(i*4 + 2);
135             int32_t limit1 = fFields.elementAti(i*4 + 3);
136             int32_t categ2 = fFields.elementAti(i*4 + 4);
137             int32_t field2 = fFields.elementAti(i*4 + 5);
138             int32_t start2 = fFields.elementAti(i*4 + 6);
139             int32_t limit2 = fFields.elementAti(i*4 + 7);
140             int64_t comparison = 0;
141             if (start1 != start2) {
142                 // Higher start index -> higher rank
143                 comparison = start2 - start1;
144             } else if (limit1 != limit2) {
145                 // Higher length (end index) -> lower rank
146                 comparison = limit1 - limit2;
147             } else if (categ1 != categ2) {
148                 // Higher field category -> lower rank
149                 comparison = categ1 - categ2;
150             } else if (field1 != field2) {
151                 // Higher field -> higher rank
152                 comparison = field2 - field1;
153             }
154             if (comparison < 0) {
155                 // Perform a swap
156                 isSorted = false;
157                 fFields.setElementAt(categ2, i*4 + 0);
158                 fFields.setElementAt(field2, i*4 + 1);
159                 fFields.setElementAt(start2, i*4 + 2);
160                 fFields.setElementAt(limit2, i*4 + 3);
161                 fFields.setElementAt(categ1, i*4 + 4);
162                 fFields.setElementAt(field1, i*4 + 5);
163                 fFields.setElementAt(start1, i*4 + 6);
164                 fFields.setElementAt(limit1, i*4 + 7);
165             }
166         }
167         if (isSorted) {
168             break;
169         }
170     }
171 }
172 
173 
174 U_NAMESPACE_END
175 
176 #endif /* #if !UCONFIG_NO_FORMATTING */
177