Coverage Report

Created: 2026-06-07 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/units_router.h
Line
Count
Source
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
41
    RouteResult(MaybeStackVector<Measure> measures, MeasureUnitImpl outputUnit)
42
0
        : 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.
64
    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
0
                              ratesInfo, status) {}
68
69
    ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
70
                        double limit, UnicodeString precision, const ConversionRates &ratesInfo,
71
                        UErrorCode &status)
72
0
        : converter(source, complexTarget, ratesInfo, status), limit(limit),
73
0
          precision(std::move(precision)), targetUnit(complexTarget.copy(status)) {}
74
};
75
76
/**
77
 * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to
78
 * one of the complex units based on the limits.
79
 * For example:
80
 *    if the input is `meter` and the output as following
81
 *    {`foot+inch`, limit: 3.0}
82
 *    {`inch`     , limit: no value (-inf)}
83
 *    Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in
84
 *    `foot+inch`, otherwise, the output will be in `inch`.
85
 *
86
 * NOTE:
87
 *    the output units and the their limits MUST BE in order, for example, if the output units, from the
88
 *    previous example, are the following:
89
 *        {`inch`     , limit: no value (-inf)}
90
 *        {`foot+inch`, limit: 3.0}
91
 *     IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`.
92
 *
93
 * NOTE:
94
 *    the output units  and their limits will be extracted from the units preferences database by knowing
95
 *    the following:
96
 *        - input unit
97
 *        - locale
98
 *        - usage
99
 *
100
 * DESIGN:
101
 *    `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the
102
 *    desired complex units and to check the limit too.
103
 */
104
class U_I18N_API_CLASS UnitsRouter {
105
  public:
106
    U_I18N_API UnitsRouter(StringPiece inputUnitIdentifier, const Locale &locale, StringPiece usage,
107
                           UErrorCode &status);
108
    U_I18N_API UnitsRouter(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage,
109
                           UErrorCode &status);
110
111
    /**
112
     * Performs locale and usage sensitive unit conversion.
113
     * @param quantity The quantity to convert, expressed in terms of inputUnit.
114
     * @param rounder If not null, this RoundingImpl will be used to do rounding
115
     *     on the converted value. If the rounder lacks an fPrecision, the
116
     *     rounder will be modified to use the preferred precision for the usage
117
     *     and locale preference, alternatively with the default precision.
118
     * @param status Receives status.
119
     */
120
    U_I18N_API RouteResult route(double quantity, icu::number::impl::RoundingImpl *rounder,
121
                                 UErrorCode &status) const;
122
123
    /**
124
     * Returns the list of possible output units, i.e. the full set of
125
     * preferences, for the localized, usage-specific unit preferences.
126
     *
127
     * The returned pointer should be valid for the lifetime of the
128
     * UnitsRouter instance.
129
     */
130
    const MaybeStackVector<MeasureUnit> *getOutputUnits() const;
131
132
  private:
133
    // List of possible output units. TODO: converterPreferences_ now also has
134
    // this data available. Maybe drop outputUnits_ and have getOutputUnits
135
    // construct a the list from data in converterPreferences_ instead?
136
    MaybeStackVector<MeasureUnit> outputUnits_;
137
138
    MaybeStackVector<ConverterPreference> converterPreferences_;
139
140
    static number::Precision parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton,
141
                                                      UErrorCode &status);
142
143
    void init(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage, UErrorCode &status);
144
};
145
146
} // namespace units
147
U_NAMESPACE_END
148
149
#endif //__UNITS_ROUTER_H__
150
151
#endif /* #if !UCONFIG_NO_FORMATTING */