1// © 2020 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3
4#ifndef __MEASUNIT_IMPL_H__
5#define __MEASUNIT_IMPL_H__
6
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "unicode/measunit.h"
12#include "cmemory.h"
13#include "charstr.h"
14
15U_NAMESPACE_BEGIN
16
17
18static const char16_t kDefaultCurrency[] = u"XXX";
19static const char kDefaultCurrency8[] = "XXX";
20
21
22/**
23 * A struct representing a single unit (optional SI prefix and dimensionality).
24 */
25struct SingleUnitImpl : public UMemory {
26 /**
27 * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
28 * code and returns the base dimensionless unit. Parses if necessary.
29 */
30 static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
31
32 /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
33 MeasureUnit build(UErrorCode& status) const;
34
35 /**
36 * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
37 * sorting and coalescing.
38 *
39 * Takes the sign of dimensionality into account, but not the absolute
40 * value: per-meter is not considered the same as meter, but meter is
41 * considered the same as square-meter.
42 *
43 * The dimensionless unit generally does not get compared, but if it did, it
44 * would sort before other units by virtue of index being < 0 and
45 * dimensionality not being negative.
46 */
47 int32_t compareTo(const SingleUnitImpl& other) const {
48 if (dimensionality < 0 && other.dimensionality > 0) {
49 // Positive dimensions first
50 return 1;
51 }
52 if (dimensionality > 0 && other.dimensionality < 0) {
53 return -1;
54 }
55 if (index < other.index) {
56 return -1;
57 }
58 if (index > other.index) {
59 return 1;
60 }
61 if (siPrefix < other.siPrefix) {
62 return -1;
63 }
64 if (siPrefix > other.siPrefix) {
65 return 1;
66 }
67 return 0;
68 }
69
70 /**
71 * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
72 *
73 * Units with the same base unit and SI prefix should match, except that they must also have
74 * the same dimensionality sign, such that we don't merge numerator and denominator.
75 */
76 bool isCompatibleWith(const SingleUnitImpl& other) const {
77 return (compareTo(other) == 0);
78 }
79
80 /**
81 * Returns true if this unit is the "dimensionless base unit", as produced
82 * by the MeasureUnit() default constructor. (This does not include the
83 * likes of concentrations or angles.)
84 */
85 bool isDimensionless() const {
86 return index == -1;
87 }
88
89 /**
90 * Simple unit index, unique for every simple unit, -1 for the dimensionless
91 * unit. This is an index into a string list in measunit_extra.cpp.
92 *
93 * The default value is -1, meaning the dimensionless unit:
94 * isDimensionless() will return true, until index is changed.
95 */
96 int32_t index = -1;
97
98 /**
99 * SI prefix.
100 *
101 * This is ignored for the dimensionless unit.
102 */
103 UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
104
105 /**
106 * Dimensionality.
107 *
108 * This is meaningless for the dimensionless unit.
109 */
110 int32_t dimensionality = 1;
111};
112
113
114/**
115 * Internal representation of measurement units. Capable of representing all complexities of units,
116 * including mixed and compound units.
117 */
118struct MeasureUnitImpl : public UMemory {
119 /** Extract the MeasureUnitImpl from a MeasureUnit. */
120 static inline const MeasureUnitImpl* get(const MeasureUnit& measureUnit) {
121 return measureUnit.fImpl;
122 }
123
124 /**
125 * Parse a unit identifier into a MeasureUnitImpl.
126 *
127 * @param identifier The unit identifier string.
128 * @param status Set if the identifier string is not valid.
129 * @return A newly parsed value object. Behaviour of this unit is
130 * unspecified if an error is returned via status.
131 */
132 static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
133
134 /**
135 * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
136 *
137 * @param measureUnit The source MeasureUnit.
138 * @param memory A place to write the new MeasureUnitImpl if parsing is required.
139 * @param status Set if an error occurs.
140 * @return A reference to either measureUnit.fImpl or memory.
141 */
142 static const MeasureUnitImpl& forMeasureUnit(
143 const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status);
144
145 /**
146 * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
147 *
148 * @param measureUnit The source MeasureUnit.
149 * @param status Set if an error occurs.
150 * @return A value object, either newly parsed or copied from measureUnit.
151 */
152 static MeasureUnitImpl forMeasureUnitMaybeCopy(
153 const MeasureUnit& measureUnit, UErrorCode& status);
154
155 /**
156 * Used for currency units.
157 */
158 static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) {
159 MeasureUnitImpl result;
160 UErrorCode localStatus = U_ZERO_ERROR;
161 result.identifier.append(currencyCode, localStatus);
162 // localStatus is not expected to fail since currencyCode should be 3 chars long
163 return result;
164 }
165
166 /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */
167 MeasureUnit build(UErrorCode& status) &&;
168
169 /**
170 * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
171 */
172 inline MeasureUnitImpl copy(UErrorCode& status) const {
173 MeasureUnitImpl result;
174 result.complexity = complexity;
175 result.units.appendAll(units, status);
176 result.identifier.append(identifier, status);
177 return result;
178 }
179
180 /** Mutates this MeasureUnitImpl to take the reciprocal. */
181 void takeReciprocal(UErrorCode& status);
182
183 /**
184 * Mutates this MeasureUnitImpl to append a single unit.
185 *
186 * @return true if a new item was added. If unit is the dimensionless unit,
187 * it is never added: the return value will always be false.
188 */
189 bool append(const SingleUnitImpl& singleUnit, UErrorCode& status);
190
191 /** The complexity, either SINGLE, COMPOUND, or MIXED. */
192 UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
193
194 /**
195 * The list of simple units. These may be summed or multiplied, based on the
196 * value of the complexity field.
197 *
198 * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
199 * added to this list.
200 */
201 MaybeStackVector<SingleUnitImpl> units;
202
203 /**
204 * The full unit identifier. Owned by the MeasureUnitImpl. Empty if not computed.
205 */
206 CharString identifier;
207};
208
209
210U_NAMESPACE_END
211
212#endif /* #if !UCONFIG_NO_FORMATTING */
213#endif //__MEASUNIT_IMPL_H__
214