Coverage Report

Created: 2023-02-22 06:51

/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 */