1 // © 2017 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 #include "unicode/numberformatter.h"
9 #include "number_types.h"
10 #include "formatted_string_builder.h"
11 #include "number_decimfmtprops.h"
12 
13 using namespace icu;
14 using namespace icu::number;
15 using namespace icu::number::impl;
16 
17 namespace {
18 
19 int32_t
addPaddingHelper(UChar32 paddingCp,int32_t requiredPadding,FormattedStringBuilder & string,int32_t index,UErrorCode & status)20 addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, FormattedStringBuilder &string, int32_t index,
21                  UErrorCode &status) {
22     for (int32_t i = 0; i < requiredPadding; i++) {
23         // TODO: If appending to the end, this will cause actual insertion operations. Improve.
24         string.insertCodePoint(index, paddingCp, kUndefinedField, status);
25     }
26     return U16_LENGTH(paddingCp) * requiredPadding;
27 }
28 
29 }
30 
Padder(UChar32 cp,int32_t width,UNumberFormatPadPosition position)31 Padder::Padder(UChar32 cp, int32_t width, UNumberFormatPadPosition position) : fWidth(width) {
32     // TODO(13034): Consider making this a string instead of code point.
33     fUnion.padding.fCp = cp;
34     fUnion.padding.fPosition = position;
35 }
36 
Padder(int32_t width)37 Padder::Padder(int32_t width) : fWidth(width) {}
38 
none()39 Padder Padder::none() {
40     return {-1};
41 }
42 
codePoints(UChar32 cp,int32_t targetWidth,UNumberFormatPadPosition position)43 Padder Padder::codePoints(UChar32 cp, int32_t targetWidth, UNumberFormatPadPosition position) {
44     // TODO: Validate the code point?
45     if (targetWidth >= 0) {
46         return {cp, targetWidth, position};
47     } else {
48         return {U_NUMBER_ARG_OUTOFBOUNDS_ERROR};
49     }
50 }
51 
forProperties(const DecimalFormatProperties & properties)52 Padder Padder::forProperties(const DecimalFormatProperties& properties) {
53     UChar32 padCp;
54     if (properties.padString.length() > 0) {
55         padCp = properties.padString.char32At(0);
56     } else {
57         padCp = kFallbackPaddingString[0];
58     }
59     return {padCp, properties.formatWidth, properties.padPosition.getOrDefault(UNUM_PAD_BEFORE_PREFIX)};
60 }
61 
padAndApply(const Modifier & mod1,const Modifier & mod2,FormattedStringBuilder & string,int32_t leftIndex,int32_t rightIndex,UErrorCode & status) const62 int32_t Padder::padAndApply(const Modifier &mod1, const Modifier &mod2,
63                             FormattedStringBuilder &string, int32_t leftIndex, int32_t rightIndex,
64                             UErrorCode &status) const {
65     int32_t modLength = mod1.getCodePointCount() + mod2.getCodePointCount();
66     int32_t requiredPadding = fWidth - modLength - string.codePointCount();
67     U_ASSERT(leftIndex == 0 &&
68              rightIndex == string.length()); // fix the previous line to remove this assertion
69 
70     int length = 0;
71     if (requiredPadding <= 0) {
72         // Padding is not required.
73         length += mod1.apply(string, leftIndex, rightIndex, status);
74         length += mod2.apply(string, leftIndex, rightIndex + length, status);
75         return length;
76     }
77 
78     PadPosition position = fUnion.padding.fPosition;
79     UChar32 paddingCp = fUnion.padding.fCp;
80     if (position == UNUM_PAD_AFTER_PREFIX) {
81         length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status);
82     } else if (position == UNUM_PAD_BEFORE_SUFFIX) {
83         length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status);
84     }
85     length += mod1.apply(string, leftIndex, rightIndex + length, status);
86     length += mod2.apply(string, leftIndex, rightIndex + length, status);
87     if (position == UNUM_PAD_BEFORE_PREFIX) {
88         length += addPaddingHelper(paddingCp, requiredPadding, string, leftIndex, status);
89     } else if (position == UNUM_PAD_AFTER_SUFFIX) {
90         length += addPaddingHelper(paddingCp, requiredPadding, string, rightIndex + length, status);
91     }
92 
93     return length;
94 }
95 
96 #endif /* #if !UCONFIG_NO_FORMATTING */
97