mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-08 06:53:45 +00:00
Merge pull request #15 from icu-units/use-impl
Use `Impl` libraries for all internal libraries
This commit is contained in:
commit
cc786cfb3b
10 changed files with 268 additions and 179 deletions
|
@ -12,22 +12,23 @@
|
|||
#include "uassert.h"
|
||||
#include "unicode/fmtable.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/measunit.h"
|
||||
#include "unicode/measure.h"
|
||||
#include "unitconverter.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
namespace units {
|
||||
|
||||
ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
|
||||
const MeasureUnit &outputUnits,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status) {
|
||||
|
||||
if (outputUnits.getComplexity(status) != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, outputUnits, ratesInfo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
units_.emplaceBackAndCheckErrorCode(status, outputUnits);
|
||||
ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
|
||||
const MeasureUnitImpl &outputUnits,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: units_(outputUnits.extractIndividualUnits(status)) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (units_.length() == 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -45,47 +46,19 @@ ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
|
|||
// 2. convert the residual of 6.56168 feet (0.56168) to inches, which will be (6.74016
|
||||
// inches)
|
||||
// 3. then, the final result will be (6 feet and 6.74016 inches)
|
||||
int32_t length;
|
||||
auto singleUnits = outputUnits.splitToSingleUnits(length, status);
|
||||
MaybeStackVector<MeasureUnit> singleUnitsInOrder;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
/**
|
||||
* TODO(younies): ensure units being in order by the biggest unit at first.
|
||||
*
|
||||
* HINT:
|
||||
* MaybeStackVector<SingleUnitImpl> singleUnitsInOrder = MeasureUnitImpl::forMeasureUnitMaybeCopy(outputUnits, status).units;
|
||||
* uprv_sortArray(
|
||||
* singleUnitsInOrder.getAlias(),
|
||||
* singleUnitsInOrder.length(),
|
||||
* sizeof(singleUnitsInOrder[0]),
|
||||
* compareSingleUnits,
|
||||
* nullptr,
|
||||
* false,
|
||||
* &status);
|
||||
*/
|
||||
singleUnitsInOrder.emplaceBackAndCheckErrorCode(status, singleUnits[i]);
|
||||
}
|
||||
|
||||
if (singleUnitsInOrder.length() == 0) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0, n = singleUnitsInOrder.length(); i < n; i++) {
|
||||
for (int i = 0, n = units_.length(); i < n; i++) {
|
||||
if (i == 0) { // first element
|
||||
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *singleUnitsInOrder[i],
|
||||
ratesInfo, status);
|
||||
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *units_[i], ratesInfo,
|
||||
status);
|
||||
} else {
|
||||
unitConverters_.emplaceBackAndCheckErrorCode(status, *singleUnitsInOrder[i - 1],
|
||||
*singleUnitsInOrder[i], ratesInfo, status);
|
||||
unitConverters_.emplaceBackAndCheckErrorCode(status, *units_[i - 1], *units_[i], ratesInfo,
|
||||
status);
|
||||
}
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
units_.appendAll(singleUnitsInOrder, status);
|
||||
}
|
||||
|
||||
UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) const {
|
||||
|
@ -106,8 +79,8 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
|
|||
Formattable formattableNewQuantity(newQuantity);
|
||||
|
||||
// NOTE: Measure would own its MeasureUnit.
|
||||
result.emplaceBackAndCheckErrorCode(status, formattableNewQuantity,
|
||||
new MeasureUnit(*units_[i]), status);
|
||||
MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
|
||||
result.emplaceBackAndCheckErrorCode(status, formattableNewQuantity, type, status);
|
||||
|
||||
// Keep the residual of the quantity.
|
||||
// For example: `3.6 feet`, keep only `0.6 feet`
|
||||
|
@ -116,8 +89,8 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
|
|||
Formattable formattableQuantity(quantity);
|
||||
|
||||
// NOTE: Measure would own its MeasureUnit.
|
||||
result.emplaceBackAndCheckErrorCode(status, formattableQuantity, new MeasureUnit(*units_[i]),
|
||||
status);
|
||||
MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
|
||||
result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#define __COMPLEXUNITSCONVERTER_H__
|
||||
|
||||
#include "cmemory.h"
|
||||
#include "unicode/measunit.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "unicode/errorcode.h"
|
||||
#include "unicode/measure.h"
|
||||
#include "unitconverter.h"
|
||||
#include "unitsdata.h"
|
||||
|
@ -26,7 +27,7 @@ namespace units {
|
|||
* single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
|
||||
* instances of the `UnitConverter` to perform the conversion.
|
||||
*/
|
||||
class U_I18N_API ComplexUnitsConverter {
|
||||
class U_I18N_API ComplexUnitsConverter : UMemory {
|
||||
public:
|
||||
/**
|
||||
* Constructor of `ComplexUnitsConverter`.
|
||||
|
@ -38,7 +39,7 @@ class U_I18N_API ComplexUnitsConverter {
|
|||
* @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
|
||||
* @param status
|
||||
*/
|
||||
ComplexUnitsConverter(const MeasureUnit &inputUnit, const MeasureUnit &outputUnits,
|
||||
ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status);
|
||||
|
||||
// Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest
|
||||
|
@ -58,7 +59,7 @@ class U_I18N_API ComplexUnitsConverter {
|
|||
|
||||
private:
|
||||
MaybeStackVector<UnitConverter> unitConverters_;
|
||||
MaybeStackVector<MeasureUnit> units_;
|
||||
MaybeStackVector<MeasureUnitImpl> units_;
|
||||
};
|
||||
|
||||
} // namespace units
|
||||
|
|
|
@ -2302,6 +2302,14 @@ int32_t MeasureUnit::getOffset() const {
|
|||
return gOffsets[fTypeId] + fSubTypeId;
|
||||
}
|
||||
|
||||
MeasureUnitImpl MeasureUnitImpl::copy(UErrorCode &status) const {
|
||||
MeasureUnitImpl result;
|
||||
result.complexity = complexity;
|
||||
result.units.appendAll(units, status);
|
||||
result.identifier.append(identifier, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* !UNCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -775,6 +775,14 @@ const char *SingleUnitImpl::getSimpleUnitID() const {
|
|||
return gSimpleUnits[index];
|
||||
}
|
||||
|
||||
MeasureUnitImpl::MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) {
|
||||
*this = other.copy(status);
|
||||
}
|
||||
|
||||
MeasureUnitImpl::MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status) {
|
||||
this->append(singleUnit, status);
|
||||
}
|
||||
|
||||
MeasureUnitImpl MeasureUnitImpl::forIdentifier(StringPiece identifier, UErrorCode& status) {
|
||||
return Parser::from(identifier, status).parse(status);
|
||||
}
|
||||
|
@ -810,12 +818,26 @@ bool MeasureUnitImpl::append(const SingleUnitImpl& singleUnit, UErrorCode& statu
|
|||
return appendImpl(*this, singleUnit, status);
|
||||
}
|
||||
|
||||
MaybeStackVector<MeasureUnitImpl> MeasureUnitImpl::extractIndividualUnits(UErrorCode &status) const {
|
||||
MaybeStackVector<MeasureUnitImpl> result;
|
||||
|
||||
if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
result.emplaceBackAndCheckErrorCode(status, *this, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < units.length(); i++) {
|
||||
result.emplaceBackAndCheckErrorCode(status, *units[i], status);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnitImpl::build(UErrorCode& status) && {
|
||||
serialize(*this, status);
|
||||
return MeasureUnit(std::move(*this));
|
||||
}
|
||||
|
||||
|
||||
MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode& status) {
|
||||
return Parser::from(identifier, status).parse(status).build(status);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ static const char kDefaultCurrency8[] = "XXX";
|
|||
/**
|
||||
* A struct representing a single unit (optional SI prefix and dimensionality).
|
||||
*/
|
||||
struct SingleUnitImpl : public UMemory {
|
||||
struct U_I18N_API SingleUnitImpl : public UMemory {
|
||||
/**
|
||||
* Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
|
||||
* code and returns the base dimensionless unit. Parses if necessary.
|
||||
|
@ -125,7 +125,14 @@ struct SingleUnitImpl : public UMemory {
|
|||
* Internal representation of measurement units. Capable of representing all complexities of units,
|
||||
* including mixed and compound units.
|
||||
*/
|
||||
struct MeasureUnitImpl : public UMemory {
|
||||
struct U_I18N_API MeasureUnitImpl : public UMemory {
|
||||
MeasureUnitImpl() = default;
|
||||
MeasureUnitImpl(MeasureUnitImpl &&other) = default;
|
||||
MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status);
|
||||
MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status);
|
||||
|
||||
MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
|
||||
|
||||
/** Extract the MeasureUnitImpl from a MeasureUnit. */
|
||||
static inline const MeasureUnitImpl* get(const MeasureUnit& measureUnit) {
|
||||
return measureUnit.fImpl;
|
||||
|
@ -179,13 +186,17 @@ struct MeasureUnitImpl : public UMemory {
|
|||
/**
|
||||
* Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
|
||||
*/
|
||||
inline MeasureUnitImpl copy(UErrorCode& status) const {
|
||||
MeasureUnitImpl result;
|
||||
result.complexity = complexity;
|
||||
result.units.appendAll(units, status);
|
||||
result.identifier.append(identifier, status);
|
||||
return result;
|
||||
}
|
||||
MeasureUnitImpl copy(UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* Extracts the list of all the individual units inside the `MeasureUnitImpl`.
|
||||
* For example:
|
||||
* - if the `MeasureUnitImpl` is `foot-per-hour`
|
||||
* it will return a list of 1 {`foot-per-hour`}
|
||||
* - if the `MeasureUnitImpl` is `foot-and-inch`
|
||||
* it will return a list of 2 { `foot`, `inch`}
|
||||
*/
|
||||
MaybeStackVector<MeasureUnitImpl> extractIndividualUnits(UErrorCode &status) const;
|
||||
|
||||
/** Mutates this MeasureUnitImpl to take the reciprocal. */
|
||||
void takeReciprocal(UErrorCode& status);
|
||||
|
@ -216,7 +227,6 @@ struct MeasureUnitImpl : public UMemory {
|
|||
CharString identifier;
|
||||
};
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include "double-conversion-string-to-double.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "uassert.h"
|
||||
#include "unicode/errorcode.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/measunit.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unitconverter.h"
|
||||
#include <algorithm>
|
||||
|
@ -235,16 +235,12 @@ Factor loadSingleFactor(StringPiece source, const ConversionRates &ratesInfo, UE
|
|||
}
|
||||
|
||||
// Load Factor of a compound source unit.
|
||||
Factor loadCompoundFactor(const MeasureUnit &source, const ConversionRates &ratesInfo,
|
||||
Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status) {
|
||||
|
||||
Factor result;
|
||||
MeasureUnitImpl memory;
|
||||
const auto &compoundSourceUnit = MeasureUnitImpl::forMeasureUnit(source, memory, status);
|
||||
if (U_FAILURE(status)) return result;
|
||||
|
||||
for (int32_t i = 0, n = compoundSourceUnit.units.length(); i < n; i++) {
|
||||
auto singleUnit = *compoundSourceUnit.units[i]; // a SingleUnitImpl
|
||||
for (int32_t i = 0, n = source.units.length(); i < n; i++) {
|
||||
SingleUnitImpl singleUnit = *source.units[i];
|
||||
|
||||
Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status);
|
||||
if (U_FAILURE(status)) return result;
|
||||
|
@ -264,30 +260,35 @@ Factor loadCompoundFactor(const MeasureUnit &source, const ConversionRates &rate
|
|||
/**
|
||||
* Checks if the source unit and the target unit are simple. For example celsius or fahrenheit. But not
|
||||
* square-celsius or square-fahrenheit.
|
||||
*
|
||||
* NOTE:
|
||||
* Empty unit means simple unit.
|
||||
*/
|
||||
UBool checkSimpleUnit(const MeasureUnit &unit, UErrorCode &status) {
|
||||
MeasureUnitImpl memory;
|
||||
const auto &compoundSourceUnit = MeasureUnitImpl::forMeasureUnit(unit, memory, status);
|
||||
UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) return false;
|
||||
|
||||
if (compoundSourceUnit.complexity != UMEASURE_UNIT_SINGLE) {
|
||||
if (unit.complexity != UMEASURE_UNIT_SINGLE) {
|
||||
return false;
|
||||
}
|
||||
if (unit.units.length() == 0) {
|
||||
// Empty units means simple unit.
|
||||
return true;
|
||||
}
|
||||
|
||||
U_ASSERT(compoundSourceUnit.units.length() == 1);
|
||||
auto singleUnit = *(compoundSourceUnit.units[0]);
|
||||
auto singleUnit = *(unit.units[0]);
|
||||
|
||||
if (singleUnit.dimensionality != 1 || singleUnit.siPrefix != UMEASURE_SI_PREFIX_ONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract conversion rate from `source` to `target`
|
||||
*/
|
||||
void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &source,
|
||||
const MeasureUnit &target, UnitsConvertibilityState unitsState,
|
||||
void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &source,
|
||||
const MeasureUnitImpl &target, Convertibility unitsState,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status) {
|
||||
// Represents the conversion factor from the source to the target.
|
||||
Factor finalFactor;
|
||||
|
@ -299,9 +300,9 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &sourc
|
|||
|
||||
// Merger Factors
|
||||
finalFactor.multiplyBy(sourceToBase);
|
||||
if (unitsState == UnitsConvertibilityState::CONVERTIBLE) {
|
||||
if (unitsState == Convertibility::CONVERTIBLE) {
|
||||
finalFactor.divideBy(targetToBase);
|
||||
} else if (unitsState == UnitsConvertibilityState::RECIPROCAL) {
|
||||
} else if (unitsState == Convertibility::RECIPROCAL) {
|
||||
finalFactor.multiplyBy(targetToBase);
|
||||
} else {
|
||||
status = UErrorCode::U_ARGUMENT_TYPE_MISMATCH;
|
||||
|
@ -321,7 +322,48 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &sourc
|
|||
targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum;
|
||||
}
|
||||
|
||||
conversionRate.reciprocal = unitsState == UnitsConvertibilityState::RECIPROCAL;
|
||||
conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL;
|
||||
}
|
||||
|
||||
struct UnitIndexAndDimension : UMemory {
|
||||
int32_t index = 0;
|
||||
int32_t dimensionality = 0;
|
||||
|
||||
UnitIndexAndDimension(const SingleUnitImpl &singleUnit, int32_t multiplier) {
|
||||
index = singleUnit.index;
|
||||
dimensionality = singleUnit.dimensionality * multiplier;
|
||||
}
|
||||
};
|
||||
|
||||
void mergeSingleUnitWithDimension(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
|
||||
const SingleUnitImpl &shouldBeMerged, int32_t multiplier) {
|
||||
for (int32_t i = 0; i < unitIndicesWithDimension.length(); i++) {
|
||||
auto &unitWithIndex = *unitIndicesWithDimension[i];
|
||||
if (unitWithIndex.index == shouldBeMerged.index) {
|
||||
unitWithIndex.dimensionality += shouldBeMerged.dimensionality * multiplier;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unitIndicesWithDimension.emplaceBack(shouldBeMerged, multiplier);
|
||||
}
|
||||
|
||||
void mergeUnitsAndDimensions(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
|
||||
const MeasureUnitImpl &shouldBeMerged, int32_t multiplier) {
|
||||
for (int32_t unit_i = 0; unit_i < shouldBeMerged.units.length(); unit_i++) {
|
||||
auto singleUnit = *shouldBeMerged.units[unit_i];
|
||||
mergeSingleUnitWithDimension(unitIndicesWithDimension, singleUnit, multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
UBool checkAllDimensionsAreZeros(const MaybeStackVector<UnitIndexAndDimension> &dimensionVector) {
|
||||
for (int32_t i = 0; i < dimensionVector.length(); i++) {
|
||||
if (dimensionVector[i]->dimensionality != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -368,21 +410,20 @@ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Sign
|
|||
* Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
|
||||
* `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
|
||||
*/
|
||||
MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status) {
|
||||
MeasureUnit result;
|
||||
int32_t count;
|
||||
const auto singleUnits = source.splitToSingleUnits(count, status);
|
||||
MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status) {
|
||||
|
||||
MeasureUnitImpl result;
|
||||
if (U_FAILURE(status)) return result;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const auto &singleUnit = singleUnits[i];
|
||||
const auto &singleUnits = source.units;
|
||||
for (int i = 0, count = singleUnits.length(); i < count; ++i) {
|
||||
const auto &singleUnit = *singleUnits[i];
|
||||
// Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`,
|
||||
// we will use `meter`
|
||||
const auto singleUnitImpl = SingleUnitImpl::forMeasureUnit(singleUnit, status);
|
||||
const auto rateInfo =
|
||||
conversionRates.extractConversionInfo(singleUnitImpl.getSimpleUnitID(), status);
|
||||
conversionRates.extractConversionInfo(singleUnit.getSimpleUnitID(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
}
|
||||
|
@ -393,14 +434,12 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
|
|||
|
||||
// Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
|
||||
// must be pow4-meter. (NOTE: hectare --> square-meter)
|
||||
auto compoundBaseUnit = MeasureUnit::forIdentifier(rateInfo->baseUnit.toStringPiece(), status);
|
||||
|
||||
int32_t baseUnitsCount;
|
||||
auto baseUnits = compoundBaseUnit.splitToSingleUnits(baseUnitsCount, status);
|
||||
for (int j = 0; j < baseUnitsCount; j++) {
|
||||
int8_t newDimensionality =
|
||||
baseUnits[j].getDimensionality(status) * singleUnit.getDimensionality(status);
|
||||
result = result.product(baseUnits[j].withDimensionality(newDimensionality, status), status);
|
||||
auto baseUnits =
|
||||
MeasureUnitImpl::forIdentifier(rateInfo->baseUnit.toStringPiece(), status).units;
|
||||
for (int32_t i = 0, baseUnitsCount = baseUnits.length(); i < baseUnitsCount; i++) {
|
||||
baseUnits[i]->dimensionality *= singleUnit.dimensionality;
|
||||
// TODO: Deal with SI-prefix
|
||||
result.append(*baseUnits[i], status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return result;
|
||||
|
@ -411,53 +450,70 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
|
|||
return result;
|
||||
}
|
||||
|
||||
UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
|
||||
const MeasureUnit &target,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status) {
|
||||
if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
|
||||
target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
/**
|
||||
* Determine the convertibility between `source` and `target`.
|
||||
* For example:
|
||||
* `meter` and `foot` are `CONVERTIBLE`.
|
||||
* `meter-per-second` and `second-per-meter` are `RECIPROCAL`.
|
||||
* `meter` and `pound` are `UNCONVERTIBLE`.
|
||||
*
|
||||
* NOTE:
|
||||
* Only works with SINGLE and COMPOUND units. If one of the units is a
|
||||
* MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
|
||||
*/
|
||||
Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
|
||||
const MeasureUnitImpl &target,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status) {
|
||||
|
||||
if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
|
||||
target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return UNCONVERTIBLE;
|
||||
}
|
||||
|
||||
auto sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
|
||||
auto targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
|
||||
|
||||
MeasureUnitImpl sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
|
||||
MeasureUnitImpl targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
|
||||
if (U_FAILURE(status)) return UNCONVERTIBLE;
|
||||
|
||||
if (sourceBaseUnit == targetBaseUnit) return CONVERTIBLE;
|
||||
if (sourceBaseUnit == targetBaseUnit.reciprocal(status)) return RECIPROCAL;
|
||||
MaybeStackVector<UnitIndexAndDimension> convertible;
|
||||
MaybeStackVector<UnitIndexAndDimension> reciprocal;
|
||||
|
||||
auto sourceSimplified = sourceBaseUnit.simplify(status);
|
||||
auto targetSimplified = targetBaseUnit.simplify(status);
|
||||
mergeUnitsAndDimensions(convertible, sourceBaseUnit, 1);
|
||||
mergeUnitsAndDimensions(reciprocal, sourceBaseUnit, 1);
|
||||
|
||||
if (sourceSimplified == targetSimplified) return CONVERTIBLE;
|
||||
if (sourceSimplified == targetSimplified.reciprocal(status)) return RECIPROCAL;
|
||||
mergeUnitsAndDimensions(convertible, targetBaseUnit, -1);
|
||||
mergeUnitsAndDimensions(reciprocal, targetBaseUnit, 1);
|
||||
|
||||
if (checkAllDimensionsAreZeros(convertible)) {
|
||||
return CONVERTIBLE;
|
||||
}
|
||||
|
||||
if (checkAllDimensionsAreZeros(reciprocal)) {
|
||||
return RECIPROCAL;
|
||||
}
|
||||
|
||||
return UNCONVERTIBLE;
|
||||
}
|
||||
|
||||
UnitConverter::UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status) {
|
||||
|
||||
if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
|
||||
target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
UnitConverter::UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: conversionRate_(source.copy(status), target.copy(status)) {
|
||||
if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
|
||||
target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
UnitsConvertibilityState unitsState = checkConvertibility(source, target, ratesInfo, status);
|
||||
Convertibility unitsState = extractConvertibility(source, target, ratesInfo, status);
|
||||
if (U_FAILURE(status)) return;
|
||||
if (unitsState == UnitsConvertibilityState::UNCONVERTIBLE) {
|
||||
if (unitsState == Convertibility::UNCONVERTIBLE) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
conversionRate_.source = source;
|
||||
conversionRate_.target = target;
|
||||
|
||||
loadConversionRate(conversionRate_, source, target, unitsState, ratesInfo, status);
|
||||
loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState,
|
||||
ratesInfo, status);
|
||||
}
|
||||
|
||||
double UnitConverter::convert(double inputValue) const {
|
||||
|
|
|
@ -7,9 +7,12 @@
|
|||
#ifndef __UNITCONVERTER_H__
|
||||
#define __UNITCONVERTER_H__
|
||||
|
||||
#include "unicode/measunit.h"
|
||||
#include "cmemory.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "unicode/errorcode.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unicode/uobject.h"
|
||||
#include "unitconverter.h"
|
||||
#include "unitsdata.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
@ -65,25 +68,28 @@ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Sign
|
|||
/**
|
||||
* Represents the conversion rate between `source` and `target`.
|
||||
*/
|
||||
struct ConversionRate {
|
||||
MeasureUnit source;
|
||||
MeasureUnit target;
|
||||
struct ConversionRate : public UMemory {
|
||||
const MeasureUnitImpl source;
|
||||
const MeasureUnitImpl target;
|
||||
double factorNum = 1;
|
||||
double factorDen = 1;
|
||||
double sourceOffset = 0;
|
||||
double targetOffset = 0;
|
||||
bool reciprocal = false;
|
||||
|
||||
ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target)
|
||||
: source(std::move(source)), target(std::move(target)) {}
|
||||
};
|
||||
|
||||
enum U_I18N_API UnitsConvertibilityState {
|
||||
enum U_I18N_API Convertibility {
|
||||
RECIPROCAL,
|
||||
CONVERTIBLE,
|
||||
UNCONVERTIBLE,
|
||||
};
|
||||
|
||||
MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status);
|
||||
MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Check if the convertibility between `source` and `target`.
|
||||
|
@ -96,10 +102,10 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
|
|||
* Only works with SINGLE and COMPOUND units. If one of the units is a
|
||||
* MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
|
||||
*/
|
||||
UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
|
||||
const MeasureUnit &target,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status);
|
||||
Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
|
||||
const MeasureUnitImpl &target,
|
||||
const ConversionRates &conversionRates,
|
||||
UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Converts from a source `MeasureUnit` to a target `MeasureUnit`.
|
||||
|
@ -120,8 +126,8 @@ class U_I18N_API UnitConverter : public UMemory {
|
|||
* @param target represents the target unit.
|
||||
* @param status
|
||||
*/
|
||||
UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status);
|
||||
UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Convert a value in the source unit to another value in the target unit.
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
|
||||
#include "charstr.h"
|
||||
#include "cmemory.h"
|
||||
#include "cstring.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "resource.h"
|
||||
#include "unicode/measure.h"
|
||||
#include "unitconverter.h"
|
||||
#include "unitsdata.h"
|
||||
#include "unitsrouter.h"
|
||||
|
||||
|
@ -22,7 +25,9 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
ConversionRates conversionRates(status);
|
||||
UnitPreferences prefs(status);
|
||||
|
||||
MeasureUnit baseUnit = extractCompoundBaseUnit(inputUnit, conversionRates, status);
|
||||
MeasureUnitImpl inputUnitImpl = MeasureUnitImpl::forMeasureUnitMaybeCopy(inputUnit, status);
|
||||
MeasureUnit baseUnit =
|
||||
(extractCompoundBaseUnit(inputUnitImpl, conversionRates, status)).build(status);
|
||||
CharString category = getUnitCategory(baseUnit.getIdentifier(), status);
|
||||
|
||||
const UnitPreference *const *unitPreferences;
|
||||
|
@ -32,7 +37,8 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
for (int i = 0; i < preferencesCount; ++i) {
|
||||
const auto &preference = *unitPreferences[i];
|
||||
|
||||
MeasureUnit complexTargetUnit = MeasureUnit::forIdentifier(preference.unit.data(), status);
|
||||
MeasureUnitImpl complexTargetUnitImpl =
|
||||
MeasureUnitImpl::forIdentifier(preference.unit.data(), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
@ -49,9 +55,11 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
return;
|
||||
}
|
||||
|
||||
outputUnits_.emplaceBackAndCheckErrorCode(status, complexTargetUnit);
|
||||
converterPreferences_.emplaceBackAndCheckErrorCode(
|
||||
status, inputUnit, complexTargetUnit, preference.geq, precision, conversionRates, status);
|
||||
outputUnits_.emplaceBackAndCheckErrorCode(status,
|
||||
complexTargetUnitImpl.copy(status).build(status));
|
||||
converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnitImpl, complexTargetUnitImpl,
|
||||
preference.geq, std::move(precision),
|
||||
conversionRates, status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "cmemory.h"
|
||||
#include "complexunitsconverter.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "unicode/measunit.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unicode/uobject.h"
|
||||
|
@ -46,14 +47,16 @@ struct ConverterPreference : UMemory {
|
|||
UnicodeString precision;
|
||||
|
||||
// In case there is no limit, the limit will be -inf.
|
||||
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, UnicodeString precision,
|
||||
const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
|
||||
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), precision,
|
||||
ratesInfo, status) {}
|
||||
|
||||
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, double limit,
|
||||
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
|
||||
: converter(source, complexTarget, ratesInfo, status), limit(limit), precision(precision) {}
|
||||
ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
|
||||
double limit, UnicodeString precision, const ConversionRates &ratesInfo,
|
||||
UErrorCode &status)
|
||||
: converter(source, complexTarget, ratesInfo, status), limit(limit),
|
||||
precision(std::move(precision)) {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -121,26 +121,27 @@ void UnitsTest::testConversionCapability() {
|
|||
struct TestCase {
|
||||
const char *const source;
|
||||
const char *const target;
|
||||
const UnitsConvertibilityState expectedState;
|
||||
const Convertibility expectedState;
|
||||
} testCases[]{
|
||||
{"meter", "foot", CONVERTIBLE}, //
|
||||
{"kilometer", "foot", CONVERTIBLE}, //
|
||||
{"hectare", "square-foot", CONVERTIBLE}, //
|
||||
{"kilometer-per-second", "second-per-meter", RECIPROCAL}, //
|
||||
{"square-meter", "square-foot", CONVERTIBLE}, //
|
||||
{"kilometer-per-second", "foot-per-second", CONVERTIBLE}, //
|
||||
{"square-hectare", "pow4-foot", CONVERTIBLE}, //
|
||||
{"square-kilometer-per-second", "second-per-square-meter", RECIPROCAL}, //
|
||||
{"meter", "foot", CONVERTIBLE}, //
|
||||
{"kilometer", "foot", CONVERTIBLE}, //
|
||||
{"hectare", "square-foot", CONVERTIBLE}, //
|
||||
{"kilometer-per-second", "second-per-meter", RECIPROCAL}, //
|
||||
{"square-meter", "square-foot", CONVERTIBLE}, //
|
||||
{"kilometer-per-second", "foot-per-second", CONVERTIBLE}, //
|
||||
{"square-hectare", "pow4-foot", CONVERTIBLE}, //
|
||||
{"square-kilometer-per-second", "second-per-square-meter", RECIPROCAL}, //
|
||||
{"cubic-kilometer-per-second-meter", "second-per-square-meter", RECIPROCAL}, //
|
||||
};
|
||||
|
||||
for (const auto &testCase : testCases) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
|
||||
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
|
||||
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
|
||||
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
|
||||
|
||||
ConversionRates conversionRates(status);
|
||||
auto convertibility = checkConvertibility(source, target, conversionRates, status);
|
||||
auto convertibility = extractConvertibility(source, target, conversionRates, status);
|
||||
|
||||
assertEquals(UnicodeString("Conversion Capability: ") + testCase.source + " to " +
|
||||
testCase.target,
|
||||
|
@ -171,8 +172,8 @@ void UnitsTest::testSiPrefixes() {
|
|||
for (const auto &testCase : testCases) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
|
||||
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
|
||||
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
|
||||
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
|
||||
|
||||
ConversionRates conversionRates(status);
|
||||
UnitConverter converter(source, target, conversionRates, status);
|
||||
|
@ -206,8 +207,8 @@ void UnitsTest::testMass() {
|
|||
for (const auto &testCase : testCases) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
|
||||
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
|
||||
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
|
||||
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
|
||||
|
||||
ConversionRates conversionRates(status);
|
||||
UnitConverter converter(source, target, conversionRates, status);
|
||||
|
@ -240,8 +241,8 @@ void UnitsTest::testTemperature() {
|
|||
for (const auto &testCase : testCases) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
|
||||
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
|
||||
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
|
||||
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
|
||||
|
||||
ConversionRates conversionRates(status);
|
||||
UnitConverter converter(source, target, conversionRates, status);
|
||||
|
@ -278,8 +279,8 @@ void UnitsTest::testArea() {
|
|||
for (const auto &testCase : testCases) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
|
||||
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
|
||||
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
|
||||
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
|
||||
|
||||
ConversionRates conversionRates(status);
|
||||
UnitConverter converter(source, target, conversionRates, status);
|
||||
|
@ -356,12 +357,12 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
|
|||
return;
|
||||
}
|
||||
|
||||
MeasureUnit sourceUnit = MeasureUnit::forIdentifier(x, status);
|
||||
MeasureUnitImpl sourceUnit = MeasureUnitImpl::forIdentifier(x, status);
|
||||
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", x.length(), x.data())) {
|
||||
return;
|
||||
}
|
||||
|
||||
MeasureUnit targetUnit = MeasureUnit::forIdentifier(y, status);
|
||||
MeasureUnitImpl targetUnit = MeasureUnitImpl::forIdentifier(y, status);
|
||||
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", y.length(), y.data())) {
|
||||
return;
|
||||
}
|
||||
|
@ -373,16 +374,16 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
|
|||
expected, commentConversionFormula.length(), commentConversionFormula.data());
|
||||
|
||||
// Convertibility:
|
||||
auto convertibility = checkConvertibility(sourceUnit, targetUnit, *ctx->conversionRates, status);
|
||||
if (status.errIfFailureAndReset("checkConvertibility(<%s>, <%s>, ...)", sourceUnit.getIdentifier(),
|
||||
targetUnit.getIdentifier())) {
|
||||
auto convertibility = extractConvertibility(sourceUnit, targetUnit, *ctx->conversionRates, status);
|
||||
if (status.errIfFailureAndReset("extractConvertibility(<%s>, <%s>, ...)",
|
||||
sourceUnit.identifier.data(), targetUnit.identifier.data())) {
|
||||
return;
|
||||
}
|
||||
CharString msg;
|
||||
msg.append("convertible: ", status)
|
||||
.append(sourceUnit.getIdentifier(), status)
|
||||
.append(sourceUnit.identifier.data(), status)
|
||||
.append(" -> ", status)
|
||||
.append(targetUnit.getIdentifier(), status);
|
||||
.append(targetUnit.identifier.data(), status);
|
||||
if (status.errIfFailureAndReset("msg construction")) {
|
||||
return;
|
||||
}
|
||||
|
@ -391,7 +392,7 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
|
|||
// Conversion:
|
||||
UnitConverter converter(sourceUnit, targetUnit, *ctx->conversionRates, status);
|
||||
if (status.errIfFailureAndReset("constructor: UnitConverter(<%s>, <%s>, status)",
|
||||
sourceUnit.getIdentifier(), targetUnit.getIdentifier())) {
|
||||
sourceUnit.identifier.data(), targetUnit.identifier.data())) {
|
||||
return;
|
||||
}
|
||||
double got = converter.convert(1000);
|
||||
|
@ -598,6 +599,7 @@ void unitPreferencesTestDataLineFn(void *context, char *fields[][2], int32_t fie
|
|||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnitsRouter router(inputMeasureUnit, region, usage, status);
|
||||
if (status.errIfFailureAndReset("UnitsRouter(<%s>, \"%.*s\", \"%.*s\", status)",
|
||||
inputMeasureUnit.getIdentifier(), region.length(), region.data(),
|
||||
|
|
Loading…
Add table
Reference in a new issue