ICU-21087 Merge maint/maint-67 to master
This commit is contained in:
commit
a5c940dfd8
@ -1,6 +1,6 @@
|
||||
{
|
||||
"localeFilter": {
|
||||
"filterType": "language",
|
||||
"filterType": "locale",
|
||||
"whitelist": [
|
||||
"en",
|
||||
"de",
|
||||
|
@ -202,7 +202,7 @@ summarizes the ICU data files and their corresponding features and categories:
|
||||
| Region Display <br/> Names | `"region_tree"` | region/\*.txt | **1.1 MiB** |
|
||||
| Rule-Based <br/> Number Formatting <br/> (Spellout, Ordinals) | `"rbnf_tree"` | rbnf/\*.txt | 538 KiB |
|
||||
| StringPrep | `"stringprep"` | sprep/\*.txt | 193 KiB |
|
||||
| Time Zones | `"misc"` <br/> `"zone_tree"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** |
|
||||
| Time Zones | `"misc"` <br/> `"zone_tree"` <br/> `"zone_supplemental"` | misc/metaZones.txt <br/> misc/timezoneTypes.txt <br/> misc/windowsZones.txt <br/> misc/zoneinfo64.txt <br/> zone/\*.txt <br/> zone/tzdbNames.txt | 41 KiB <br/> 20 KiB <br/> 22 KiB <br/> 151 KiB <br/> **2.7 MiB** <br/> 4.8 KiB |
|
||||
| Transliteration | `"translit"` | translit/\*.txt | 685 KiB |
|
||||
| Unicode Character <br/> Names | `"unames"` | in/unames.icu | 269 KiB |
|
||||
| Unicode Text Layout | `"ulayout"` | in/ulayout.icu | 14 KiB |
|
||||
|
@ -466,6 +466,7 @@ LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT :
|
||||
thresholdDistance(src.thresholdDistance),
|
||||
demotionPerDesiredLocale(src.demotionPerDesiredLocale),
|
||||
favorSubtag(src.favorSubtag),
|
||||
direction(src.direction),
|
||||
supportedLocales(src.supportedLocales), lsrs(src.lsrs),
|
||||
supportedLocalesLength(src.supportedLocalesLength),
|
||||
supportedLsrToIndex(src.supportedLsrToIndex),
|
||||
@ -502,6 +503,7 @@ LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT {
|
||||
thresholdDistance = src.thresholdDistance;
|
||||
demotionPerDesiredLocale = src.demotionPerDesiredLocale;
|
||||
favorSubtag = src.favorSubtag;
|
||||
direction = src.direction;
|
||||
supportedLocales = src.supportedLocales;
|
||||
lsrs = src.lsrs;
|
||||
supportedLocalesLength = src.supportedLocalesLength;
|
||||
|
@ -83,25 +83,6 @@ umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode);
|
||||
U_CAPI void U_EXPORT2
|
||||
umutablecptrie_close(UMutableCPTrie *trie);
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \class LocalUMutableCPTriePointer
|
||||
* "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
|
||||
* For most methods see the LocalPointerBase base class.
|
||||
*
|
||||
* @see LocalPointerBase
|
||||
* @see LocalPointer
|
||||
* @stable ICU 63
|
||||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a mutable trie with the same contents as the UCPMap.
|
||||
* You must umutablecptrie_close() the mutable trie once you are done using it.
|
||||
@ -235,4 +216,23 @@ umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieVal
|
||||
|
||||
U_CDECL_END
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \class LocalUMutableCPTriePointer
|
||||
* "Smart pointer" class, closes a UMutableCPTrie via umutablecptrie_close().
|
||||
* For most methods see the LocalPointerBase base class.
|
||||
*
|
||||
* @see LocalPointerBase
|
||||
* @see LocalPointer
|
||||
* @stable ICU 63
|
||||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUMutableCPTriePointer, UMutableCPTrie, umutablecptrie_close);
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -183,25 +183,6 @@ typedef struct UText UText; /**< C typedef for struct UText. @stable ICU 3.6 */
|
||||
U_STABLE UText * U_EXPORT2
|
||||
utext_close(UText *ut);
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \class LocalUTextPointer
|
||||
* "Smart pointer" class, closes a UText via utext_close().
|
||||
* For most methods see the LocalPointerBase base class.
|
||||
*
|
||||
* @see LocalPointerBase
|
||||
* @see LocalPointer
|
||||
* @stable ICU 4.4
|
||||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Open a read-only UText implementation for UTF-8 strings.
|
||||
*
|
||||
@ -1599,5 +1580,24 @@ enum {
|
||||
U_CDECL_END
|
||||
|
||||
|
||||
#if U_SHOW_CPLUSPLUS_API
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \class LocalUTextPointer
|
||||
* "Smart pointer" class, closes a UText via utext_close().
|
||||
* For most methods see the LocalPointerBase base class.
|
||||
*
|
||||
* @see LocalPointerBase
|
||||
* @see LocalPointer
|
||||
* @stable ICU 4.4
|
||||
*/
|
||||
U_DEFINE_LOCAL_OPEN_POINTER(LocalUTextPointer, UText, utext_close);
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -33,6 +33,7 @@ def generate(config, io, common_vars):
|
||||
requests += generate_unames(config, io, common_vars)
|
||||
requests += generate_misc(config, io, common_vars)
|
||||
requests += generate_curr_supplemental(config, io, common_vars)
|
||||
requests += generate_zone_supplemental(config, io, common_vars)
|
||||
requests += generate_translit(config, io, common_vars)
|
||||
|
||||
# Res Tree Files
|
||||
@ -399,6 +400,29 @@ def generate_curr_supplemental(config, io, common_vars):
|
||||
]
|
||||
|
||||
|
||||
def generate_zone_supplemental(config, io, common_vars):
|
||||
# tzdbNames Res File
|
||||
input_file = InFile("zone/tzdbNames.txt")
|
||||
input_basename = "tzdbNames.txt"
|
||||
output_file = OutFile("zone/tzdbNames.res")
|
||||
return [
|
||||
SingleExecutionRequest(
|
||||
name = "zone_supplemental_res",
|
||||
category = "zone_supplemental",
|
||||
dep_targets = [],
|
||||
input_files = [input_file],
|
||||
output_files = [output_file],
|
||||
tool = IcuTool("genrb"),
|
||||
args = "-s {IN_DIR}/zone -d {OUT_DIR}/zone -i {OUT_DIR} "
|
||||
"-k "
|
||||
"{INPUT_BASENAME}",
|
||||
format_with = {
|
||||
"INPUT_BASENAME": input_basename
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def generate_translit(config, io, common_vars):
|
||||
input_files = [
|
||||
InFile("translit/root.txt"),
|
||||
@ -444,10 +468,11 @@ def generate_tree(
|
||||
requests = []
|
||||
category = "%s_tree" % sub_dir
|
||||
out_prefix = "%s/" % out_sub_dir if out_sub_dir else ""
|
||||
# TODO: Clean this up for curr
|
||||
input_files = [InFile(filename) for filename in io.glob("%s/*.txt" % sub_dir)]
|
||||
if sub_dir == "curr":
|
||||
input_files.remove(InFile("curr/supplementalData.txt"))
|
||||
if sub_dir == "zone":
|
||||
input_files.remove(InFile("zone/tzdbNames.txt"))
|
||||
input_basenames = [v.filename[len(sub_dir)+1:] for v in input_files]
|
||||
output_files = [
|
||||
OutFile("%s%s.res" % (out_prefix, v[:-4]))
|
||||
|
@ -66,15 +66,23 @@ inline void abort_noreturn() { abort(); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Not all compilers support __has_attribute and combining a check for both
|
||||
// ifdef and __has_attribute on the same preprocessor line isn't portable.
|
||||
#ifdef __has_attribute
|
||||
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
|
||||
#else
|
||||
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_UNUSED
|
||||
#ifdef __GNUC__
|
||||
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
|
||||
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_UNUSED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && __has_attribute(uninitialized)
|
||||
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
|
||||
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED
|
||||
|
@ -348,6 +348,7 @@ const ListFormatInternal* ListFormatter::getListFormatInternal(
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
static const char* typeWidthToStyleString(UListFormatterType type, UListFormatterWidth width) {
|
||||
switch (type) {
|
||||
case ULISTFMT_TYPE_AND:
|
||||
@ -391,6 +392,7 @@ static const char* typeWidthToStyleString(UListFormatterType type, UListFormatte
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const UChar solidus = 0x2F;
|
||||
static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
|
||||
@ -511,9 +513,14 @@ ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
|
||||
}
|
||||
|
||||
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
return createInstance(locale, ULISTFMT_TYPE_AND, ULISTFMT_WIDTH_WIDE, errorCode);
|
||||
#else
|
||||
return createInstance(locale, "standard", errorCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
ListFormatter* ListFormatter::createInstance(
|
||||
const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode) {
|
||||
const char* style = typeWidthToStyleString(type, width);
|
||||
@ -523,6 +530,7 @@ ListFormatter* ListFormatter::createInstance(
|
||||
}
|
||||
return createInstance(locale, style, errorCode);
|
||||
}
|
||||
#endif
|
||||
|
||||
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
|
||||
const ListFormatInternal* listFormatInternal = getListFormatInternal(locale, style, errorCode);
|
||||
|
@ -537,9 +537,9 @@ static const char * const gSubTypes[] = {
|
||||
"solar-mass",
|
||||
"stone",
|
||||
"ton",
|
||||
"one",
|
||||
"percent",
|
||||
"permille",
|
||||
"", // TODO(ICU-21076): manual edit of what should have been generated by Java.
|
||||
"percent", // TODO(ICU-21076): regenerate, deal with duplication.
|
||||
"permille", // TODO(ICU-21076): regenerate, deal with duplication.
|
||||
"gigawatt",
|
||||
"horsepower",
|
||||
"kilowatt",
|
||||
|
@ -12,6 +12,7 @@
|
||||
// Helpful in toString methods and elsewhere.
|
||||
#define UNISTR_FROM_STRING_EXPLICIT
|
||||
|
||||
#include <cstdlib>
|
||||
#include "cstring.h"
|
||||
#include "measunit_impl.h"
|
||||
#include "uarrsort.h"
|
||||
@ -34,17 +35,32 @@ namespace {
|
||||
// TODO: Propose a new error code for this?
|
||||
constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
|
||||
// This is to ensure we only insert positive integers into the trie
|
||||
// Trie value offset for SI Prefixes. This is big enough to ensure we only
|
||||
// insert positive integers into the trie.
|
||||
constexpr int32_t kSIPrefixOffset = 64;
|
||||
|
||||
// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
|
||||
constexpr int32_t kCompoundPartOffset = 128;
|
||||
|
||||
enum CompoundPart {
|
||||
// Represents "-per-"
|
||||
COMPOUND_PART_PER = kCompoundPartOffset,
|
||||
// Represents "-"
|
||||
COMPOUND_PART_TIMES,
|
||||
COMPOUND_PART_PLUS,
|
||||
// Represents "-and-"
|
||||
COMPOUND_PART_AND,
|
||||
};
|
||||
|
||||
// Trie value offset for "per-".
|
||||
constexpr int32_t kInitialCompoundPartOffset = 192;
|
||||
|
||||
enum InitialCompoundPart {
|
||||
// Represents "per-", the only compound part that can appear at the start of
|
||||
// an identifier.
|
||||
INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset,
|
||||
};
|
||||
|
||||
// Trie value offset for powers like "square-", "cubic-", "p2-" etc.
|
||||
constexpr int32_t kPowerPartOffset = 256;
|
||||
|
||||
enum PowerPart {
|
||||
@ -64,6 +80,8 @@ enum PowerPart {
|
||||
POWER_PART_P15,
|
||||
};
|
||||
|
||||
// Trie value offset for simple units, e.g. "gram", "nautical-mile",
|
||||
// "fluid-ounce-imperial".
|
||||
constexpr int32_t kSimpleUnitOffset = 512;
|
||||
|
||||
const struct SIPrefixStrings {
|
||||
@ -94,7 +112,6 @@ const struct SIPrefixStrings {
|
||||
|
||||
// TODO(ICU-21059): Get this list from data
|
||||
const char16_t* const gSimpleUnits[] = {
|
||||
u"one", // note: expected to be index 0
|
||||
u"candela",
|
||||
u"carat",
|
||||
u"gram",
|
||||
@ -226,7 +243,8 @@ void U_CALLCONV initUnitExtras(UErrorCode& status) {
|
||||
// Add syntax parts (compound, power prefixes)
|
||||
b.add(u"-per-", COMPOUND_PART_PER, status);
|
||||
b.add(u"-", COMPOUND_PART_TIMES, status);
|
||||
b.add(u"-and-", COMPOUND_PART_PLUS, status);
|
||||
b.add(u"-and-", COMPOUND_PART_AND, status);
|
||||
b.add(u"per-", INITIAL_COMPOUND_PART_PER, status);
|
||||
b.add(u"square-", POWER_PART_P2, status);
|
||||
b.add(u"cubic-", POWER_PART_P3, status);
|
||||
b.add(u"p2-", POWER_PART_P2, status);
|
||||
@ -270,28 +288,30 @@ public:
|
||||
enum Type {
|
||||
TYPE_UNDEFINED,
|
||||
TYPE_SI_PREFIX,
|
||||
// Token type for "-per-", "-", and "-and-".
|
||||
TYPE_COMPOUND_PART,
|
||||
// Token type for "per-".
|
||||
TYPE_INITIAL_COMPOUND_PART,
|
||||
TYPE_POWER_PART,
|
||||
TYPE_ONE,
|
||||
TYPE_SIMPLE_UNIT,
|
||||
};
|
||||
|
||||
// Calling getType() is invalid, resulting in an assertion failure, if Token
|
||||
// value isn't positive.
|
||||
Type getType() const {
|
||||
if (fMatch <= 0) {
|
||||
UPRV_UNREACHABLE;
|
||||
}
|
||||
U_ASSERT(fMatch > 0);
|
||||
if (fMatch < kCompoundPartOffset) {
|
||||
return TYPE_SI_PREFIX;
|
||||
}
|
||||
if (fMatch < kPowerPartOffset) {
|
||||
if (fMatch < kInitialCompoundPartOffset) {
|
||||
return TYPE_COMPOUND_PART;
|
||||
}
|
||||
if (fMatch < kPowerPartOffset) {
|
||||
return TYPE_INITIAL_COMPOUND_PART;
|
||||
}
|
||||
if (fMatch < kSimpleUnitOffset) {
|
||||
return TYPE_POWER_PART;
|
||||
}
|
||||
if (fMatch == kSimpleUnitOffset) {
|
||||
return TYPE_ONE;
|
||||
}
|
||||
return TYPE_SIMPLE_UNIT;
|
||||
}
|
||||
|
||||
@ -300,11 +320,22 @@ public:
|
||||
return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
|
||||
}
|
||||
|
||||
// Valid only for tokens with type TYPE_COMPOUND_PART.
|
||||
int32_t getMatch() const {
|
||||
U_ASSERT(getType() == TYPE_COMPOUND_PART);
|
||||
return fMatch;
|
||||
}
|
||||
|
||||
int32_t getInitialCompoundPart() const {
|
||||
// Even if there is only one InitialCompoundPart value, we have this
|
||||
// function for the simplicity of code consistency.
|
||||
U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART);
|
||||
// Defensive: if this assert fails, code using this function also needs
|
||||
// to change.
|
||||
U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER);
|
||||
return fMatch;
|
||||
}
|
||||
|
||||
int8_t getPower() const {
|
||||
U_ASSERT(getType() == TYPE_POWER_PART);
|
||||
return static_cast<int8_t>(fMatch - kPowerPartOffset);
|
||||
@ -321,6 +352,14 @@ private:
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
/**
|
||||
* Factory function for parsing the given identifier.
|
||||
*
|
||||
* @param source The identifier to parse. This function does not make a copy
|
||||
* of source: the underlying string that source points at, must outlive the
|
||||
* parser.
|
||||
* @param status ICU error code.
|
||||
*/
|
||||
static Parser from(StringPiece source, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return Parser();
|
||||
@ -339,10 +378,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Tracks parser progress: the offset into fSource.
|
||||
int32_t fIndex = 0;
|
||||
|
||||
// Since we're not owning this memory, whatever is passed to the constructor
|
||||
// should live longer than this Parser - and the parser shouldn't return any
|
||||
// references to that string.
|
||||
StringPiece fSource;
|
||||
UCharsTrie fTrie;
|
||||
|
||||
// Set to true when we've seen a "-per-" or a "per-", after which all units
|
||||
// are in the denominator. Until we find an "-and-", at which point the
|
||||
// identifier is invalid pending TODO(CLDR-13700).
|
||||
bool fAfterPer = false;
|
||||
|
||||
Parser() : fSource(""), fTrie(u"") {}
|
||||
@ -354,11 +401,17 @@ private:
|
||||
return fIndex < fSource.length();
|
||||
}
|
||||
|
||||
// Returns the next Token parsed from fSource, advancing fIndex to the end
|
||||
// of that token in fSource. In case of U_FAILURE(status), the token
|
||||
// returned will cause an abort if getType() is called on it.
|
||||
Token nextToken(UErrorCode& status) {
|
||||
fTrie.reset();
|
||||
int32_t match = -1;
|
||||
// Saves the position in the fSource string for the end of the most
|
||||
// recent matching token.
|
||||
int32_t previ = -1;
|
||||
do {
|
||||
// Find the longest token that matches a value in the trie:
|
||||
while (fIndex < fSource.length()) {
|
||||
auto result = fTrie.next(fSource.data()[fIndex++]);
|
||||
if (result == USTRINGTRIE_NO_MATCH) {
|
||||
break;
|
||||
@ -373,7 +426,7 @@ private:
|
||||
}
|
||||
U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE);
|
||||
// continue;
|
||||
} while (fIndex < fSource.length());
|
||||
}
|
||||
|
||||
if (match < 0) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
@ -383,62 +436,88 @@ private:
|
||||
return Token(match);
|
||||
}
|
||||
|
||||
void nextSingleUnit(SingleUnitImpl& result, bool& sawPlus, UErrorCode& status) {
|
||||
sawPlus = false;
|
||||
/**
|
||||
* Returns the next "single unit" via result.
|
||||
*
|
||||
* If a "-per-" was parsed, the result will have appropriate negative
|
||||
* dimensionality.
|
||||
*
|
||||
* Returns an error if we parse both compound units and "-and-", since mixed
|
||||
* compound units are not yet supported - TODO(CLDR-13700).
|
||||
*
|
||||
* @param result Will be overwritten by the result, if status shows success.
|
||||
* @param sawAnd If an "-and-" was parsed prior to finding the "single
|
||||
* unit", sawAnd is set to true. If not, it is left as is.
|
||||
* @param status ICU error code.
|
||||
*/
|
||||
void nextSingleUnit(SingleUnitImpl& result, bool& sawAnd, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasNext()) {
|
||||
// probably "one"
|
||||
return;
|
||||
}
|
||||
|
||||
// state:
|
||||
// 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
|
||||
// 1 = power token seen (will not accept another power token)
|
||||
// 2 = SI prefix token seen (will not accept a power or SI prefix token)
|
||||
int32_t state = 0;
|
||||
int32_t previ = fIndex;
|
||||
|
||||
// Maybe read a compound part
|
||||
if (fIndex != 0) {
|
||||
Token token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
bool atStart = fIndex == 0;
|
||||
Token token = nextToken(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
if (atStart) {
|
||||
// Identifiers optionally start with "per-".
|
||||
if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
|
||||
U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART_PER);
|
||||
fAfterPer = true;
|
||||
result.dimensionality = -1;
|
||||
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
}
|
||||
} else {
|
||||
// All other SingleUnit's are separated from previous SingleUnit's
|
||||
// via a compound part:
|
||||
if (token.getType() != Token::TYPE_COMPOUND_PART) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (token.getMatch()) {
|
||||
case COMPOUND_PART_PER:
|
||||
if (fAfterPer) {
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
fAfterPer = true;
|
||||
case COMPOUND_PART_PER:
|
||||
if (sawAnd) {
|
||||
// Mixed compound units not yet supported,
|
||||
// TODO(CLDR-13700).
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
fAfterPer = true;
|
||||
result.dimensionality = -1;
|
||||
break;
|
||||
|
||||
case COMPOUND_PART_TIMES:
|
||||
if (fAfterPer) {
|
||||
result.dimensionality = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPOUND_PART_TIMES:
|
||||
break;
|
||||
|
||||
case COMPOUND_PART_PLUS:
|
||||
sawPlus = true;
|
||||
fAfterPer = false;
|
||||
break;
|
||||
case COMPOUND_PART_AND:
|
||||
if (fAfterPer) {
|
||||
// Can't start with "-and-", and mixed compound units
|
||||
// not yet supported, TODO(CLDR-13700).
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
sawAnd = true;
|
||||
break;
|
||||
}
|
||||
previ = fIndex;
|
||||
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
}
|
||||
|
||||
// Read a unit
|
||||
while (hasNext()) {
|
||||
Token token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read tokens until we have a complete SingleUnit or we reach the end.
|
||||
while (true) {
|
||||
switch (token.getType()) {
|
||||
case Token::TYPE_POWER_PART:
|
||||
if (state > 0) {
|
||||
@ -446,7 +525,6 @@ private:
|
||||
return;
|
||||
}
|
||||
result.dimensionality *= token.getPower();
|
||||
previ = fIndex;
|
||||
state = 1;
|
||||
break;
|
||||
|
||||
@ -456,54 +534,62 @@ private:
|
||||
return;
|
||||
}
|
||||
result.siPrefix = token.getSIPrefix();
|
||||
previ = fIndex;
|
||||
state = 2;
|
||||
break;
|
||||
|
||||
case Token::TYPE_ONE:
|
||||
// Skip "one" and go to the next unit
|
||||
return nextSingleUnit(result, sawPlus, status);
|
||||
|
||||
case Token::TYPE_SIMPLE_UNIT:
|
||||
result.index = token.getSimpleUnitIndex();
|
||||
result.identifier = fSource.substr(previ, fIndex - previ);
|
||||
return;
|
||||
|
||||
default:
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We ran out of tokens before finding a complete single unit.
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
if (!hasNext()) {
|
||||
// We ran out of tokens before finding a complete single unit.
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
token = nextToken(status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @param result is modified, not overridden. Caller must pass in a
|
||||
/// default-constructed (empty) MeasureUnitImpl instance.
|
||||
void parseImpl(MeasureUnitImpl& result, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (fSource.empty()) {
|
||||
// The dimenionless unit: nothing to parse. leave result as is.
|
||||
return;
|
||||
}
|
||||
int32_t unitNum = 0;
|
||||
while (hasNext()) {
|
||||
bool sawPlus;
|
||||
bool sawAnd = false;
|
||||
SingleUnitImpl singleUnit;
|
||||
nextSingleUnit(singleUnit, sawPlus, status);
|
||||
nextSingleUnit(singleUnit, sawAnd, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
if (singleUnit.index == 0) {
|
||||
continue;
|
||||
}
|
||||
U_ASSERT(!singleUnit.isDimensionless());
|
||||
bool added = result.append(singleUnit, status);
|
||||
if (sawPlus && !added) {
|
||||
if (sawAnd && !added) {
|
||||
// Two similar units are not allowed in a mixed unit
|
||||
status = kUnitIdentifierSyntaxError;
|
||||
return;
|
||||
}
|
||||
if ((++unitNum) >= 2) {
|
||||
UMeasureUnitComplexity complexity = sawPlus
|
||||
? UMEASURE_UNIT_MIXED
|
||||
: UMEASURE_UNIT_COMPOUND;
|
||||
// nextSingleUnit fails appropriately for "per" and "and" in the
|
||||
// same identifier. It doesn't fail for other compound units
|
||||
// (COMPOUND_PART_TIMES). Consequently we take care of that
|
||||
// here.
|
||||
UMeasureUnitComplexity complexity =
|
||||
sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND;
|
||||
if (unitNum == 2) {
|
||||
U_ASSERT(result.complexity == UMEASURE_UNIT_SINGLE);
|
||||
result.complexity = complexity;
|
||||
@ -526,15 +612,22 @@ compareSingleUnits(const void* /*context*/, const void* left, const void* right)
|
||||
|
||||
/**
|
||||
* Generate the identifier string for a single unit in place.
|
||||
*
|
||||
* Does not support the dimensionless SingleUnitImpl: calling serializeSingle
|
||||
* with the dimensionless unit results in an U_INTERNAL_PROGRAM_ERROR.
|
||||
*
|
||||
* @param first If singleUnit is part of a compound unit, and not its first
|
||||
* single unit, set this to false. Otherwise: set to true.
|
||||
*/
|
||||
void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& output, UErrorCode& status) {
|
||||
if (first && singleUnit.dimensionality < 0) {
|
||||
output.append("one-per-", status);
|
||||
// Essentially the "unary per". For compound units with a numerator, the
|
||||
// caller takes care of the "binary per".
|
||||
output.append("per-", status);
|
||||
}
|
||||
|
||||
if (singleUnit.index == 0) {
|
||||
// Don't propagate SI prefixes and powers on one
|
||||
output.append("one", status);
|
||||
if (singleUnit.isDimensionless()) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
int8_t posPower = std::abs(singleUnit.dimensionality);
|
||||
@ -573,7 +666,7 @@ void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& o
|
||||
return;
|
||||
}
|
||||
|
||||
output.append(singleUnit.identifier, status);
|
||||
output.appendInvariantChars(gSimpleUnits[singleUnit.index], status);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -585,7 +678,8 @@ void serialize(MeasureUnitImpl& impl, UErrorCode& status) {
|
||||
}
|
||||
U_ASSERT(impl.identifier.isEmpty());
|
||||
if (impl.units.length() == 0) {
|
||||
impl.identifier.append("one", status);
|
||||
// Dimensionless, constructed by the default constructor: no appending
|
||||
// to impl.identifier, we wish it to contain the zero-length string.
|
||||
return;
|
||||
}
|
||||
if (impl.complexity == UMEASURE_UNIT_COMPOUND) {
|
||||
@ -624,8 +718,17 @@ void serialize(MeasureUnitImpl& impl, UErrorCode& status) {
|
||||
|
||||
}
|
||||
|
||||
/** @return true if a new item was added */
|
||||
/**
|
||||
* Appends a SingleUnitImpl to a MeasureUnitImpl.
|
||||
*
|
||||
* @return true if a new item was added. If unit is the dimensionless unit, it
|
||||
* is never added: the return value will always be false.
|
||||
*/
|
||||
bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& status) {
|
||||
if (unit.isDimensionless()) {
|
||||
// We don't append dimensionless units.
|
||||
return false;
|
||||
}
|
||||
// Find a similar unit that already exists, to attempt to coalesce
|
||||
SingleUnitImpl* oldUnit = nullptr;
|
||||
for (int32_t i = 0; i < impl.units.length(); i++) {
|
||||
@ -635,6 +738,8 @@ bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& s
|
||||
}
|
||||
}
|
||||
if (oldUnit) {
|
||||
// Both dimensionalities will be positive, or both will be negative, by
|
||||
// virtue of isCompatibleWith().
|
||||
oldUnit->dimensionality += unit.dimensionality;
|
||||
} else {
|
||||
SingleUnitImpl* destination = impl.units.emplaceBack();
|
||||
@ -734,7 +839,12 @@ MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& statu
|
||||
}
|
||||
|
||||
int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
|
||||
return SingleUnitImpl::forMeasureUnit(*this, status).dimensionality;
|
||||
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
|
||||
if (U_FAILURE(status)) { return 0; }
|
||||
if (singleUnit.isDimensionless()) {
|
||||
return 0;
|
||||
}
|
||||
return singleUnit.dimensionality;
|
||||
}
|
||||
|
||||
MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const {
|
||||
|
@ -25,14 +25,25 @@ static const char kDefaultCurrency8[] = "XXX";
|
||||
struct SingleUnitImpl : public UMemory {
|
||||
/**
|
||||
* Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
|
||||
* code and return the base dimensionless unit. Parses if necessary.
|
||||
* code and returns the base dimensionless unit. Parses if necessary.
|
||||
*/
|
||||
static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
|
||||
|
||||
/** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
|
||||
MeasureUnit build(UErrorCode& status) const;
|
||||
|
||||
/** Compare this SingleUnitImpl to another SingleUnitImpl. */
|
||||
/**
|
||||
* Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
|
||||
* sorting and coalescing.
|
||||
*
|
||||
* Takes the sign of dimensionality into account, but not the absolute
|
||||
* value: per-meter is not considered the same as meter, but meter is
|
||||
* considered the same as square-meter.
|
||||
*
|
||||
* The dimensionless unit generally does not get compared, but if it did, it
|
||||
* would sort before other units by virtue of index being < 0 and
|
||||
* dimensionality not being negative.
|
||||
*/
|
||||
int32_t compareTo(const SingleUnitImpl& other) const {
|
||||
if (dimensionality < 0 && other.dimensionality > 0) {
|
||||
// Positive dimensions first
|
||||
@ -66,16 +77,36 @@ struct SingleUnitImpl : public UMemory {
|
||||
return (compareTo(other) == 0);
|
||||
}
|
||||
|
||||
/** Simple unit index, unique for every simple unit. */
|
||||
int32_t index = 0;
|
||||
/**
|
||||
* Returns true if this unit is the "dimensionless base unit", as produced
|
||||
* by the MeasureUnit() default constructor. (This does not include the
|
||||
* likes of concentrations or angles.)
|
||||
*/
|
||||
bool isDimensionless() const {
|
||||
return index == -1;
|
||||
}
|
||||
|
||||
/** Simple unit identifier; memory not owned by the SimpleUnit. */
|
||||
StringPiece identifier;
|
||||
/**
|
||||
* Simple unit index, unique for every simple unit, -1 for the dimensionless
|
||||
* unit. This is an index into a string list in measunit_extra.cpp.
|
||||
*
|
||||
* The default value is -1, meaning the dimensionless unit:
|
||||
* isDimensionless() will return true, until index is changed.
|
||||
*/
|
||||
int32_t index = -1;
|
||||
|
||||
/** SI prefix. **/
|
||||
/**
|
||||
* SI prefix.
|
||||
*
|
||||
* This is ignored for the dimensionless unit.
|
||||
*/
|
||||
UMeasureSIPrefix siPrefix = UMEASURE_SI_PREFIX_ONE;
|
||||
|
||||
/** Dimensionality. **/
|
||||
|
||||
/**
|
||||
* Dimensionality.
|
||||
*
|
||||
* This is meaningless for the dimensionless unit.
|
||||
*/
|
||||
int32_t dimensionality = 1;
|
||||
};
|
||||
|
||||
@ -95,7 +126,8 @@ struct MeasureUnitImpl : public UMemory {
|
||||
*
|
||||
* @param identifier The unit identifier string.
|
||||
* @param status Set if the identifier string is not valid.
|
||||
* @return A newly parsed value object.
|
||||
* @return A newly parsed value object. Behaviour of this unit is
|
||||
* unspecified if an error is returned via status.
|
||||
*/
|
||||
static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
|
||||
|
||||
@ -148,15 +180,23 @@ struct MeasureUnitImpl : public UMemory {
|
||||
/** Mutates this MeasureUnitImpl to take the reciprocal. */
|
||||
void takeReciprocal(UErrorCode& status);
|
||||
|
||||
/** Mutates this MeasureUnitImpl to append a single unit. */
|
||||
/**
|
||||
* Mutates this MeasureUnitImpl to append a single unit.
|
||||
*
|
||||
* @return true if a new item was added. If unit is the dimensionless unit,
|
||||
* it is never added: the return value will always be false.
|
||||
*/
|
||||
bool append(const SingleUnitImpl& singleUnit, UErrorCode& status);
|
||||
|
||||
/** The complexity, either SINGLE, COMPOUND, or MIXED. */
|
||||
UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
|
||||
|
||||
/**
|
||||
* The list of simple units. These may be summed or multiplied, based on the value of the
|
||||
* complexity field.
|
||||
* The list of simple units. These may be summed or multiplied, based on the
|
||||
* value of the complexity field.
|
||||
*
|
||||
* The "dimensionless" unit (SingleUnitImpl default constructor) must not be
|
||||
* added to this list.
|
||||
*/
|
||||
MaybeStackVector<SingleUnitImpl> units;
|
||||
|
||||
|
@ -11,7 +11,7 @@ U_NAMESPACE_BEGIN
|
||||
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(NoUnit)
|
||||
|
||||
NoUnit U_EXPORT2 NoUnit::base() {
|
||||
return NoUnit("one");
|
||||
return NoUnit("");
|
||||
}
|
||||
|
||||
NoUnit U_EXPORT2 NoUnit::percent() {
|
||||
|
@ -203,6 +203,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
|
||||
}
|
||||
pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto patternInfo = new ParsedPatternInfo();
|
||||
if (patternInfo == nullptr) {
|
||||
@ -211,6 +214,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
}
|
||||
fPatternInfo.adoptInstead(patternInfo);
|
||||
PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
/// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
|
||||
@ -241,6 +247,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
roundingMode = precision.fRoundingMode;
|
||||
}
|
||||
fMicros.rounder = {precision, roundingMode, currency, status};
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Grouping strategy
|
||||
if (!macros.grouper.isBogus()) {
|
||||
@ -323,6 +332,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
if (safe) {
|
||||
fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Outer modifier (CLDR units and currency long names)
|
||||
if (isCldrUnit) {
|
||||
@ -349,6 +361,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
// No outer modifier required
|
||||
fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Compact notation
|
||||
if (macros.notation.fType == Notation::NTN_COMPACT) {
|
||||
@ -371,6 +386,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
||||
fCompactHandler.adoptInstead(newCompactHandler);
|
||||
chain = fCompactHandler.getAlias();
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Always add the pattern modifier as the last element of the chain.
|
||||
if (safe) {
|
||||
|
@ -246,7 +246,8 @@ LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit, con
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
SimpleFormatter secondaryCompiled(secondaryFormat, 1, 1, status);
|
||||
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
|
||||
SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
|
||||
// TODO: Why does UnicodeString need to be explicit in the following line?
|
||||
|
@ -483,6 +483,8 @@ public:
|
||||
*/
|
||||
const UnicodeString& getDecimal() const;
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Get the default hour cycle for a locale. Uses the locale that the
|
||||
@ -499,6 +501,8 @@ public:
|
||||
UDateFormatHourCycle getDefaultHourCycle(UErrorCode& status) const;
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
/**
|
||||
* ICU "poor man's RTTI", returns a UClassID for the actual class.
|
||||
*
|
||||
|
@ -186,6 +186,7 @@ class U_I18N_API ListFormatter : public UObject{
|
||||
static ListFormatter* createInstance(const Locale& locale, UErrorCode& errorCode);
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
/**
|
||||
* Creates a ListFormatter for the given locale, list type, and style.
|
||||
*
|
||||
@ -198,8 +199,9 @@ class U_I18N_API ListFormatter : public UObject{
|
||||
*/
|
||||
static ListFormatter* createInstance(
|
||||
const Locale& locale, UListFormatterType type, UListFormatterWidth width, UErrorCode& errorCode);
|
||||
#endif /* !UCONFIG_NO_FORMATTING */
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Creates a ListFormatter appropriate for a locale and style.
|
||||
|
@ -37,7 +37,7 @@ struct MeasureUnitImpl;
|
||||
* Enumeration for unit complexity. There are three levels:
|
||||
*
|
||||
* - SINGLE: A single unit, optionally with a power and/or SI prefix. Examples: hectare,
|
||||
* square-kilometer, kilojoule, one-per-second.
|
||||
* square-kilometer, kilojoule, per-second.
|
||||
* - COMPOUND: A unit composed of the product of multiple single units. Examples:
|
||||
* meter-per-second, kilowatt-hour, kilogram-meter-per-square-second.
|
||||
* - MIXED: A unit composed of the sum of multiple single units. Examples: foot+inch,
|
||||
@ -387,6 +387,8 @@ class U_I18N_API MeasureUnit: public UObject {
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see UMeasureUnitComplexity.
|
||||
*
|
||||
* For the base dimensionless unit, withDimensionality does nothing.
|
||||
*
|
||||
* @param dimensionality The dimensionality (power).
|
||||
* @param status Set if this is not a SINGLE unit or if another error occurs.
|
||||
* @return A new SINGLE unit.
|
||||
@ -401,6 +403,8 @@ class U_I18N_API MeasureUnit: public UObject {
|
||||
* NOTE: Only works on SINGLE units. If this is a COMPOUND or MIXED unit, an error will
|
||||
* occur. For more information, see UMeasureUnitComplexity.
|
||||
*
|
||||
* For the base dimensionless unit, getDimensionality returns 0.
|
||||
*
|
||||
* @param status Set if this is not a SINGLE unit or if another error occurs.
|
||||
* @return The dimensionality (power) of this simple unit.
|
||||
* @draft ICU 67
|
||||
@ -447,7 +451,7 @@ class U_I18N_API MeasureUnit: public UObject {
|
||||
*
|
||||
* Examples:
|
||||
* - Given "meter-kilogram-per-second", three units will be returned: "meter",
|
||||
* "kilogram", and "one-per-second".
|
||||
* "kilogram", and "per-second".
|
||||
* - Given "hour+minute+second", three units will be returned: "hour", "minute",
|
||||
* and "second".
|
||||
*
|
||||
@ -3375,11 +3379,15 @@ class U_I18N_API MeasureUnit: public UObject {
|
||||
|
||||
private:
|
||||
|
||||
// If non-null, fImpl is owned by the MeasureUnit.
|
||||
// Used by new draft APIs in ICU 67. If non-null, fImpl is owned by the
|
||||
// MeasureUnit.
|
||||
MeasureUnitImpl* fImpl;
|
||||
|
||||
// These two ints are indices into static string lists in measunit.cpp
|
||||
// An index into a static string list in measunit.cpp. If set to -1, fImpl
|
||||
// is in use instead of fTypeId and fSubTypeId.
|
||||
int16_t fSubTypeId;
|
||||
// An index into a static string list in measunit.cpp. If set to -1, fImpl
|
||||
// is in use instead of fTypeId and fSubTypeId.
|
||||
int8_t fTypeId;
|
||||
|
||||
MeasureUnit(int32_t typeId, int32_t subTypeId);
|
||||
@ -3389,7 +3397,11 @@ private:
|
||||
static MeasureUnit *create(int typeId, int subTypeId, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* @return Whether subType is known to ICU.
|
||||
* Sets output's typeId and subTypeId according to subType, if subType is a
|
||||
* valid/known identifier.
|
||||
*
|
||||
* @return Whether subType is known to ICU. If false, output was not
|
||||
* modified.
|
||||
*/
|
||||
static bool findBySubType(StringPiece subType, MeasureUnit* output);
|
||||
|
||||
|
@ -652,6 +652,8 @@ udatpg_getPatternForSkeleton(const UDateTimePatternGenerator *dtpg,
|
||||
const UChar *skeleton, int32_t skeletonLength,
|
||||
int32_t *pLength);
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Return the default hour cycle for a locale. Uses the locale that the
|
||||
@ -670,4 +672,6 @@ U_DRAFT UDateFormatHourCycle U_EXPORT2
|
||||
udatpg_getDefaultHourCycle(const UDateTimePatternGenerator *dtpg, UErrorCode* pErrorCode);
|
||||
#endif /* U_HIDE_DRAFT_API */
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
||||
#endif
|
||||
|
@ -217,7 +217,7 @@ class LocaleFilter(Filter):
|
||||
return "root"
|
||||
i = locale.rfind("_")
|
||||
if i < 0:
|
||||
assert locale == "root"
|
||||
assert locale == "root", "Invalid locale: %s/%s" % (tree, locale)
|
||||
return None
|
||||
return locale[:i]
|
||||
|
||||
|
@ -9,9 +9,11 @@
|
||||
// Helpful in toString methods and elsewhere.
|
||||
#define UNISTR_FROM_STRING_EXPLICIT
|
||||
|
||||
#include <stdio.h>
|
||||
#include "unicode/unumberformatter.h"
|
||||
#include "unicode/umisc.h"
|
||||
#include "unicode/unum.h"
|
||||
#include "unicode/ustring.h"
|
||||
#include "cformtst.h"
|
||||
#include "cintltst.h"
|
||||
#include "cmemory.h"
|
||||
@ -26,6 +28,8 @@ static void TestFormattedValue(void);
|
||||
|
||||
static void TestSkeletonParseError(void);
|
||||
|
||||
static void TestPerUnitInArabic(void);
|
||||
|
||||
void addUNumberFormatterTest(TestNode** root);
|
||||
|
||||
#define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
|
||||
@ -36,6 +40,7 @@ void addUNumberFormatterTest(TestNode** root) {
|
||||
TESTCASE(TestExampleCode);
|
||||
TESTCASE(TestFormattedValue);
|
||||
TESTCASE(TestSkeletonParseError);
|
||||
TESTCASE(TestPerUnitInArabic);
|
||||
}
|
||||
|
||||
|
||||
@ -254,5 +259,88 @@ static void TestSkeletonParseError() {
|
||||
unumf_close(uformatter);
|
||||
}
|
||||
|
||||
|
||||
static void TestPerUnitInArabic() {
|
||||
const char* simpleMeasureUnits[] = {
|
||||
"area-acre",
|
||||
"digital-bit",
|
||||
"digital-byte",
|
||||
"temperature-celsius",
|
||||
"length-centimeter",
|
||||
"duration-day",
|
||||
"angle-degree",
|
||||
"temperature-fahrenheit",
|
||||
"volume-fluid-ounce",
|
||||
"length-foot",
|
||||
"volume-gallon",
|
||||
"digital-gigabit",
|
||||
"digital-gigabyte",
|
||||
"mass-gram",
|
||||
"area-hectare",
|
||||
"duration-hour",
|
||||
"length-inch",
|
||||
"digital-kilobit",
|
||||
"digital-kilobyte",
|
||||
"mass-kilogram",
|
||||
"length-kilometer",
|
||||
"volume-liter",
|
||||
"digital-megabit",
|
||||
"digital-megabyte",
|
||||
"length-meter",
|
||||
"length-mile",
|
||||
"length-mile-scandinavian",
|
||||
"volume-milliliter",
|
||||
"length-millimeter",
|
||||
"duration-millisecond",
|
||||
"duration-minute",
|
||||
"duration-month",
|
||||
"mass-ounce",
|
||||
"concentr-percent",
|
||||
"digital-petabyte",
|
||||
"mass-pound",
|
||||
"duration-second",
|
||||
"mass-stone",
|
||||
"digital-terabit",
|
||||
"digital-terabyte",
|
||||
"duration-week",
|
||||
"length-yard",
|
||||
"duration-year"
|
||||
};
|
||||
#define BUFFER_LEN 256
|
||||
char buffer[BUFFER_LEN];
|
||||
UChar ubuffer[BUFFER_LEN];
|
||||
const char* locale = "ar";
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UFormattedNumber* formatted = unumf_openResult(&status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_err("FAIL: unumf_openResult failed");
|
||||
return;
|
||||
}
|
||||
for(int32_t i=0; i < UPRV_LENGTHOF(simpleMeasureUnits); ++i) {
|
||||
for(int32_t j=0; j < UPRV_LENGTHOF(simpleMeasureUnits); ++j) {
|
||||
status = U_ZERO_ERROR;
|
||||
sprintf(buffer, "measure-unit/%s per-measure-unit/%s",
|
||||
simpleMeasureUnits[i], simpleMeasureUnits[j]);
|
||||
int32_t outputlen = 0;
|
||||
u_strFromUTF8(ubuffer, BUFFER_LEN, &outputlen, buffer, strlen(buffer), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_err("FAIL u_strFromUTF8: %s = %s ( %s )\n", locale, buffer,
|
||||
u_errorName(status));
|
||||
}
|
||||
UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
|
||||
ubuffer, outputlen, locale, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_err("FAIL unumf_openForSkeletonAndLocale: %s = %s ( %s )\n",
|
||||
locale, buffer, u_errorName(status));
|
||||
} else {
|
||||
unumf_formatDouble(nf, 1, formatted, &status);
|
||||
if (U_FAILURE(status)) {
|
||||
log_err("FAIL unumf_formatDouble: %s = %s ( %s )\n",
|
||||
locale, buffer, u_errorName(status));
|
||||
}
|
||||
}
|
||||
unumf_close(nf);
|
||||
}
|
||||
}
|
||||
unumf_closeResult(formatted);
|
||||
}
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
@ -1943,6 +1943,9 @@ void DateIntervalFormatTest::testTicket20707() {
|
||||
int32_t j = 0;
|
||||
for (const UnicodeString skeleton : {u"hh", u"HH", u"kk", u"KK", u"jj", u"JJs", u"CC"}) {
|
||||
LocalPointer<DateIntervalFormat> dtifmt(DateIntervalFormat::createInstance(skeleton, locale, status));
|
||||
if (status.errDataIfFailureAndReset()) {
|
||||
continue;
|
||||
}
|
||||
FieldPosition fposition;
|
||||
UnicodeString result;
|
||||
LocalPointer<Calendar> calendar(Calendar::createInstance(TimeZone::createTimeZone(timeZone), status));
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/localematcher.h"
|
||||
@ -333,7 +334,9 @@ void LocaleMatcherTest::testDirection() {
|
||||
{
|
||||
// arz is a close one-way match to ar, and the region matches.
|
||||
// (Egyptian Arabic vs. Arabic)
|
||||
LocaleMatcher withOneWay = builder.build(errorCode);
|
||||
// Also explicitly exercise the move copy constructor.
|
||||
LocaleMatcher built = builder.build(errorCode);
|
||||
LocaleMatcher withOneWay(std::move(built));
|
||||
Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
|
||||
assertEquals("with one-way", "ar",
|
||||
locString(withOneWay.getBestMatch(desiredIter, errorCode)));
|
||||
@ -341,8 +344,11 @@ void LocaleMatcherTest::testDirection() {
|
||||
{
|
||||
// nb is a less close two-way match to nn, and the regions differ.
|
||||
// (Norwegian Bokmal vs. Nynorsk)
|
||||
LocaleMatcher onlyTwoWay =
|
||||
// Also explicitly exercise the move assignment operator.
|
||||
LocaleMatcher onlyTwoWay = builder.build(errorCode);
|
||||
LocaleMatcher built =
|
||||
builder.setDirection(ULOCMATCH_DIRECTION_ONLY_TWO_WAY).build(errorCode);
|
||||
onlyTwoWay = std::move(built);
|
||||
Locale::RangeIterator<Locale *> desiredIter(ARRAY_RANGE(desired));
|
||||
assertEquals("only two-way", "nn",
|
||||
locString(onlyTwoWay.getBestMatch(desiredIter, errorCode)));
|
||||
|
@ -79,9 +79,11 @@ private:
|
||||
void Test20332_PersonUnits();
|
||||
void TestNumericTime();
|
||||
void TestNumericTimeSomeSpecialFormats();
|
||||
void TestIdentifiers();
|
||||
void TestInvalidIdentifiers();
|
||||
void TestCompoundUnitOperations();
|
||||
void TestIdentifiers();
|
||||
void TestDimensionlessBehaviour();
|
||||
void Test21060_AddressSanitizerProblem();
|
||||
|
||||
void verifyFormat(
|
||||
const char *description,
|
||||
@ -201,9 +203,11 @@ void MeasureFormatTest::runIndexedTest(
|
||||
TESTCASE_AUTO(Test20332_PersonUnits);
|
||||
TESTCASE_AUTO(TestNumericTime);
|
||||
TESTCASE_AUTO(TestNumericTimeSomeSpecialFormats);
|
||||
TESTCASE_AUTO(TestIdentifiers);
|
||||
TESTCASE_AUTO(TestInvalidIdentifiers);
|
||||
TESTCASE_AUTO(TestCompoundUnitOperations);
|
||||
TESTCASE_AUTO(TestIdentifiers);
|
||||
TESTCASE_AUTO(TestDimensionlessBehaviour);
|
||||
TESTCASE_AUTO(Test21060_AddressSanitizerProblem);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
@ -3237,10 +3241,43 @@ void MeasureFormatTest::TestNumericTimeSomeSpecialFormats() {
|
||||
verifyFormat("Danish fhoursFminutes", fmtDa, fhoursFminutes, 2, "2.03,877");
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestIdentifiers() {
|
||||
IcuTestErrorCode status(*this, "TestIdentifiers");
|
||||
struct TestCase {
|
||||
const char* id;
|
||||
const char* normalized;
|
||||
} cases[] = {
|
||||
// Correctly normalized identifiers should not change
|
||||
{"", ""},
|
||||
{"square-meter-per-square-meter", "square-meter-per-square-meter"},
|
||||
{"kilogram-meter-per-square-meter-square-second",
|
||||
"kilogram-meter-per-square-meter-square-second"},
|
||||
{"square-mile-and-square-foot", "square-mile-and-square-foot"},
|
||||
{"square-foot-and-square-mile", "square-foot-and-square-mile"},
|
||||
{"per-cubic-centimeter", "per-cubic-centimeter"},
|
||||
{"per-kilometer", "per-kilometer"},
|
||||
|
||||
// Normalization of power and per
|
||||
{"p2-foot-and-p2-mile", "square-foot-and-square-mile"},
|
||||
{"gram-square-gram-per-dekagram", "cubic-gram-per-dekagram"},
|
||||
{"kilogram-per-meter-per-second", "kilogram-per-meter-second"},
|
||||
|
||||
// TODO(ICU-20920): Add more test cases once the proper ranking is available.
|
||||
};
|
||||
for (const auto &cas : cases) {
|
||||
status.setScope(cas.id);
|
||||
MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
|
||||
status.errIfFailureAndReset();
|
||||
const char* actual = unit.getIdentifier();
|
||||
assertEquals(cas.id, cas.normalized, actual);
|
||||
status.errIfFailureAndReset();
|
||||
}
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestInvalidIdentifiers() {
|
||||
IcuTestErrorCode status(*this, "TestInvalidIdentifiers");
|
||||
|
||||
const char* const inputs[] = {
|
||||
const char *const inputs[] = {
|
||||
"kilo",
|
||||
"kilokilo",
|
||||
"onekilo",
|
||||
@ -3256,7 +3293,23 @@ void MeasureFormatTest::TestInvalidIdentifiers() {
|
||||
"-p2-meter",
|
||||
"+p2-meter",
|
||||
"+",
|
||||
"-"
|
||||
"-",
|
||||
"-mile",
|
||||
"-and-mile",
|
||||
"-per-mile",
|
||||
"one",
|
||||
"one-one",
|
||||
"one-per-mile",
|
||||
"one-per-cubic-centimeter",
|
||||
"square--per-meter",
|
||||
"metersecond", // Must have compound part in between single units
|
||||
|
||||
// Negative powers not supported in mixed units yet. TODO(CLDR-13701).
|
||||
"per-hour-and-hertz",
|
||||
"hertz-and-per-hour",
|
||||
|
||||
// Compound units not supported in mixed units yet. TODO(CLDR-13700).
|
||||
"kilonewton-meter-and-newton-meter",
|
||||
};
|
||||
|
||||
for (const auto& input : inputs) {
|
||||
@ -3293,9 +3346,9 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
||||
MeasureUnit overQuarticKilometer1 = kilometer.withDimensionality(-4, status);
|
||||
|
||||
verifySingleUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
|
||||
verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "one-per-cubic-centimeter");
|
||||
verifySingleUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "per-cubic-centimeter");
|
||||
verifySingleUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
|
||||
|
||||
assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
|
||||
|
||||
@ -3308,9 +3361,9 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
||||
.reciprocal(status)
|
||||
.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
|
||||
verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
|
||||
verifySingleUnit(overQuarticKilometer4, UMEASURE_SI_PREFIX_KILO, -4, "per-p4-kilometer");
|
||||
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
|
||||
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
|
||||
@ -3341,7 +3394,7 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
||||
const char* secondCentimeterSub[] = {"centimeter", "square-kilosecond"};
|
||||
verifyCompoundUnit(secondCentimeter, "centimeter-square-kilosecond",
|
||||
secondCentimeterSub, UPRV_LENGTHOF(secondCentimeterSub));
|
||||
const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "one-per-kilometer"};
|
||||
const char* secondCentimeterPerKilometerSub[] = {"centimeter", "square-kilosecond", "per-kilometer"};
|
||||
verifyCompoundUnit(secondCentimeterPerKilometer, "centimeter-square-kilosecond-per-kilometer",
|
||||
secondCentimeterPerKilometerSub, UPRV_LENGTHOF(secondCentimeterPerKilometerSub));
|
||||
|
||||
@ -3376,31 +3429,16 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
||||
|
||||
assertTrue("order matters inequality", footInch != inchFoot);
|
||||
|
||||
MeasureUnit one1;
|
||||
MeasureUnit one2 = MeasureUnit::forIdentifier("one", status);
|
||||
MeasureUnit one3 = MeasureUnit::forIdentifier("", status);
|
||||
MeasureUnit squareOne = one2.withDimensionality(2, status);
|
||||
MeasureUnit onePerOne = one2.reciprocal(status);
|
||||
MeasureUnit squareKiloOne = squareOne.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
MeasureUnit onePerSquareKiloOne = squareKiloOne.reciprocal(status);
|
||||
MeasureUnit oneOne = MeasureUnit::forIdentifier("one-one", status);
|
||||
MeasureUnit onePlusOne = MeasureUnit::forIdentifier("one-and-one", status);
|
||||
MeasureUnit kilometer2 = one2.product(kilometer, status);
|
||||
MeasureUnit dimensionless;
|
||||
MeasureUnit dimensionless2 = MeasureUnit::forIdentifier("", status);
|
||||
status.errIfFailureAndReset("Dimensionless MeasureUnit.");
|
||||
assertTrue("dimensionless equality", dimensionless == dimensionless2);
|
||||
|
||||
verifySingleUnit(one1, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(one2, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(one3, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(squareOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(onePerOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(squareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(onePerSquareKiloOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(oneOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
verifySingleUnit(onePlusOne, UMEASURE_SI_PREFIX_ONE, 1, "one");
|
||||
// We support starting from an "identity" MeasureUnit and then combining it
|
||||
// with others via product:
|
||||
MeasureUnit kilometer2 = dimensionless.product(kilometer, status);
|
||||
status.errIfFailureAndReset("dimensionless.product(kilometer, status)");
|
||||
verifySingleUnit(kilometer2, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
|
||||
|
||||
assertTrue("one equality", one1 == one2);
|
||||
assertTrue("one equality", one2 == one3);
|
||||
assertTrue("one-per-one equality", onePerOne == onePerSquareKiloOne);
|
||||
assertTrue("kilometer equality", kilometer == kilometer2);
|
||||
|
||||
// Test out-of-range powers
|
||||
@ -3411,36 +3449,102 @@ void MeasureFormatTest::TestCompoundUnitOperations() {
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
MeasureUnit power16b = power15.product(kilometer, status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
MeasureUnit powerN15 = MeasureUnit::forIdentifier("one-per-p15-kilometer", status);
|
||||
verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "one-per-p15-kilometer");
|
||||
MeasureUnit powerN15 = MeasureUnit::forIdentifier("per-p15-kilometer", status);
|
||||
verifySingleUnit(powerN15, UMEASURE_SI_PREFIX_KILO, -15, "per-p15-kilometer");
|
||||
status.errIfFailureAndReset();
|
||||
MeasureUnit powerN16a = MeasureUnit::forIdentifier("one-per-p16-kilometer", status);
|
||||
MeasureUnit powerN16a = MeasureUnit::forIdentifier("per-p16-kilometer", status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
MeasureUnit powerN16b = powerN15.product(overQuarticKilometer1, status);
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
}
|
||||
|
||||
void MeasureFormatTest::TestIdentifiers() {
|
||||
IcuTestErrorCode status(*this, "TestIdentifiers");
|
||||
struct TestCase {
|
||||
bool valid;
|
||||
const char* id;
|
||||
const char* normalized;
|
||||
} cases[] = {
|
||||
{ true, "square-meter-per-square-meter", "square-meter-per-square-meter" },
|
||||
// TODO(ICU-20920): Add more test cases once the proper ranking is available.
|
||||
};
|
||||
for (const auto& cas : cases) {
|
||||
status.setScope(cas.id);
|
||||
MeasureUnit unit = MeasureUnit::forIdentifier(cas.id, status);
|
||||
if (!cas.valid) {
|
||||
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
|
||||
continue;
|
||||
}
|
||||
const char* actual = unit.getIdentifier();
|
||||
assertEquals(cas.id, cas.normalized, actual);
|
||||
status.errIfFailureAndReset();
|
||||
}
|
||||
void MeasureFormatTest::TestDimensionlessBehaviour() {
|
||||
IcuTestErrorCode status(*this, "TestDimensionlessBehaviour");
|
||||
MeasureUnit dimensionless;
|
||||
MeasureUnit modified;
|
||||
|
||||
// At the time of writing, each of the seven groups below caused
|
||||
// Parser::from("") to be called:
|
||||
|
||||
// splitToSingleUnits
|
||||
int32_t count;
|
||||
LocalArray<MeasureUnit> singles = dimensionless.splitToSingleUnits(count, status);
|
||||
status.errIfFailureAndReset("dimensionless.splitToSingleUnits(...)");
|
||||
assertEquals("no singles in dimensionless", 0, count);
|
||||
|
||||
// product(dimensionless)
|
||||
MeasureUnit mile = MeasureUnit::getMile();
|
||||
mile = mile.product(dimensionless, status);
|
||||
status.errIfFailureAndReset("mile.product(dimensionless, ...)");
|
||||
verifySingleUnit(mile, UMEASURE_SI_PREFIX_ONE, 1, "mile");
|
||||
|
||||
// dimensionless.getSIPrefix()
|
||||
UMeasureSIPrefix siPrefix = dimensionless.getSIPrefix(status);
|
||||
status.errIfFailureAndReset("dimensionless.getSIPrefix(...)");
|
||||
assertEquals("dimensionless SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
|
||||
|
||||
// dimensionless.withSIPrefix()
|
||||
modified = dimensionless.withSIPrefix(UMEASURE_SI_PREFIX_KILO, status);
|
||||
status.errIfFailureAndReset("dimensionless.withSIPrefix(...)");
|
||||
singles = modified.splitToSingleUnits(count, status);
|
||||
assertEquals("no singles in modified", 0, count);
|
||||
siPrefix = modified.getSIPrefix(status);
|
||||
status.errIfFailureAndReset("modified.getSIPrefix(...)");
|
||||
assertEquals("modified SIPrefix", UMEASURE_SI_PREFIX_ONE, siPrefix);
|
||||
|
||||
// dimensionless.getComplexity()
|
||||
UMeasureUnitComplexity complexity = dimensionless.getComplexity(status);
|
||||
status.errIfFailureAndReset("dimensionless.getComplexity(...)");
|
||||
assertEquals("dimensionless complexity", UMEASURE_UNIT_SINGLE, complexity);
|
||||
|
||||
// Dimensionality is mostly meaningless for dimensionless units, but it's
|
||||
// still considered a SINGLE unit, so this code doesn't throw errors:
|
||||
|
||||
// dimensionless.getDimensionality()
|
||||
int32_t dimensionality = dimensionless.getDimensionality(status);
|
||||
status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
|
||||
assertEquals("dimensionless dimensionality", 0, dimensionality);
|
||||
|
||||
// dimensionless.withDimensionality()
|
||||
dimensionless.withDimensionality(-1, status);
|
||||
status.errIfFailureAndReset("dimensionless.withDimensionality(...)");
|
||||
dimensionality = dimensionless.getDimensionality(status);
|
||||
status.errIfFailureAndReset("dimensionless.getDimensionality(...)");
|
||||
assertEquals("dimensionless dimensionality", 0, dimensionality);
|
||||
}
|
||||
|
||||
// ICU-21060
|
||||
void MeasureFormatTest::Test21060_AddressSanitizerProblem() {
|
||||
IcuTestErrorCode status(*this, "Test21060_AddressSanitizerProblem");
|
||||
|
||||
MeasureUnit first = MeasureUnit::forIdentifier("", status);
|
||||
status.errIfFailureAndReset();
|
||||
|
||||
// Experimentally, a compound unit like "kilogram-meter" failed. A single
|
||||
// unit like "kilogram" or "meter" did not fail, did not trigger the
|
||||
// problem.
|
||||
MeasureUnit crux = MeasureUnit::forIdentifier("per-meter", status);
|
||||
|
||||
// Heap allocation of a new CharString for first.identifier happens here:
|
||||
first = first.product(crux, status);
|
||||
|
||||
// Constructing second from first's identifier resulted in a failure later,
|
||||
// as second held a reference to a substring of first's identifier:
|
||||
MeasureUnit second = MeasureUnit::forIdentifier(first.getIdentifier(), status);
|
||||
|
||||
// Heap is freed here, as an old first.identifier CharString is deallocated
|
||||
// and a new CharString is allocated:
|
||||
first = first.product(crux, status);
|
||||
|
||||
// Proving we've had no failure yet:
|
||||
status.errIfFailureAndReset();
|
||||
|
||||
// heap-use-after-free failure happened here, since a SingleUnitImpl had
|
||||
// held onto a StringPiece pointing at a substring of an identifier that was
|
||||
// freed above:
|
||||
second = second.product(crux, status);
|
||||
|
||||
status.errIfFailureAndReset();
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,6 +258,7 @@ class NumberSkeletonTest : public IntlTest {
|
||||
void defaultTokens();
|
||||
void flexibleSeparators();
|
||||
void wildcardCharacters();
|
||||
void perUnitInArabic();
|
||||
|
||||
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
|
||||
|
||||
|
@ -30,6 +30,7 @@ void NumberSkeletonTest::runIndexedTest(int32_t index, UBool exec, const char*&
|
||||
TESTCASE_AUTO(defaultTokens);
|
||||
TESTCASE_AUTO(flexibleSeparators);
|
||||
TESTCASE_AUTO(wildcardCharacters);
|
||||
TESTCASE_AUTO(perUnitInArabic);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
@ -362,5 +363,77 @@ void NumberSkeletonTest::expectedErrorSkeleton(const char16_t** cases, int32_t c
|
||||
}
|
||||
}
|
||||
|
||||
void NumberSkeletonTest::perUnitInArabic() {
|
||||
IcuTestErrorCode status(*this, "perUnitInArabic");
|
||||
|
||||
struct TestCase {
|
||||
const char16_t* type;
|
||||
const char16_t* subtype;
|
||||
} cases[] = {
|
||||
{u"area", u"acre"},
|
||||
{u"digital", u"bit"},
|
||||
{u"digital", u"byte"},
|
||||
{u"temperature", u"celsius"},
|
||||
{u"length", u"centimeter"},
|
||||
{u"duration", u"day"},
|
||||
{u"angle", u"degree"},
|
||||
{u"temperature", u"fahrenheit"},
|
||||
{u"volume", u"fluid-ounce"},
|
||||
{u"length", u"foot"},
|
||||
{u"volume", u"gallon"},
|
||||
{u"digital", u"gigabit"},
|
||||
{u"digital", u"gigabyte"},
|
||||
{u"mass", u"gram"},
|
||||
{u"area", u"hectare"},
|
||||
{u"duration", u"hour"},
|
||||
{u"length", u"inch"},
|
||||
{u"digital", u"kilobit"},
|
||||
{u"digital", u"kilobyte"},
|
||||
{u"mass", u"kilogram"},
|
||||
{u"length", u"kilometer"},
|
||||
{u"volume", u"liter"},
|
||||
{u"digital", u"megabit"},
|
||||
{u"digital", u"megabyte"},
|
||||
{u"length", u"meter"},
|
||||
{u"length", u"mile"},
|
||||
{u"length", u"mile-scandinavian"},
|
||||
{u"volume", u"milliliter"},
|
||||
{u"length", u"millimeter"},
|
||||
{u"duration", u"millisecond"},
|
||||
{u"duration", u"minute"},
|
||||
{u"duration", u"month"},
|
||||
{u"mass", u"ounce"},
|
||||
{u"concentr", u"percent"},
|
||||
{u"digital", u"petabyte"},
|
||||
{u"mass", u"pound"},
|
||||
{u"duration", u"second"},
|
||||
{u"mass", u"stone"},
|
||||
{u"digital", u"terabit"},
|
||||
{u"digital", u"terabyte"},
|
||||
{u"duration", u"week"},
|
||||
{u"length", u"yard"},
|
||||
{u"duration", u"year"},
|
||||
};
|
||||
|
||||
for (const auto& cas1 : cases) {
|
||||
for (const auto& cas2 : cases) {
|
||||
UnicodeString skeleton(u"measure-unit/");
|
||||
skeleton += cas1.type;
|
||||
skeleton += u"-";
|
||||
skeleton += cas1.subtype;
|
||||
skeleton += u" ";
|
||||
skeleton += u"per-measure-unit/";
|
||||
skeleton += cas2.type;
|
||||
skeleton += u"-";
|
||||
skeleton += cas2.subtype;
|
||||
|
||||
status.setScope(skeleton);
|
||||
UnicodeString actual = NumberFormatter::forSkeleton(skeleton, status).locale("ar")
|
||||
.formatDouble(5142.3, status)
|
||||
.toString(status);
|
||||
status.errIfFailureAndReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
@ -9679,6 +9679,9 @@ void NumberFormatTest::Test20956_MonetarySymbolGetters() {
|
||||
IcuTestErrorCode status(*this, "Test20956_MonetarySymbolGetters");
|
||||
LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
|
||||
NumberFormat::createCurrencyInstance("et", status)));
|
||||
if (status.errDataIfFailureAndReset()) {
|
||||
return;
|
||||
}
|
||||
|
||||
decimalFormat->setCurrency(u"EEK");
|
||||
|
||||
@ -9823,6 +9826,9 @@ void NumberFormatTest::Test20961_CurrencyPluralPattern() {
|
||||
{
|
||||
LocalPointer<DecimalFormat> decimalFormat(static_cast<DecimalFormat*>(
|
||||
NumberFormat::createInstance("en-US", UNUM_CURRENCY_PLURAL, status)));
|
||||
if (status.errDataIfFailureAndReset()) {
|
||||
return;
|
||||
}
|
||||
UnicodeString result;
|
||||
decimalFormat->toPattern(result);
|
||||
assertEquals("Currency pattern", u"#,##0.00 ¤¤¤", result);
|
||||
|
@ -1395,6 +1395,14 @@ void NewResourceBundleTest::TestFilter() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The following test for ICU-20706 has infinite loops on certain inputs for
|
||||
* locales and calendars. In order to unblock the build (ICU-21055), those
|
||||
* specific values are temporarily removed.
|
||||
* The issue of the infinite loops and its blocking dependencies were captured
|
||||
* in ICU-21080.
|
||||
*/
|
||||
|
||||
void NewResourceBundleTest::TestIntervalAliasFallbacks() {
|
||||
const char* locales[] = {
|
||||
// Thee will not cause infinity loop
|
||||
@ -1402,6 +1410,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
|
||||
"ja",
|
||||
|
||||
// These will cause infinity loop
|
||||
#if 0
|
||||
"fr_CA",
|
||||
"en_150",
|
||||
"es_419",
|
||||
@ -1413,6 +1422,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
|
||||
"zh_Hant",
|
||||
"zh_Hant_TW",
|
||||
"zh_TW",
|
||||
#endif
|
||||
};
|
||||
const char* calendars[] = {
|
||||
// These won't cause infinity loop
|
||||
@ -1420,6 +1430,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
|
||||
"chinese",
|
||||
|
||||
// These will cause infinity loop
|
||||
#if 0
|
||||
"islamic",
|
||||
"islamic-civil",
|
||||
"islamic-tbla",
|
||||
@ -1428,6 +1439,7 @@ void NewResourceBundleTest::TestIntervalAliasFallbacks() {
|
||||
"islamic-rgsa",
|
||||
"japanese",
|
||||
"roc",
|
||||
#endif
|
||||
};
|
||||
|
||||
for (int lidx = 0; lidx < UPRV_LENGTHOF(locales); lidx++) {
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
void TestGetByFallback(void);
|
||||
|
||||
void TestFilter(void);
|
||||
|
||||
void TestIntervalAliasFallbacks(void);
|
||||
|
||||
#if U_ENABLE_TRACING
|
||||
|
@ -1562,6 +1562,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
|
||||
BASIC_TRANSLITERATOR_ID[i], UTRANS_FORWARD, parseError, status));
|
||||
if (translit.get() == nullptr || !U_SUCCESS(status)) {
|
||||
dataerrln("FAIL: createInstance %s failed", BASIC_TRANSLITERATOR_ID[i]);
|
||||
continue;
|
||||
}
|
||||
UnicodeString data(TEST_DATA);
|
||||
UnicodeString expected(EXPECTED_RESULTS[i]);
|
||||
@ -1570,6 +1571,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
|
||||
dataerrln(UnicodeString("FAIL: expected translit(") +
|
||||
BASIC_TRANSLITERATOR_ID[i] + ") = '" +
|
||||
EXPECTED_RESULTS[i] + "' but got '" + data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (int32_t i=0; BASIC_TRANSLITERATOR_RULES[i]; i++) {
|
||||
@ -1580,6 +1582,7 @@ void TransliteratorTest::TestBasicTransliteratorEvenWithoutData() {
|
||||
BASIC_TRANSLITERATOR_RULES[i], UTRANS_FORWARD, parseError, status));
|
||||
if (translit.get() == nullptr || !U_SUCCESS(status)) {
|
||||
dataerrln("FAIL: createFromRules %s failed", BASIC_TRANSLITERATOR_RULES[i]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#*******************************************************************************
|
||||
api.report.version = 67
|
||||
api.report.prev.version = 66
|
||||
release.file.ver = 67rc
|
||||
api.doc.version = 67 Release Candidate
|
||||
maven.pom.ver = 67.1-SNAPSHOT
|
||||
release.file.ver = 67_1
|
||||
api.doc.version = 67.1
|
||||
maven.pom.ver = 67.1
|
||||
|
||||
|
@ -241,8 +241,10 @@ public class LongNameHandler implements MicroPropsGenerator, ModifierStore {
|
||||
String compiled = SimpleFormatterImpl
|
||||
.compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2);
|
||||
String secondaryFormat = getWithPlural(secondaryData, StandardPlural.ONE);
|
||||
|
||||
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
|
||||
String secondaryCompiled = SimpleFormatterImpl
|
||||
.compileToStringMinMaxArguments(secondaryFormat, sb, 1, 1);
|
||||
.compileToStringMinMaxArguments(secondaryFormat, sb, 0, 1);
|
||||
String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(secondaryCompiled)
|
||||
.trim();
|
||||
perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled, "{0}", secondaryString);
|
||||
|
@ -349,4 +349,65 @@ public class NumberSkeletonTest {
|
||||
assertEquals(mode.toString(), modeString, skeleton.substring(14));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void perUnitInArabic() {
|
||||
String[][] cases = {
|
||||
{"area", "acre"},
|
||||
{"digital", "bit"},
|
||||
{"digital", "byte"},
|
||||
{"temperature", "celsius"},
|
||||
{"length", "centimeter"},
|
||||
{"duration", "day"},
|
||||
{"angle", "degree"},
|
||||
{"temperature", "fahrenheit"},
|
||||
{"volume", "fluid-ounce"},
|
||||
{"length", "foot"},
|
||||
{"volume", "gallon"},
|
||||
{"digital", "gigabit"},
|
||||
{"digital", "gigabyte"},
|
||||
{"mass", "gram"},
|
||||
{"area", "hectare"},
|
||||
{"duration", "hour"},
|
||||
{"length", "inch"},
|
||||
{"digital", "kilobit"},
|
||||
{"digital", "kilobyte"},
|
||||
{"mass", "kilogram"},
|
||||
{"length", "kilometer"},
|
||||
{"volume", "liter"},
|
||||
{"digital", "megabit"},
|
||||
{"digital", "megabyte"},
|
||||
{"length", "meter"},
|
||||
{"length", "mile"},
|
||||
{"length", "mile-scandinavian"},
|
||||
{"volume", "milliliter"},
|
||||
{"length", "millimeter"},
|
||||
{"duration", "millisecond"},
|
||||
{"duration", "minute"},
|
||||
{"duration", "month"},
|
||||
{"mass", "ounce"},
|
||||
{"concentr", "percent"},
|
||||
{"digital", "petabyte"},
|
||||
{"mass", "pound"},
|
||||
{"duration", "second"},
|
||||
{"mass", "stone"},
|
||||
{"digital", "terabit"},
|
||||
{"digital", "terabyte"},
|
||||
{"duration", "week"},
|
||||
{"length", "yard"},
|
||||
{"duration", "year"},
|
||||
};
|
||||
|
||||
ULocale arabic = new ULocale("ar");
|
||||
for (String[] cas1 : cases) {
|
||||
for (String[] cas2 : cases) {
|
||||
String skeleton = "measure-unit/";
|
||||
skeleton += cas1[0] + "-" + cas1[1] + " per-measure-unit/" + cas2[0] + "-" + cas2[1];
|
||||
|
||||
String actual = NumberFormatter.forSkeleton(skeleton).locale(arabic).format(5142.3)
|
||||
.toString();
|
||||
// Just make sure it won't throw exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,15 +56,23 @@ inline void abort_noreturn() { abort(); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Not all compilers support __has_attribute and combining a check for both
|
||||
// ifdef and __has_attribute on the same preprocessor line isn't portable.
|
||||
#ifdef __has_attribute
|
||||
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) __has_attribute(x)
|
||||
#else
|
||||
# define DOUBLE_CONVERSION_HAS_ATTRIBUTE(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef DOUBLE_CONVERSION_UNUSED
|
||||
#ifdef __GNUC__
|
||||
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(unused)
|
||||
#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_UNUSED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && __has_attribute(uninitialized)
|
||||
#if DOUBLE_CONVERSION_HAS_ATTRIBUTE(uninitialized)
|
||||
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
|
||||
#else
|
||||
#define DOUBLE_CONVERSION_STACK_UNINITIALIZED
|
||||
|
@ -147,24 +147,25 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
|
||||
<ClCompile Include="..\double-conversion\bignum.cc" />
|
||||
<ClCompile Include="..\double-conversion\bignum-dtoa.cc" />
|
||||
<ClCompile Include="..\double-conversion\cached-powers.cc" />
|
||||
<ClCompile Include="..\double-conversion\diy-fp.cc" />
|
||||
<ClCompile Include="..\double-conversion\double-conversion.cc" />
|
||||
<ClCompile Include="..\double-conversion\double-to-string.cc" />
|
||||
<ClCompile Include="..\double-conversion\fast-dtoa.cc" />
|
||||
<ClCompile Include="..\double-conversion\fixed-dtoa.cc" />
|
||||
<ClCompile Include="..\double-conversion\string-to-double.cc" />
|
||||
<ClCompile Include="..\double-conversion\strtod.cc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\double-conversion\bignum-dtoa.h" />
|
||||
<ClInclude Include="..\double-conversion\bignum.h" />
|
||||
<ClInclude Include="..\double-conversion\cached-powers.h" />
|
||||
<ClInclude Include="..\double-conversion\diy-fp.h" />
|
||||
<ClInclude Include="..\double-conversion\double-conversion.h" />
|
||||
<ClInclude Include="..\double-conversion\double-to-string.h" />
|
||||
<ClInclude Include="..\double-conversion\fast-dtoa.h" />
|
||||
<ClInclude Include="..\double-conversion\fixed-dtoa.h" />
|
||||
<ClInclude Include="..\double-conversion\ieee.h" />
|
||||
<ClInclude Include="..\double-conversion\string-to-double.h" />
|
||||
<ClInclude Include="..\double-conversion\strtod.h" />
|
||||
<ClInclude Include="..\double-conversion\utils.h" />
|
||||
</ItemGroup>
|
||||
|
@ -24,12 +24,6 @@
|
||||
<ClCompile Include="..\double-conversion\cached-powers.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\double-conversion\diy-fp.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\double-conversion\double-conversion.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\double-conversion\fast-dtoa.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@ -39,14 +33,17 @@
|
||||
<ClCompile Include="..\double-conversion\strtod.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\double-conversion\double-to-string.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\double-conversion\string-to-double.cc">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\double-conversion\bignum.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\double-conversion\bignum-dtoa.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\double-conversion\cached-powers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -71,5 +68,11 @@
|
||||
<ClInclude Include="..\double-conversion\utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\double-conversion\double-to-string.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\double-conversion\string-to-double.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -109,6 +109,7 @@
|
||||
<PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)..</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
|
Loading…
Reference in New Issue
Block a user