]> granicus.if.org Git - icu/commitdiff
ICU-11276 Wiring SimpleFormatter logic into NumberRangeFormatter.
authorShane Carr <shane@unicode.org>
Thu, 6 Sep 2018 03:44:27 +0000 (20:44 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:40 +0000 (14:27 -0700)
icu4c/source/i18n/number_formatimpl.cpp
icu4c/source/i18n/number_formatimpl.h
icu4c/source/i18n/number_modifiers.cpp
icu4c/source/i18n/number_modifiers.h
icu4c/source/i18n/number_stringbuilder.cpp
icu4c/source/i18n/numrange_impl.cpp
icu4c/source/i18n/numrange_impl.h

index d84ba2cfead78d8314a8854ade6d22de507a8a90..a48f810a679b0c7709ff84efb16fb930353ce6c4 100644 (file)
@@ -71,13 +71,14 @@ NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& s
     : NumberFormatterImpl(macros, true, status) {
 }
 
-void NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
+int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
                                        NumberStringBuilder& outString, UErrorCode& status) {
     NumberFormatterImpl impl(macros, false, status);
     MicroProps& micros = impl.preProcessUnsafe(inValue, status);
-    if (U_FAILURE(status)) { return; }
+    if (U_FAILURE(status)) { return 0; }
     int32_t length = writeNumber(micros, inValue, outString, 0, status);
-    writeAffixes(micros, outString, 0, length, status);
+    length += writeAffixes(micros, outString, 0, length, status);
+    return length;
 }
 
 int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum,
@@ -92,13 +93,14 @@ int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int
 // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
 // See MicroProps::processQuantity() for details.
 
-void NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString,
+int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString,
                                 UErrorCode& status) const {
     MicroProps micros;
     preProcess(inValue, micros, status);
-    if (U_FAILURE(status)) { return; }
+    if (U_FAILURE(status)) { return 0; }
     int32_t length = writeNumber(micros, inValue, outString, 0, status);
-    writeAffixes(micros, outString, 0, length, status);
+    length += writeAffixes(micros, outString, 0, length, status);
+    return length;
 }
 
 void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
