X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/4388f060552cc537e71e957d32f35e9d75a61233..HEAD:/icuSources/i18n/plurfmt.cpp diff --git a/icuSources/i18n/plurfmt.cpp b/icuSources/i18n/plurfmt.cpp index 510b8324..b9943763 100644 --- a/icuSources/i18n/plurfmt.cpp +++ b/icuSources/i18n/plurfmt.cpp @@ -1,31 +1,36 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* -* Copyright (C) 2009-2012, International Business Machines Corporation and +* Copyright (C) 2009-2015, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* * * File PLURFMT.CPP -* -* Modification History: -* -* Date Name Description ******************************************************************************* */ +#include "unicode/decimfmt.h" #include "unicode/messagepattern.h" #include "unicode/plurfmt.h" #include "unicode/plurrule.h" #include "unicode/utypes.h" #include "cmemory.h" #include "messageimpl.h" +#include "nfrule.h" #include "plurrule_impl.h" #include "uassert.h" #include "uhash.h" +#include "number_decimalquantity.h" +#include "number_utils.h" +#include "number_utypes.h" #if !UCONFIG_NO_FORMATTING U_NAMESPACE_BEGIN +using number::impl::DecimalQuantity; + static const UChar OTHER_STRING[] = { 0x6F, 0x74, 0x68, 0x65, 0x72, 0 // "other" }; @@ -37,7 +42,7 @@ PluralFormat::PluralFormat(UErrorCode& status) msgPattern(status), numberFormat(NULL), offset(0) { - init(NULL, status); + init(NULL, UPLURAL_TYPE_CARDINAL, status); } PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) @@ -45,7 +50,7 @@ PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) msgPattern(status), numberFormat(NULL), offset(0) { - init(NULL, status); + init(NULL, UPLURAL_TYPE_CARDINAL, status); } PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) @@ -53,7 +58,7 @@ PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) msgPattern(status), numberFormat(NULL), offset(0) { - init(&rules, status); + init(&rules, UPLURAL_TYPE_COUNT, status); } PluralFormat::PluralFormat(const Locale& loc, @@ -63,7 +68,17 @@ PluralFormat::PluralFormat(const Locale& loc, msgPattern(status), numberFormat(NULL), offset(0) { - init(&rules, status); + init(&rules, UPLURAL_TYPE_COUNT, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + UPluralType type, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(NULL), + offset(0) { + init(NULL, type, status); } PluralFormat::PluralFormat(const UnicodeString& pat, @@ -72,7 +87,7 @@ PluralFormat::PluralFormat(const UnicodeString& pat, msgPattern(status), numberFormat(NULL), offset(0) { - init(NULL, status); + init(NULL, UPLURAL_TYPE_CARDINAL, status); applyPattern(pat, status); } @@ -83,7 +98,7 @@ PluralFormat::PluralFormat(const Locale& loc, msgPattern(status), numberFormat(NULL), offset(0) { - init(NULL, status); + init(NULL, UPLURAL_TYPE_CARDINAL, status); applyPattern(pat, status); } @@ -94,7 +109,7 @@ PluralFormat::PluralFormat(const PluralRules& rules, msgPattern(status), numberFormat(NULL), offset(0) { - init(&rules, status); + init(&rules, UPLURAL_TYPE_COUNT, status); applyPattern(pat, status); } @@ -106,7 +121,19 @@ PluralFormat::PluralFormat(const Locale& loc, msgPattern(status), numberFormat(NULL), offset(0) { - init(&rules, status); + init(&rules, UPLURAL_TYPE_COUNT, status); + applyPattern(pat, status); +} + +PluralFormat::PluralFormat(const Locale& loc, + UPluralType type, + const UnicodeString& pat, + UErrorCode& status) + : locale(loc), + msgPattern(status), + numberFormat(NULL), + offset(0) { + init(NULL, type, status); applyPattern(pat, status); } @@ -132,7 +159,7 @@ PluralFormat::copyObjects(const PluralFormat& other) { if (other.numberFormat == NULL) { numberFormat = NumberFormat::createInstance(locale, status); } else { - numberFormat = (NumberFormat*)other.numberFormat->clone(); + numberFormat = other.numberFormat->clone(); } if (other.pluralRulesWrapper.pluralRules == NULL) { pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status); @@ -147,13 +174,13 @@ PluralFormat::~PluralFormat() { } void -PluralFormat::init(const PluralRules* rules, UErrorCode& status) { +PluralFormat::init(const PluralRules* rules, UPluralType type, UErrorCode& status) { if (U_FAILURE(status)) { return; } if (rules==NULL) { - pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, status); + pluralRulesWrapper.pluralRules = PluralRules::forLocale(locale, type, status); } else { pluralRulesWrapper.pluralRules = rules->clone(); if (pluralRulesWrapper.pluralRules == NULL) { @@ -185,7 +212,7 @@ PluralFormat::format(const Formattable& obj, if (U_FAILURE(status)) return appendTo; if (obj.isNumeric()) { - return format(obj.getDouble(), appendTo, pos, status); + return format(obj, obj.getDouble(), appendTo, pos, status); } else { status = U_ILLEGAL_ARGUMENT_ERROR; return appendTo; @@ -194,16 +221,16 @@ PluralFormat::format(const Formattable& obj, UnicodeString PluralFormat::format(int32_t number, UErrorCode& status) const { - FieldPosition fpos(0); + FieldPosition fpos(FieldPosition::DONT_CARE); UnicodeString result; - return format(number, result, fpos, status); + return format(Formattable(number), number, result, fpos, status); } UnicodeString PluralFormat::format(double number, UErrorCode& status) const { - FieldPosition fpos(0); + FieldPosition fpos(FieldPosition::DONT_CARE); UnicodeString result; - return format(number, result, fpos, status); + return format(Formattable(number), number, result, fpos, status); } @@ -212,7 +239,7 @@ PluralFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const { - return format((double)number, appendTo, pos, status); + return format(Formattable(number), (double)number, appendTo, pos, status); } UnicodeString& @@ -220,18 +247,58 @@ PluralFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos, UErrorCode& status) const { + return format(Formattable(number), (double)number, appendTo, pos, status); +} + +UnicodeString& +PluralFormat::format(const Formattable& numberObject, double number, + UnicodeString& appendTo, + FieldPosition& pos, + UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; } if (msgPattern.countParts() == 0) { - return numberFormat->format(number, appendTo, pos); + return numberFormat->format(numberObject, appendTo, pos, status); } + // Get the appropriate sub-message. - int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, number, status); + // Select it based on the formatted number-offset. + double numberMinusOffset = number - offset; + // Call NumberFormatter to get both the DecimalQuantity and the string. + // This call site needs to use more internal APIs than the Java equivalent. + number::impl::UFormattedNumberData data; + if (offset == 0) { + // could be BigDecimal etc. + numberObject.populateDecimalQuantity(data.quantity, status); + } else { + data.quantity.setToDouble(numberMinusOffset); + } + UnicodeString numberString; + auto *decFmt = dynamic_cast(numberFormat); + if(decFmt != nullptr) { + const number::LocalizedNumberFormatter* lnf = decFmt->toNumberFormatter(status); + if (U_FAILURE(status)) { + return appendTo; + } + lnf->formatImpl(&data, status); // mutates &data + if (U_FAILURE(status)) { + return appendTo; + } + numberString = data.getStringRef().toUnicodeString(); + } else { + if (offset == 0) { + numberFormat->format(numberObject, numberString, status); + } else { + numberFormat->format(numberMinusOffset, numberString, status); + } + } + + int32_t partIndex = findSubMessage(msgPattern, 0, pluralRulesWrapper, &data.quantity, number, status); + if (U_FAILURE(status)) { return appendTo; } // Replace syntactic # signs in the top level of this sub-message // (not in nested arguments) with the formatted number-offset. const UnicodeString& pattern = msgPattern.getPatternString(); - number -= offset; int32_t prevIndex = msgPattern.getPart(partIndex).getLimit(); for (;;) { const MessagePattern::Part& part = msgPattern.getPart(++partIndex); @@ -243,7 +310,7 @@ PluralFormat::format(double number, (type == UMSGPAT_PART_TYPE_SKIP_SYNTAX && MessageImpl::jdkAposMode(msgPattern))) { appendTo.append(pattern, prevIndex, index - prevIndex); if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) { - numberFormat->format(number, appendTo); + appendTo.append(numberString); } prevIndex = part.getLimit(); } else if (type == UMSGPAT_PART_TYPE_ARG_START) { @@ -278,7 +345,7 @@ PluralFormat::setLocale(const Locale& loc, UErrorCode& status) { offset = 0; numberFormat = NULL; pluralRulesWrapper.reset(); - init(NULL, status); + init(NULL, UPLURAL_TYPE_CARDINAL, status); } void @@ -286,7 +353,7 @@ PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { if (U_FAILURE(status)) { return; } - NumberFormat* nf = (NumberFormat*)format->clone(); + NumberFormat* nf = format->clone(); if (nf != NULL) { delete numberFormat; numberFormat = nf; @@ -295,7 +362,7 @@ PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& status) { } } -Format* +PluralFormat* PluralFormat::clone() const { return new PluralFormat(*this); @@ -348,7 +415,8 @@ PluralFormat::parseObject(const UnicodeString& /*source*/, } int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex, - const PluralSelector& selector, double number, UErrorCode& ec) { + const PluralSelector& selector, void *context, + double number, UErrorCode& ec) { if (U_FAILURE(ec)) { return 0; } @@ -361,7 +429,7 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part } else { offset=0; } - // The keyword is empty until we need to match against non-explicit, not-"other" value. + // The keyword is empty until we need to match against a non-explicit, not-"other" value. // Then we get the keyword from the selector. // (In other words, we never call the selector if we match against an explicit value, // or if the only non-explicit keyword is "other".) @@ -414,7 +482,7 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part } } else { if(keyword.isEmpty()) { - keyword=selector.select(number-offset, ec); + keyword=selector.select(context, number-offset, ec); if(msgStart!=0 && (0 == keyword.compare(other))) { // We have already seen an "other" sub-message. // Do not match "other" again. @@ -435,15 +503,88 @@ int32_t PluralFormat::findSubMessage(const MessagePattern& pattern, int32_t part return msgStart; } +void PluralFormat::parseType(const UnicodeString& source, const NFRule *rbnfLenientScanner, Formattable& result, FieldPosition& pos) const { + // If no pattern was applied, return null. + if (msgPattern.countParts() == 0) { + pos.setBeginIndex(-1); + pos.setEndIndex(-1); + return; + } + int partIndex = 0; + int currMatchIndex; + int count=msgPattern.countParts(); + int startingAt = pos.getBeginIndex(); + if (startingAt < 0) { + startingAt = 0; + } + + // The keyword is null until we need to match against a non-explicit, not-"other" value. + // Then we get the keyword from the selector. + // (In other words, we never call the selector if we match against an explicit value, + // or if the only non-explicit keyword is "other".) + UnicodeString keyword; + UnicodeString matchedWord; + const UnicodeString& pattern = msgPattern.getPatternString(); + int matchedIndex = -1; + // Iterate over (ARG_SELECTOR ARG_START message ARG_LIMIT) tuples + // until the end of the plural-only pattern. + while (partIndex < count) { + const MessagePattern::Part* partSelector = &msgPattern.getPart(partIndex++); + if (partSelector->getType() != UMSGPAT_PART_TYPE_ARG_SELECTOR) { + // Bad format + continue; + } + + const MessagePattern::Part* partStart = &msgPattern.getPart(partIndex++); + if (partStart->getType() != UMSGPAT_PART_TYPE_MSG_START) { + // Bad format + continue; + } + + const MessagePattern::Part* partLimit = &msgPattern.getPart(partIndex++); + if (partLimit->getType() != UMSGPAT_PART_TYPE_MSG_LIMIT) { + // Bad format + continue; + } + + UnicodeString currArg = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); + if (rbnfLenientScanner != NULL) { + // If lenient parsing is turned ON, we've got some time consuming parsing ahead of us. + int32_t length = -1; + currMatchIndex = rbnfLenientScanner->findTextLenient(source, currArg, startingAt, &length); + } + else { + currMatchIndex = source.indexOf(currArg, startingAt); + } + if (currMatchIndex >= 0 && currMatchIndex >= matchedIndex && currArg.length() > matchedWord.length()) { + matchedIndex = currMatchIndex; + matchedWord = currArg; + keyword = pattern.tempSubString(partStart->getLimit(), partLimit->getIndex() - partStart->getLimit()); + } + } + if (matchedIndex >= 0) { + pos.setBeginIndex(matchedIndex); + pos.setEndIndex(matchedIndex + matchedWord.length()); + result.setString(keyword); + return; + } + + // Not found! + pos.setBeginIndex(-1); + pos.setEndIndex(-1); +} + PluralFormat::PluralSelector::~PluralSelector() {} PluralFormat::PluralSelectorAdapter::~PluralSelectorAdapter() { delete pluralRules; } -UnicodeString PluralFormat::PluralSelectorAdapter::select(double number, +UnicodeString PluralFormat::PluralSelectorAdapter::select(void *context, double number, UErrorCode& /*ec*/) const { - return pluralRules->select(number); + (void)number; // unused except in the assertion + IFixedDecimal *dec=static_cast(context); + return pluralRules->select(*dec); } void PluralFormat::PluralSelectorAdapter::reset() {