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 RouteResultRouteResult41 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. ConverterPreferenceConverterPreference64 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 ConverterPreferenceConverterPreference69 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 } // namespace units 77 78 // Export explicit template instantiations of MaybeStackArray, MemoryPool and 79 // MaybeStackVector. This is required when building DLLs for Windows. (See 80 // datefmt.h, collationiterator.h, erarules.h and others for similar examples.) 81 // 82 // Note: These need to be outside of the units namespace, or Clang will generate 83 // a compile error. 84 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN 85 template class U_I18N_API MaybeStackArray<units::ConverterPreference*, 8>; 86 template class U_I18N_API MemoryPool<units::ConverterPreference, 8>; 87 template class U_I18N_API MaybeStackVector<units::ConverterPreference, 8>; 88 #endif 89 90 namespace units { 91 92 /** 93 * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to 94 * one of the complex units based on the limits. 95 * For example: 96 * if the input is `meter` and the output as following 97 * {`foot+inch`, limit: 3.0} 98 * {`inch` , limit: no value (-inf)} 99 * Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in 100 * `foot+inch`, otherwise, the output will be in `inch`. 101 * 102 * NOTE: 103 * the output units and the their limits MUST BE in order, for example, if the output units, from the 104 * previous example, are the following: 105 * {`inch` , limit: no value (-inf)} 106 * {`foot+inch`, limit: 3.0} 107 * IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`. 108 * 109 * NOTE: 110 * the output units and their limits will be extracted from the units preferences database by knowing 111 * the following: 112 * - input unit 113 * - locale 114 * - usage 115 * 116 * DESIGN: 117 * `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the 118 * desired complex units and to check the limit too. 119 */ 120 class U_I18N_API UnitsRouter { 121 public: 122 UnitsRouter(StringPiece inputUnitIdentifier, const Locale &locale, StringPiece usage, 123 UErrorCode &status); 124 UnitsRouter(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage, 125 UErrorCode &status); 126 127 /** 128 * Performs locale and usage sensitive unit conversion. 129 * @param quantity The quantity to convert, expressed in terms of inputUnit. 130 * @param rounder If not null, this RoundingImpl will be used to do rounding 131 * on the converted value. If the rounder lacks an fPrecision, the 132 * rounder will be modified to use the preferred precision for the usage 133 * and locale preference, alternatively with the default precision. 134 * @param status Receives status. 135 */ 136 RouteResult route(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const; 137 138 /** 139 * Returns the list of possible output units, i.e. the full set of 140 * preferences, for the localized, usage-specific unit preferences. 141 * 142 * The returned pointer should be valid for the lifetime of the 143 * UnitsRouter instance. 144 */ 145 const MaybeStackVector<MeasureUnit> *getOutputUnits() const; 146 147 private: 148 // List of possible output units. TODO: converterPreferences_ now also has 149 // this data available. Maybe drop outputUnits_ and have getOutputUnits 150 // construct a the list from data in converterPreferences_ instead? 151 MaybeStackVector<MeasureUnit> outputUnits_; 152 153 MaybeStackVector<ConverterPreference> converterPreferences_; 154 155 static number::Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton, 156 UErrorCode &status); 157 158 void init(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage, UErrorCode &status); 159 }; 160 161 } // namespace units 162 U_NAMESPACE_END 163 164 #endif //__UNITS_ROUTER_H__ 165 166 #endif /* #if !UCONFIG_NO_FORMATTING */ 167