244 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| // © 2016 and later: Unicode, Inc. and others.
 | |
| // License & terms of use: http://www.unicode.org/copyright.html
 | |
| /*
 | |
| ******************************************************************************
 | |
| * Copyright (C) 2014-2016, International Business Machines
 | |
| * Corporation and others.  All Rights Reserved.
 | |
| ******************************************************************************
 | |
| * quantityformatter.cpp
 | |
| */
 | |
| 
 | |
| #include "unicode/utypes.h"
 | |
| 
 | |
| #if !UCONFIG_NO_FORMATTING
 | |
| 
 | |
| #include "unicode/simpleformatter.h"
 | |
| #include "quantityformatter.h"
 | |
| #include "uassert.h"
 | |
| #include "unicode/unistr.h"
 | |
| #include "unicode/decimfmt.h"
 | |
| #include "cstring.h"
 | |
| #include "unicode/plurrule.h"
 | |
| #include "charstr.h"
 | |
| #include "unicode/fmtable.h"
 | |
| #include "unicode/fieldpos.h"
 | |
| #include "standardplural.h"
 | |
| #include "uassert.h"
 | |
| #include "number_decimalquantity.h"
 | |
| #include "number_utypes.h"
 | |
| #include "formatted_string_builder.h"
 | |
| 
 | |
| U_NAMESPACE_BEGIN
 | |
| 
 | |
| QuantityFormatter::QuantityFormatter() {
 | |
|     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
 | |
|         formatters[i] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
 | |
|     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
 | |
|         if (other.formatters[i] == NULL) {
 | |
|             formatters[i] = NULL;
 | |
|         } else {
 | |
|             formatters[i] = new SimpleFormatter(*other.formatters[i]);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| QuantityFormatter &QuantityFormatter::operator=(
 | |
|         const QuantityFormatter& other) {
 | |
|     if (this == &other) {
 | |
|         return *this;
 | |
|     }
 | |
|     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
 | |
|         delete formatters[i];
 | |
|         if (other.formatters[i] == NULL) {
 | |
|             formatters[i] = NULL;
 | |
|         } else {
 | |
|             formatters[i] = new SimpleFormatter(*other.formatters[i]);
 | |
|         }
 | |
|     }
 | |
|     return *this;
 | |
| }
 | |
| 
 | |
| QuantityFormatter::~QuantityFormatter() {
 | |
|     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
 | |
|         delete formatters[i];
 | |
|     }
 | |
| }
 | |
| 
 | |
