/src/icu/source/i18n/units_router.h
Line | Count | Source (jump to first uncovered line) |
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 | | |
42 | | RouteResult(MaybeStackVector<Measure> measures, MeasureUnitImpl outputUnit) |
43 | 0 | : 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. |
65 | | 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 | 0 | ratesInfo, status) {} |
69 | | |
70 | | 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 | 0 | 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 following: |
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 */ |