number_usageprefs.cpp (6737B)
1 // © 2020 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 "number_usageprefs.h" 9 #include "cstring.h" 10 #include "number_decimalquantity.h" 11 #include "number_microprops.h" 12 #include "number_roundingutils.h" 13 #include "number_skeletons.h" 14 #include "unicode/char16ptr.h" 15 #include "unicode/currunit.h" 16 #include "unicode/fmtable.h" 17 #include "unicode/measure.h" 18 #include "unicode/numberformatter.h" 19 #include "unicode/platform.h" 20 #include "unicode/unum.h" 21 #include "unicode/urename.h" 22 #include "units_data.h" 23 24 using namespace icu; 25 using namespace icu::number; 26 using namespace icu::number::impl; 27 using icu::StringSegment; 28 using icu::units::ConversionRates; 29 30 // Copy constructor 31 StringProp::StringProp(const StringProp &other) : StringProp() { 32 this->operator=(other); 33 } 34 35 // Copy assignment operator 36 StringProp &StringProp::operator=(const StringProp &other) { 37 if (this == &other) { return *this; } // self-assignment: no-op 38 fLength = 0; 39 fError = other.fError; 40 if (fValue != nullptr) { 41 uprv_free(fValue); 42 fValue = nullptr; 43 } 44 if (other.fValue == nullptr) { 45 return *this; 46 } 47 if (U_FAILURE(other.fError)) { 48 // We don't bother trying to allocating memory if we're in any case busy 49 // copying an errored StringProp. 50 return *this; 51 } 52 fValue = static_cast<char*>(uprv_malloc(other.fLength + 1)); 53 if (fValue == nullptr) { 54 fError = U_MEMORY_ALLOCATION_ERROR; 55 return *this; 56 } 57 fLength = other.fLength; 58 uprv_strncpy(fValue, other.fValue, fLength + 1); 59 return *this; 60 } 61 62 // Move constructor 63 StringProp::StringProp(StringProp &&src) noexcept : fValue(src.fValue), 64 fLength(src.fLength), 65 fError(src.fError) { 66 // Take ownership away from src if necessary 67 src.fValue = nullptr; 68 } 69 70 // Move assignment operator 71 StringProp &StringProp::operator=(StringProp &&src) noexcept { 72 if (this == &src) { 73 return *this; 74 } 75 if (fValue != nullptr) { 76 uprv_free(fValue); 77 } 78 fValue = src.fValue; 79 fLength = src.fLength; 80 fError = src.fError; 81 // Take ownership away from src if necessary 82 src.fValue = nullptr; 83 return *this; 84 } 85 86 StringProp::~StringProp() { 87 if (fValue != nullptr) { 88 uprv_free(fValue); 89 fValue = nullptr; 90 } 91 } 92 93 void StringProp::set(StringPiece value) { 94 if (fValue != nullptr) { 95 uprv_free(fValue); 96 fValue = nullptr; 97 } 98 fLength = value.length(); 99 fValue = static_cast<char*>(uprv_malloc(fLength + 1)); 100 if (fValue == nullptr) { 101 fLength = 0; 102 fError = U_MEMORY_ALLOCATION_ERROR; 103 return; 104 } 105 if (fLength > 0) { 106 uprv_strncpy(fValue, value.data(), fLength); 107 } 108 fValue[fLength] = 0; 109 } 110 111 // Populates micros.mixedMeasures and modifies quantity, based on the values in 112 // measures. 113 void mixedMeasuresToMicros(const MaybeStackVector<Measure> &measures, DecimalQuantity *quantity, 114 MicroProps *micros, UErrorCode status) { 115 micros->mixedMeasuresCount = measures.length(); 116 117 if (micros->mixedMeasures.getCapacity() < micros->mixedMeasuresCount) { 118 if (micros->mixedMeasures.resize(micros->mixedMeasuresCount) == nullptr) { 119 status = U_MEMORY_ALLOCATION_ERROR; 120 return; 121 } 122 } 123 124 for (int32_t i = 0; i < micros->mixedMeasuresCount; i++) { 125 switch (measures[i]->getNumber().getType()) { 126 case Formattable::kInt64: 127 micros->mixedMeasures[i] = measures[i]->getNumber().getInt64(); 128 break; 129 130 case Formattable::kDouble: 131 U_ASSERT(micros->indexOfQuantity < 0); 132 quantity->setToDouble(measures[i]->getNumber().getDouble()); 133 micros->indexOfQuantity = i; 134 break; 135 136 default: 137 U_ASSERT(0 == "Found a Measure Number which is neither a double nor an int"); 138 UPRV_UNREACHABLE_EXIT; 139 break; 140 } 141 142 if (U_FAILURE(status)) { 143 return; 144 } 145 } 146 147 if (micros->indexOfQuantity < 0) { 148 // There is no quantity. 149 status = U_INTERNAL_PROGRAM_ERROR; 150 } 151 } 152 153 UsagePrefsHandler::UsagePrefsHandler(const Locale &locale, 154 const MeasureUnit &inputUnit, 155 const StringPiece usage, 156 const MicroPropsGenerator *parent, 157 UErrorCode &status) 158 : fUnitsRouter(inputUnit, locale, usage, status), 159 fParent(parent) { 160 } 161 162 void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, 163 UErrorCode &status) const { 164 fParent->processQuantity(quantity, micros, status); 165 if (U_FAILURE(status)) { 166 return; 167 } 168 169 quantity.roundToInfinity(); // Enables toDouble 170 const units::RouteResult routed = fUnitsRouter.route(quantity.toDouble(), µs.rounder, status); 171 if (U_FAILURE(status)) { 172 return; 173 } 174 const MaybeStackVector<Measure>& routedMeasures = routed.measures; 175 micros.outputUnit = routed.outputUnit.copy(status).build(status); 176 if (U_FAILURE(status)) { 177 return; 178 } 179 180 mixedMeasuresToMicros(routedMeasures, &quantity, µs, status); 181 } 182 183 UnitConversionHandler::UnitConversionHandler(const MeasureUnit &targetUnit, 184 const MicroPropsGenerator *parent, UErrorCode &status) 185 : fOutputUnit(targetUnit), fParent(parent) { 186 MeasureUnitImpl tempInput, tempOutput; 187 188 ConversionRates conversionRates(status); 189 if (U_FAILURE(status)) { 190 return; 191 } 192 193 const MeasureUnitImpl &targetUnitImpl = 194 MeasureUnitImpl::forMeasureUnit(targetUnit, tempOutput, status); 195 fUnitConverter.adoptInsteadAndCheckErrorCode( 196 new ComplexUnitsConverter(targetUnitImpl, conversionRates, status), status); 197 } 198 199 void UnitConversionHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, 200 UErrorCode &status) const { 201 fParent->processQuantity(quantity, micros, status); 202 if (U_FAILURE(status)) { 203 return; 204 } 205 quantity.roundToInfinity(); // Enables toDouble 206 MaybeStackVector<Measure> measures = 207 fUnitConverter->convert(quantity.toDouble(), µs.rounder, status); 208 micros.outputUnit = fOutputUnit; 209 if (U_FAILURE(status)) { 210 return; 211 } 212 213 mixedMeasuresToMicros(measures, &quantity, µs, status); 214 } 215 216 #endif /* #if !UCONFIG_NO_FORMATTING */