| void QuantityFormatter::reset() {
 | |
|     for (int32_t i = 0; i < UPRV_LENGTHOF(formatters); ++i) {
 | |
|         delete formatters[i];
 | |
|         formatters[i] = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| UBool QuantityFormatter::addIfAbsent(
 | |
|         const char *variant,
 | |
|         const UnicodeString &rawPattern,
 | |
|         UErrorCode &status) {
 | |
|     int32_t pluralIndex = StandardPlural::indexFromString(variant, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return FALSE;
 | |
|     }
 | |
|     if (formatters[pluralIndex] != NULL) {
 | |
|         return TRUE;
 | |
|     }
 | |
|     SimpleFormatter *newFmt = new SimpleFormatter(rawPattern, 0, 1, status);
 | |
|     if (newFmt == NULL) {
 | |
|         status = U_MEMORY_ALLOCATION_ERROR;
 | |
|         return FALSE;
 | |
|     }
 | |
|     if (U_FAILURE(status)) {
 | |
|         delete newFmt;
 | |
|         return FALSE;
 | |
|     }
 | |
|     formatters[pluralIndex] = newFmt;
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| UBool QuantityFormatter::isValid() const {
 | |
|     return formatters[StandardPlural::OTHER] != NULL;
 | |
| }
 | |
| 
 | |
| const SimpleFormatter *QuantityFormatter::getByVariant(
 | |
|         const char *variant) const {
 | |
|     U_ASSERT(isValid());
 | |
|     int32_t pluralIndex = StandardPlural::indexOrOtherIndexFromString(variant);
 | |
|     const SimpleFormatter *pattern = formatters[pluralIndex];
 | |
|     if (pattern == NULL) {
 | |
|         pattern = formatters[StandardPlural::OTHER];
 | |
|     }
 | |
|     return pattern;
 | |
| }
 | |
| 
 | |
| UnicodeString &QuantityFormatter::format(
 | |
|             const Formattable &number,
 | |
|             const NumberFormat &fmt,
 | |
|             const PluralRules &rules,
 | |
|             UnicodeString &appendTo,
 | |
|             FieldPosition &pos,
 | |
|             UErrorCode &status) const {
 | |
|     UnicodeString formattedNumber;
 | |
|     StandardPlural::Form p = selectPlural(number, fmt, rules, formattedNumber, pos, status);
 | |
|     if (U_FAILURE(status)) {
 | |
|         return appendTo;
 | |
|     }
 | |
|     const SimpleFormatter *pattern = formatters[p];
 | |
|     if (pattern == NULL) {
 | |
|         pattern = formatters[StandardPlural::OTHER];
 | |
|         if (pattern == NULL) {
 | |
|             status = U_INVALID_STATE_ERROR;
 | |
|             return appendTo;
 | |
|         }
 | |
|     }
 | |
|     return format(*pattern, formattedNumber, appendTo, pos, status);
 | |
| }
 | |
| 
 | |
| // The following methods live here so that class PluralRules does not depend on number formatting,
 | |
| // and the SimpleFormatter does not depend on FieldPosition.
 | |
| 
 | |
| StandardPlural::Form QuantityFormatter::selectPlural(
 | |
|             const Formattable &number,
 | |
|             const NumberFormat &fmt,
 | |
|             const PluralRules &rules,
 | |
|             UnicodeString &formattedNumber,
 | |
|             FieldPosition &pos,
 | |
|             UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return StandardPlural::OTHER;
 | |
|     }
 | |
|     UnicodeString pluralKeyword;
 | |
|     const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
 | |
|     if (decFmt != NULL) {
 | |
|         number::impl::DecimalQuantity dq;
 | |
|         decFmt->formatToDecimalQuantity(number, dq, status);
 | |
|         if (U_FAILURE(status)) {
 | |
|             return StandardPlural::OTHER;
 | |
|         }
 | |
|         pluralKeyword = rules.select(dq);
 | |
|         decFmt->format(number, formattedNumber, pos, status);
 | |
|     } else {
 | |
|         if (number.getType() == Formattable::kDouble) {
 | |
|             pluralKeyword = rules.select(number.getDouble());
 | |
|         } else if (number.getType() == Formattable::kLong) {
 | |
|             pluralKeyword = rules.select(number.getLong());
 | |
|         } else if (number.getType() == Formattable::kInt64) {
 | |
|             pluralKeyword = rules.select((double) number.getInt64());
 | |
|         } else {
 | |
|             status = U_ILLEGAL_ARGUMENT_ERROR;
 | |
|             return StandardPlural::OTHER;
 | |
|         }
 | |
|         fmt.format(number, formattedNumber, pos, status);
 | |
|     }
 | |
|     return StandardPlural::orOtherFromString(pluralKeyword);
 | |
| }
 | |
| 
 | |
| void QuantityFormatter::formatAndSelect(
 | |
|         double quantity,
 | |
|         const NumberFormat& fmt,
 | |
|         const PluralRules& rules,
 | |
|         FormattedStringBuilder& output,
 | |
|         StandardPlural::Form& pluralForm,
 | |
|         UErrorCode& status) {
 | |
|     UnicodeString pluralKeyword;
 | |
|     const DecimalFormat* df = dynamic_cast<const DecimalFormat*>(&fmt);
 | |
|     if (df != nullptr) {
 | |
|         number::impl::UFormattedNumberData fn;
 | |
|         fn.quantity.setToDouble(quantity);
 | |
|         const number::LocalizedNumberFormatter* lnf = df->toNumberFormatter(status);
 | |
|         if (U_FAILURE(status)) {
 | |
|             return;
 | |
|         }
 | |
|         lnf->formatImpl(&fn, status);
 | |
|         if (U_FAILURE(status)) {
 | |
|             return;
 | |
|         }
 | |
|         output = std::move(fn.getStringRef());
 | |
|         pluralKeyword = rules.select(fn.quantity);
 | |
|     } else {
 | |
|         UnicodeString result;
 | |
|         fmt.format(quantity, result, status);
 | |
|         if (U_FAILURE(status)) {
 | |
|             return;
 | |
|         }
 | |
|         // This code path is probably RBNF. Use the generic numeric field.
 | |
|         output.append(result, kGeneralNumericField, status);
 | |
|         if (U_FAILURE(status)) {
 | |
|             return;
 | |
|         }
 | |
|         pluralKeyword = rules.select(quantity);
 | |
|     }
 | |
|     pluralForm = StandardPlural::orOtherFromString(pluralKeyword);
 | |
| }
 | |
| 
 | |
| UnicodeString &QuantityFormatter::format(
 | |
|             const SimpleFormatter &pattern,
 | |
|             const UnicodeString &value,
 | |
|             UnicodeString &appendTo,
 | |
|             FieldPosition &pos,
 | |
|             UErrorCode &status) {
 | |
|     if (U_FAILURE(status)) {
 | |
|         return appendTo;
 | |
|     }
 | |
|     const UnicodeString *param = &value;
 | |
|     int32_t offset;
 | |
|     pattern.formatAndAppend(¶m, 1, appendTo, &offset, 1, status);
 | |
|     if (pos.getBeginIndex() != 0 || pos.getEndIndex() != 0) {
 | |
|         if (offset >= 0) {
 | |
|             pos.setBeginIndex(pos.getBeginIndex() + offset);
 | |
|             pos.setEndIndex(pos.getEndIndex() + offset);
 | |
|         } else {
 | |
|             pos.setBeginIndex(0);
 | |
|             pos.setEndIndex(0);
 | |
|         }
 | |
|     }
 | |
|     return appendTo;
 | |
| }
 | |
| 
 | |
| U_NAMESPACE_END
 | |
| 
 | |
| #endif /* #if !UCONFIG_NO_FORMATTING */
 |