units_router.h (5897B)
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 #ifndef __UNITS_ROUTER_H__ 8 #define __UNITS_ROUTER_H__ 9 10 #include <limits> 11 12 #include "cmemory.h" 13 #include "measunit_impl.h" 14 #include "unicode/locid.h" 15 #include "unicode/measunit.h" 16 #include "unicode/stringpiece.h" 17 #include "unicode/uobject.h" 18 #include "units_complexconverter.h" 19 #include "units_data.h" 20 21 U_NAMESPACE_BEGIN 22 23 // Forward declarations 24 class Measure; 25 namespace number { 26 class Precision; 27 } 28 29 namespace units { 30 31 struct RouteResult : UMemory { 32 // A list of measures: a single measure for single units, multiple measures 33 // for mixed units. 34 MaybeStackVector<Measure> measures; 35 36 // The output unit for this RouteResult. This may be a MIXED unit - for 37 // example: "yard-and-foot-and-inch", for which `measures` will have three 38 // elements. 39 MeasureUnitImpl outputUnit; 40 41 RouteResult(MaybeStackVector<Measure> measures, MeasureUnitImpl outputUnit) 42 : measures(std::move(measures)), outputUnit(std::move(outputUnit)) {} 43 }; 44 45 /** 46 * Contains the complex unit converter and the limit which representing the smallest value that the 47 * converter should accept. For example, if the converter is converting to `foot+inch` and the limit 48 * equals 3.0, thus means the converter should not convert to a value less than `3.0 feet`. 49 * 50 * NOTE: 51 * if the limit doest not has a value `i.e. (std::numeric_limits<double>::lowest())`, this mean there 52 * is no limit for the converter. 53 */ 54 struct ConverterPreference : UMemory { 55 ComplexUnitsConverter converter; 56 double limit; 57 UnicodeString precision; 58 59 // The output unit for this ConverterPreference. This may be a MIXED unit - 60 // for example: "yard-and-foot-and-inch". 61 MeasureUnitImpl targetUnit; 62 63 // In case there is no limit, the limit will be -inf. 64 ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, 65 UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status) 66 : ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), precision, 67 ratesInfo, status) {} 68 69 ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, 70 double limit, UnicodeString precision, const ConversionRates &ratesInfo, 71 UErrorCode &status) 72 : converter(source, complexTarget, ratesInfo, status), limit(limit), 73 precision(std::move(precision)), targetUnit(complexTarget.copy(status)) {} 74 }; 75 76 /** 77 * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to 78 * one of the complex units based on the limits. 79 * For example: 80 * if the input is `meter` and the output as following 81 * {`foot+inch`, limit: 3.0} 82 * {`inch` , limit: no value (-inf)} 83 * Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in 84 * `foot+inch`, otherwise, the output will be in `inch`. 85 * 86 * NOTE: 87 * the output units and the their limits MUST BE in order, for example, if the output units, from the 88 * previous example, are the following: 89 * {`inch` , limit: no value (-inf)} 90 * {`foot+inch`, limit: 3.0} 91 * IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`. 92 * 93 * NOTE: 94 * the output units and their limits will be extracted from the units preferences database by knowing 95 * the following: 96 * - input unit 97 * - locale 98 * - usage 99 * 100 * DESIGN: 101 * `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the 102 * desired complex units and to check the limit too. 103 */ 104 class U_I18N_API_CLASS UnitsRouter { 105 public: 106 U_I18N_API UnitsRouter(StringPiece inputUnitIdentifier, const Locale &locale, StringPiece usage, 107 UErrorCode &status); 108 U_I18N_API UnitsRouter(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage, 109 UErrorCode &status); 110 111 /** 112 * Performs locale and usage sensitive unit conversion. 113 * @param quantity The quantity to convert, expressed in terms of inputUnit. 114 * @param rounder If not null, this RoundingImpl will be used to do rounding 115 * on the converted value. If the rounder lacks an fPrecision, the 116 * rounder will be modified to use the preferred precision for the usage 117 * and locale preference, alternatively with the default precision. 118 * @param status Receives status. 119 */ 120 U_I18N_API RouteResult route(double quantity, icu::number::impl::RoundingImpl *rounder, 121 UErrorCode &status) const; 122 123 /** 124 * Returns the list of possible output units, i.e. the full set of 125 * preferences, for the localized, usage-specific unit preferences. 126 * 127 * The returned pointer should be valid for the lifetime of the 128 * UnitsRouter instance. 129 */ 130 const MaybeStackVector<MeasureUnit> *getOutputUnits() const; 131 132 private: 133 // List of possible output units. TODO: converterPreferences_ now also has 134 // this data available. Maybe drop outputUnits_ and have getOutputUnits 135 // construct a the list from data in converterPreferences_ instead? 136 MaybeStackVector<MeasureUnit> outputUnits_; 137 138 MaybeStackVector<ConverterPreference> converterPreferences_; 139 140 static number::Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, 141 UErrorCode &status); 142 143 void init(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage, UErrorCode &status); 144 }; 145 146 } // namespace units 147 U_NAMESPACE_END 148 149 #endif //__UNITS_ROUTER_H__ 150 151 #endif /* #if !UCONFIG_NO_FORMATTING */