index 9b89e5b88e61de04ab31677143f509b18c3b8eae..fda38c92845f87cb3d6de62a7a1274fd9abb3c76 100644 (file)
@@ -34,7 +34,7 @@ class NumberFormatterImpl : public UMemory {
     /**
      * Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
      */
-    static void
+    static int32_t
     formatStatic(const MacroProps &macros, DecimalQuantity &inValue, NumberStringBuilder &outString,
                  UErrorCode &status);
 
@@ -51,7 +51,7 @@ class NumberFormatterImpl : public UMemory {
     /**
      * Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
      */
-    void format(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
+    int32_t format(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const;
 
     /**
      * Like format(), but saves the result into an output MicroProps without additional processing.
index d69413f1f63025093e4889088ffc3bb6372954fd..07adce16c49788c7f95d34b323b81ea4442384fa 100644 (file)
@@ -107,15 +107,19 @@ SimpleModifier::SimpleModifier(const SimpleFormatter &simpleFormatter, Field fie
     } else {
         U_ASSERT(argLimit == 1);
         if (fCompiledPattern.charAt(1) != 0) {
+            // Found prefix
             fPrefixLength = fCompiledPattern.charAt(1) - ARG_NUM_LIMIT;
             fSuffixOffset = 3 + fPrefixLength;
         } else {
+            // No prefix
             fPrefixLength = 0;
             fSuffixOffset = 2;
         }
         if (3 + fPrefixLength < fCompiledPattern.length()) {
+            // Found suffix
             fSuffixLength = fCompiledPattern.charAt(fSuffixOffset) - ARG_NUM_LIMIT;
         } else {
+            // No suffix
             fSuffixLength = 0;
         }
     }
@@ -170,7 +174,7 @@ bool SimpleModifier::operator==(const Modifier& other) const {
 int32_t
 SimpleModifier::formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startIndex, int32_t endIndex,
                                      Field field, UErrorCode &status) const {
-    if (fSuffixOffset == -1) {
+    if (fSuffixOffset == -1 && fPrefixLength + fSuffixLength > 0) {
         // There is no argument for the inner number; overwrite the entire segment with our string.
         return result.splice(startIndex, endIndex, fCompiledPattern, 2, 2 + fPrefixLength, field, status);
     } else {
@@ -190,6 +194,65 @@ SimpleModifier::formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startI
     }
 }
 
+
+int32_t
+SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result,
+                                    int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength,
+                                    Field field, UErrorCode& status) {
+    const UnicodeString& compiledPattern = compiled.compiledPattern;
+    int32_t argLimit = SimpleFormatter::getArgumentLimit(
+            compiledPattern.getBuffer(), compiledPattern.length());
+    if (argLimit != 2) {
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return 0;
+    }
+    int32_t offset = 1; // offset into compiledPattern
+    int32_t length = 0; // chars added to result
+
+    int32_t prefixLength = compiledPattern.charAt(offset);
+    offset++;
+    if (prefixLength < ARG_NUM_LIMIT) {
+        // No prefix
+        prefixLength = 0;
+    } else {
+        prefixLength -= ARG_NUM_LIMIT;
+        result.insert(index + length, compiledPattern, offset, offset + prefixLength, field, status);
+        offset += prefixLength;
+        length += prefixLength;
+        offset++;
+    }
+
+    int32_t infixLength = compiledPattern.charAt(offset);
+    offset++;
+    if (infixLength < ARG_NUM_LIMIT) {
+        // No infix
+        infixLength = 0;
+    } else {
+        infixLength -= ARG_NUM_LIMIT;
+        result.insert(index + length, compiledPattern, offset, offset + infixLength, field, status);
+        offset += infixLength;
+        length += infixLength;
+        offset++;
+    }
+
+    int32_t suffixLength;
+    if (offset == compiledPattern.length()) {
+        // No suffix
+        suffixLength = 0;
+    } else {
+        suffixLength = compiledPattern.charAt(offset) -  ARG_NUM_LIMIT;
+        offset++;
+        result.insert(index + length, compiledPattern, offset, offset + suffixLength, field, status);
+        length += suffixLength;
+    }
+
+    *outPrefixLength = prefixLength;
+    *outSuffixLength = suffixLength;
+
+    return length;
+}
+
+
 int32_t ConstantMultiFieldModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex,
                                           UErrorCode &status) const {
     int32_t length = output.insert(leftIndex, fPrefix, status);
index fa69eeb2a5cff53f6ba7c04e1a7909f2e3dd3cdb..9209e3c0f34e758d318e5852025fc2952c114845 100644 (file)
@@ -74,7 +74,7 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory {
 
     /**
      * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because
-     * DoubleSidedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it.
+     * NumberStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it.
      *
      * <p>
      * Formats a value that is already stored inside the StringBuilder <code>result</code> between the indices
@@ -93,16 +93,32 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory {
      * @return The number of characters (UTF-16 code points) that were added to the StringBuilder.
      */
     int32_t
-    formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startIndex, int32_t endIndex, Field field,
-                         UErrorCode &status) const;
+    formatAsPrefixSuffix(NumberStringBuilder& result, int32_t startIndex, int32_t endIndex, Field field,
+                         UErrorCode& status) const;
+
+    /**
+     * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code.
+     * I put it here so that the SimpleFormatter uses in NumberStringBuilder are near each other.
+     *
+     * <p>
+     * Applies the compiled two-argument pattern to the NumberStringBuilder.
+     *
+     * <p>
+     * This method is optimized for the case where the prefix and suffix are often empty, such as
+     * in the range pattern like "{0}-{1}".
+     */
+    static int32_t
+    formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result,
+                        int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength,
+                        Field field, UErrorCode& status);
 
   private:
     UnicodeString fCompiledPattern;
     Field fField;
-    bool fStrong;
-    int32_t fPrefixLength;
-    int32_t fSuffixOffset;
-    int32_t fSuffixLength;
+    bool fStrong = false;
+    int32_t fPrefixLength = 0;
+    int32_t fSuffixOffset = -1;
+    int32_t fSuffixLength = 0;
 };
 
 /**
index 37770d11d51dfba56f5516cb89056a77956dc7a8..2806fefbaa99037d7dce824f50a0282db61a467f 100644 (file)
@@ -241,6 +241,9 @@ NumberStringBuilder::insert(int32_t index, const NumberStringBuilder &other, UEr
 }
 
 int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) {
+    U_ASSERT(index >= 0);
+    U_ASSERT(index <= fLength);
+    U_ASSERT(count >= 0);
     if (index == 0 && fZero - count >= 0) {
         // Append to start
         fZero -= count;
index f2da9b7043813b2f8029f396b30f5d3deb9873dc..022d8faa76f7874d71c15597d148d743f99c636d 100644 (file)
@@ -11,6 +11,8 @@
 
 #include "unicode/numberrangeformatter.h"
 #include "numrange_impl.h"
+#include "uresimp.h"
+#include "util.h"
 
 using namespace icu;
 using namespace icu::number;
@@ -23,6 +25,63 @@ constexpr int8_t identity2d(UNumberIdentityFallback a, UNumberRangeIdentityResul
     return static_cast<int8_t>(a) | (static_cast<int8_t>(b) << 4);
 }
 
+
+class NumberRangeDataSink : public ResourceSink {
+  public:
+    NumberRangeDataSink(NumberRangeData& data) : fData(data) {}
+
+    void put(const char* key, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
+        ResourceTable miscTable = value.getTable(status);
+        if (U_FAILURE(status)) { return; }
+        for (int i = 0; miscTable.getKeyAndValue(i, key, value); i++) {
+            if (uprv_strcmp(key, "range") == 0) {
+                if (fData.rangePattern.getArgumentLimit() == 0) {
+                    continue; // have already seen this pattern
+                }
+                fData.rangePattern = {value.getUnicodeString(status), status};
+            } else if (uprv_strcmp(key, "approximately") == 0) {
+                if (fData.approximatelyPattern.getArgumentLimit() == 0) {
+                    continue; // have already seen this pattern
+                }
+                fData.approximatelyPattern = {value.getUnicodeString(status), status};
+            }
+        }
+    }
+
+  private:
+    NumberRangeData& fData;
+};
+
+void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeData& data, UErrorCode& status) {
+    if (U_FAILURE(status)) { return; }
+    LocalUResourceBundlePointer rb(ures_open(NULL, localeName, &status));
+    if (U_FAILURE(status)) { return; }
+    NumberRangeDataSink sink(data);
+
+    CharString dataPath;
+    dataPath.append("NumberElements/", -1, status);
+    dataPath.append(nsName, -1, status);
+    dataPath.append("/miscPatterns", -1, status);
+    ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
+    if (U_FAILURE(status)) { return; }
+
+    if (uprv_strcmp(nsName, "latn") != 0 && (data.rangePattern.getArgumentLimit() == 0
+            || data.approximatelyPattern.getArgumentLimit() == 0)) {
+        // fall back to Latin data
+        ures_getAllItemsWithFallback(rb.getAlias(), "NumberElements/latn/miscPatterns", sink, status);
+        if (U_FAILURE(status)) { return; }
+    }
+
+    if (data.rangePattern.getArgumentLimit() == 0) {
+        // No data!
+        data.rangePattern = {u"{0} --- {1}", status};
+    }
+    if (data.approximatelyPattern.getArgumentLimit() == 0) {
+        // No data!
+        data.approximatelyPattern = {u"~{0}", status};
+    }
+}
+
 } // namespace
 
 
@@ -32,6 +91,12 @@ NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros
       fSameFormatters(true), // FIXME
       fCollapse(macros.collapse),
       fIdentityFallback(macros.identityFallback) {
+    // TODO: get local ns
+    NumberRangeData data;
+    getNumberRangeData(macros.locale.getName(), "latn", data, status);
+    if (U_FAILURE(status)) { return; }
+    fRangeFormatter = data.rangePattern;
+    fApproximatelyModifier = {data.approximatelyPattern, UNUM_FIELD_COUNT, false};
 }
 
 void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equalBeforeRounding, UErrorCode& status) const {
@@ -116,9 +181,8 @@ void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& d
                                                     MicroProps& micros1, MicroProps& micros2,
                                                     UErrorCode& status) const {
     if (fSameFormatters) {
-        // FIXME
-        formatterImpl1.format(data.quantity1, data.string, status);
-        data.string.insertCodePoint(0, u'~', UNUM_FIELD_COUNT, status);
+        int32_t length = formatterImpl1.format(data.quantity1, data.string, status);
+        fApproximatelyModifier.apply(data.string, 0, length, status);
     } else {
         formatRange(data, micros1, micros2, status);
     }
@@ -198,30 +262,41 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
     }
 
     NumberStringBuilder& string = data.string;
+    int32_t lengthPrefix = 0;
     int32_t length1 = 0;
-    int32_t lengthShared = 0;
+    int32_t lengthInfix = 0;
     int32_t length2 = 0;
-    #define UPRV_INDEX_0 0
-    #define UPRV_INDEX_1 length1
-    #define UPRV_INDEX_2 length1 + lengthShared
-    #define UPRV_INDEX_3 length1 + lengthShared + length2
+    int32_t lengthSuffix = 0;
+    #define UPRV_INDEX_0 (lengthPrefix)
+    #define UPRV_INDEX_1 (lengthPrefix + length1)
+    #define UPRV_INDEX_2 (lengthPrefix + length1 + lengthInfix)
+    #define UPRV_INDEX_3 (lengthPrefix + length1 + lengthInfix + length2)
+
+    int32_t lengthRange = SimpleModifier::formatTwoArgPattern(
+        fRangeFormatter,
+        string,
+        0,
+        &lengthPrefix,
+        &lengthSuffix,
+        UNUM_FIELD_COUNT,
+        status);
+    if (U_FAILURE(status)) { return; }
+    lengthInfix = lengthRange - lengthPrefix - lengthSuffix;
 
-    // TODO: Use localized pattern
-    lengthShared += string.insert(UPRV_INDEX_0, u" --- ", UNUM_FIELD_COUNT, status);
     length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status);
     length2 += NumberFormatterImpl::writeNumber(micros2, data.quantity2, string, UPRV_INDEX_2, status);
 
     // TODO: Support padding?
 
     if (collapseInner) {
-        lengthShared += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+        lengthInfix += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
     } else {
         length1 += micros1.modInner->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
         length2 += micros1.modInner->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
     }
 
     if (collapseMiddle) {
-        lengthShared += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
+        lengthInfix += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_3, status);
     } else {
         length1 += micros1.modMiddle->apply(string, UPRV_INDEX_0, UPRV_INDEX_1, status);
         length2 += micros1.modMiddle->apply(string, UPRV_INDEX_2, UPRV_INDEX_3, status);
index 8a478abe29a17564cec59810b794382619b9727c..5e08fad581ca11c96ed2888c654d9a8083c25383 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "unicode/numberformatter.h"
 #include "unicode/numberrangeformatter.h"
+#include "unicode/simpleformatter.h"
 #include "number_types.h"
 #include "number_decimalquantity.h"
 #include "number_formatimpl.h"
@@ -43,6 +44,12 @@ struct UFormattedNumberRangeData : public UMemory {
 };
 
 
+struct NumberRangeData {
+    SimpleFormatter rangePattern;
+    SimpleFormatter approximatelyPattern;
+};
+
+
 class NumberRangeFormatterImpl : public UMemory {
   public:
     NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status);
@@ -57,6 +64,9 @@ class NumberRangeFormatterImpl : public UMemory {
     UNumberRangeCollapse fCollapse;
     UNumberRangeIdentityFallback fIdentityFallback;
 
+    SimpleFormatter fRangeFormatter;
+    SimpleModifier fApproximatelyModifier;
+
     void formatSingleValue(UFormattedNumberRangeData& data,
                            MicroProps& micros1, MicroProps& micros2,
                            UErrorCode& status) const;