quantityformatter.cpp (7733B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * Copyright (C) 2014-2016, International Business Machines 6 * Corporation and others. All Rights Reserved. 7 ****************************************************************************** 8 * quantityformatter.cpp 9 */ 10 11 #include "unicode/utypes.h" 12 13 #if !UCONFIG_NO_FORMATTING 14 15 #include "unicode/simpleformatter.h" 16 #include "quantityformatter.h" 17 #include "uassert.h" 18 #include "unicode/unistr.h" 19 #include "unicode/decimfmt.h" 20 #include "cstring.h" 21 #include "unicode/plurrule.h" 22 #include "charstr.h" 23 #include "unicode/fmtable.h" 24 #include "unicode/fieldpos.h" 25 #include "standardplural.h" 26 #include "uassert.h" 27 #include "number_decimalquantity.h" 28 #include "number_utypes.h" 29 #include "formatted_string_builder.h" 30 31 U_NAMESPACE_BEGIN 32 33 QuantityFormatter::QuantityFormatter() { 34 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { 35 formatters[i] = nullptr; 36 } 37 } 38 39 QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) { 40 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { 41 if (other.formatters[i] == nullptr) { 42 formatters[i] = nullptr; 43 } else { 44 formatters[i] = new SimpleFormatter(*other.formatters[i]); 45 } 46 } 47 } 48 49 QuantityFormatter &QuantityFormatter::operator=( 50 const QuantityFormatter& other) { 51 if (this == &other) { 52 return *this; 53 } 54 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { 55 delete formatters[i]; 56 if (other.formatters[i] == nullptr) { 57 formatters[i] = nullptr; 58 } else { 59 formatters[i] = new SimpleFormatter(*other.formatters[i]); 60 } 61 } 62 return *this; 63 } 64 65 QuantityFormatter::~QuantityFormatter() { 66 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { 67 delete formatters[i]; 68 } 69 } 70 71 void QuantityFormatter::reset() { 72 for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) { 73 delete formatters[i]; 74 formatters[i] = nullptr; 75 } 76 } 77 78 UBool QuantityFormatter::addIfAbsent( 79 const char *variant, 80 const UnicodeString &rawPattern, 81 UErrorCode &status) { 82 int32_t pluralIndex = StandardPlural::indexFromString(variant, status); 83 if (U_FAILURE(status)) { 84 return false; 85 } 86 if (formatters[pluralIndex] != nullptr) { 87 return true; 88 } 89 SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status); 90 if (newFmt == nullptr) { 91 status = U_MEMORY_ALLOCATION_ERROR; 92 return false; 93 } 94 if (U_FAILURE(status)) { 95 delete newFmt; 96 return false; 97 } 98 formatters[pluralIndex] = newFmt; 99 return true; 100 } 101 102 UBool QuantityFormatter::isValid() const { 103 return formatters[StandardPlural::OTHER] != nullptr; 104 } 105 106 const SimpleFormatter *QuantityFormatter::getByVariant( 107 const char *variant) const { 108 U_ASSERT(isValid()); 109 int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant); 110 const SimpleFormatter *pattern = formatters[pluralIndex]; 111 if (pattern == nullptr) { 112 pattern = formatters[StandardPlural::OTHER]; 113 } 114 return pattern; 115 } 116 117 UnicodeString &QuantityFormatter::format( 118 const Formattable &number, 119 const NumberFormat &fmt, 120 const PluralRules &rules, 121 UnicodeString &appendTo, 122 FieldPosition &pos, 123 UErrorCode &status) const { 124 UnicodeString formattedNumber; 125 StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status); 126 if (U_FAILURE(status)) { 127 return appendTo; 128 } 129 const SimpleFormatter *pattern = formatters[p]; 130 if (pattern == nullptr) { 131 pattern = formatters[StandardPlural::OTHER]; 132 if (pattern == nullptr) { 133 status = U_INVALID_STATE_ERROR; 134 return appendTo; 135 } 136 } 137 return format(*pattern, formattedNumber, appendTo, pos, status); 138 } 139 140 // The following methods live here so that class PluralRules does not depend on number formatting, 141 // and the SimpleFormatter does not depend on FieldPosition. 142 143 StandardPlural::Form QuantityFormatter::selectPlural( 144 const Formattable &number, 145 const NumberFormat &fmt, 146 const PluralRules &rules, 147 UnicodeString &formattedNumber, 148 FieldPosition &pos, 149 UErrorCode &status) { 150 if (U_FAILURE(status)) { 151 return StandardPlural::OTHER; 152 } 153 UnicodeString pluralKeyword; 154 const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt); 155 if (decFmt != nullptr) { 156 number::impl::DecimalQuantity dq; 157 decFmt->formatToDecimalQuantity(number, dq, status); 158 if (U_FAILURE(status)) { 159 return StandardPlural::OTHER; 160 } 161 pluralKeyword = rules.select(dq); 162 decFmt->format(number, formattedNumber, pos, status); 163 } else { 164 if (number.getType() == Formattable::kDouble) { 165 pluralKeyword = rules.select(number.getDouble()); 166 } else if (number.getType() == Formattable::kLong) { 167 pluralKeyword = rules.select(number.getLong()); 168 } else if (number.getType() == Formattable::kInt64) { 169 pluralKeyword = rules.select(static_cast<double>(number.getInt64())); 170 } else { 171 status = U_ILLEGAL_ARGUMENT_ERROR; 172 return StandardPlural::OTHER; 173 } 174 fmt.format(number, formattedNumber, pos, status); 175 } 176 return StandardPlural::orOtherFromString(pluralKeyword); 177 } 178 179 void QuantityFormatter::formatAndSelect( 180 double quantity, 181 const NumberFormat& fmt, 182 const PluralRules& rules, 183 FormattedStringBuilder& output, 184 StandardPlural::Form& pluralForm, 185 UErrorCode& status) { 186 UnicodeString pluralKeyword; 187 const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(&fmt); 188 if (df != nullptr) { 189 number::impl::UFormattedNumberData fn; 190 fn.quantity.setToDouble(quantity); 191 const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status); 192 if (U_FAILURE(status)) { 193 return; 194 } 195 lnf->formatImpl(&fn, status); 196 if (U_FAILURE(status)) { 197 return; 198 } 199 output = std::move(fn.getStringRef()); 200 pluralKeyword = rules.select(fn.quantity); 201 } else { 202 UnicodeString result; 203 fmt.format(quantity, result, status); 204 if (U_FAILURE(status)) { 205 return; 206 } 207 // This code path is probably RBNF. Use the generic numeric field. 208 output.append(result, kGeneralNumericField, status); 209 if (U_FAILURE(status)) { 210 return; 211 } 212 pluralKeyword = rules.select(quantity); 213 } 214 pluralForm = StandardPlural::orOtherFromString(pluralKeyword); 215 } 216 217 UnicodeString &QuantityFormatter::format( 218 const SimpleFormatter &pattern, 219 const UnicodeString &value, 220 UnicodeString &appendTo, 221 FieldPosition &pos, 222 UErrorCode &status) { 223 if (U_FAILURE(status)) { 224 return appendTo; 225 } 226 const UnicodeString *param = &value; 227 int32_t offset; 228 pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status); 229 if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) { 230 if (offset >= 0) { 231 pos.setBeginIndex(pos.getBeginIndex() + offset); 232 pos.setEndIndex(pos.getEndIndex() + offset); 233 } else { 234 pos.setBeginIndex(0); 235 pos.setEndIndex(0); 236 } 237 } 238 return appendTo; 239 } 240 241 U_NAMESPACE_END 242 243 #endif /* #if !UCONFIG_NO_FORMATTING */