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/measunit.h" 15 #include "unicode/stringpiece.h" 16 #include "unicode/uobject.h" 17 #include "units_complexconverter.h" 18 #include "units_data.h" 19 20 U_NAMESPACE_BEGIN 21 22 // Forward declarations 23 class Measure; 24 namespace number { 25 class Precision; 26 } 27 28 namespace units { 29 30 struct RouteResult : UMemory { 31 // A list of measures: a single measure for single units, multiple measures 32 // for mixed units. 33 // 34 // TODO(icu-units/icu#21): figure out the right mixed unit API. 35 MaybeStackVector<Measure> measures; 36 37 // The output unit for this RouteResult. This may be a MIXED unit - for 38 // example: "yard-and-foot-and-inch", for which `measures` will have three 39 // elements. 40 MeasureUnitImpl outputUnit; 41 RouteResultRouteResult42 RouteResult(MaybeStackVector<Measure> measures, MeasureUnitImpl outputUnit) 43 : measures(std::move(measures)), outputUnit(std::move(outputUnit)) {} 44 }; 45 46 /** 47 * Contains the complex unit converter and the limit which representing the smallest value that the 48 * converter should accept. For example, if the converter is converting to `foot+inch` and the limit 49 * equals 3.0, thus means the converter should not convert to a value less than `3.0 feet`. 50 * 51 * NOTE: 52 * if the limit doest not has a value `i.e. (std::numeric_limits<double>::lowest())`, this mean there 53 * is no limit for the converter. 54 */ 55 struct ConverterPreference : UMemory { 56 ComplexUnitsConverter converter; 57 double limit; 58 UnicodeString precision; 59 60 // The output unit for this ConverterPreference. This may be a MIXED unit - 61 // for example: "yard-and-foot-and-inch". 62 MeasureUnitImpl targetUnit; 63 64 // In case there is no limit, the limit will be -inf. ConverterPreferenceConverterPreference65 ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, 66 UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status) 67 : ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), precision, 68 ratesInfo, status) {} 69 ConverterPreferenceConverterPreference70 ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget, 71 double limit, UnicodeString precision, const ConversionRates &ratesInfo, 72 UErrorCode &status) 73 : converter(source, complexTarget, ratesInfo, status), limit(limit), 74 precision(std::move(precision)), targetUnit(complexTarget.copy(status)) {} 75 }; 76 77 } // namespace units 78 79 // Export explicit template instantiations of MaybeStackArray, MemoryPool and 80 // MaybeStackVector. This is required when building DLLs for Windows. (See 81 // datefmt.h, collationiterator.h, erarules.h and others for similar examples.) 82 // 83 // Note: These need to be outside of the units namespace, or Clang will generate 84 // a compile error. 85 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN 86 template class U_I18N_API MaybeStackArray<units::ConverterPreference*, 8>; 87 template class U_I18N_API MemoryPool<units::ConverterPreference, 8>; 88 template class U_I18N_API MaybeStackVector<units::ConverterPreference, 8>; 89 #endif 90 91 namespace units { 92 93 /** 94 * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to 95 * one of the complex units based on the limits. 96 * For example: 97 * if the input is `meter` and the output as following 98 * {`foot+inch`, limit: 3.0} 99 * {`inch` , limit: no value (-inf)} 100 * Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in 101 * `foot+inch`, otherwise, the output will be in `inch`. 102 * 103 * NOTE: 104 * the output units and the their limits MUST BE in order, for example, if the output units, from the 105 * previous example, are the following: 106 * {`inch` , limit: no value (-inf)} 107 * {`foot+inch`, limit: 3.0} 108 * IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`. 109 * 110 * NOTE: 111 * the output units and their limits will be extracted from the units preferences database by knowing 112 * the followings: 113 * - input unit 114 * - locale 115 * - usage 116 * 117 * DESIGN: 118 * `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the 119 * desired complex units and to check the limit too. 120 */ 121 class U_I18N_API UnitsRouter { 122 public: 123 UnitsRouter(StringPiece inputUnitIdentifier, StringPiece locale, StringPiece usage, 124 UErrorCode &status); 125 UnitsRouter(const MeasureUnit &inputUnit, StringPiece locale, StringPiece usage, 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, StringPiece 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