• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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