]> granicus.if.org Git - icu/commitdiff
ICU-13597 Revising UNumberFormatter according to feedback: (1) adds LocalPointer...
authorShane Carr <shane@unicode.org>
Wed, 18 Apr 2018 09:42:05 +0000 (09:42 +0000)
committerShane Carr <shane@unicode.org>
Wed, 18 Apr 2018 09:42:05 +0000 (09:42 +0000)
X-SVN-Rev: 41244

icu4c/source/i18n/number_capi.cpp
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/test/cintltst/unumberformattertst.c
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp

index f40b7dad5cbb35bae9545c577e3172beb7903820..6160f45eebebbb7ff6691740e629e1183b618e98 100644 (file)
@@ -150,9 +150,9 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
     const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
     if (U_FAILURE(*ec)) { return 0; }
 
-    if (buffer == nullptr) {
-        // Return the length without setting an error.
-        return result->string.length();
+    if (buffer == nullptr ? bufferCapacity != 0 : bufferCapacity < 0) {
+        *ec = U_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
     }
 
     return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec);
@@ -188,7 +188,7 @@ unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator
 }
 
 U_CAPI void U_EXPORT2
-unumf_closeResult(const UFormattedNumber* uresult) {
+unumf_closeResult(UFormattedNumber* uresult) {
     UErrorCode localStatus = U_ZERO_ERROR;
     const UFormattedNumberData* impl = UFormattedNumberData::validate(uresult, localStatus);
     if (U_FAILURE(localStatus)) { return; }
index d0bb64c05f767a05404605074e8475a8016533b0..ebd0585b33e8b1cfd601440e23ce557e310f2693 100644 (file)
  * unumf_formatDouble(uformatter, 5142.3, uresult, &ec);
  * if (U_FAILURE(ec)) { return; }
  *
- * // Export the string:
+ * // Export the string to a malloc'd buffer:
  * int32_t len = unumf_resultToString(uresult, NULL, 0, &ec);
+ * // at this point, ec == U_BUFFER_OVERFLOW_ERROR
+ * ec = U_ZERO_ERROR;
  * UChar* buffer = (UChar*) malloc((len+1)*sizeof(UChar));
  * unumf_resultToString(uresult, buffer, len+1, &ec);
  * if (U_FAILURE(ec)) { return; }
+ * // buffer should equal "5,142"
  *
  * // Cleanup:
  * unumf_close(uformatter);
  * unumf_closeResult(uresult);
  * free(buffer);
  * </pre>
+ *
+ * If you are a C++ user linking against the C libraries, you can use the LocalPointer versions of these
+ * APIs. The following example uses LocalPointer with the decimal number and field position APIs:
+ *
+ * <pre>
+ * // Setup:
+ * LocalUNumberFormatterPointer uformatter(unumf_openFromSkeletonAndLocale(u"percent", -1, "en", &ec));
+ * LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a decimal number:
+ * unumf_formatDecimal(uformatter.getAlias(), "9.87E6", -1, uresult.getAlias(), &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Get the location of the percent sign:
+ * UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
+ * unumf_resultGetField(uresult.getAlias(), &ufpos, &ec);
+ * // ufpos should contain beginIndex=7 and endIndex=8 since the string is "0.00987%"
+ *
+ * // No need to do any cleanup since we are using LocalPointer.
+ * </pre>
  */
 
 
@@ -373,6 +397,7 @@ typedef enum UNumberDecimalSeparatorDisplay {
  *
  * @draft ICU 62
  */
+struct UNumberFormatter;
 typedef struct UNumberFormatter UNumberFormatter;
 
 
@@ -383,6 +408,7 @@ typedef struct UNumberFormatter UNumberFormatter;
  *
  * @draft ICU 62
  */
+struct UFormattedNumber;
 typedef struct UFormattedNumber UFormattedNumber;
 
 
@@ -395,6 +421,10 @@ typedef struct UFormattedNumber UFormattedNumber;
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param skeleton The skeleton string, like u"percent round-integer"
+ * @param skeletonLen The number of UChars in the skeleton string, or -1 it it is NUL-terminated.
+ * @param locale The NUL-terminated locale ID.
+ * @param ec Set if an error occurs.
  * @draft ICU 62
  */
 U_DRAFT UNumberFormatter* U_EXPORT2
@@ -407,6 +437,7 @@ unumf_openFromSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, cons
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param ec Set if an error occurs.
  * @draft ICU 62
  */
 U_DRAFT UFormattedNumber* U_EXPORT2
@@ -419,6 +450,10 @@ unumf_openResult(UErrorCode* ec);
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uformatter A formatter object created by unumf_openFromSkeletonAndLocale or similar.
+ * @param value The number to be formatted.
+ * @param uresult The object that will be mutated to store the result; see unumf_openResult.
+ * @param ec Set if an error occurs.
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
@@ -432,6 +467,10 @@ unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNum
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uformatter A formatter object created by unumf_openFromSkeletonAndLocale or similar.
+ * @param value The number to be formatted.
+ * @param uresult The object that will be mutated to store the result; see unumf_openResult.
+ * @param ec Set if an error occurs.
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
@@ -448,6 +487,11 @@ unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedN
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uformatter A formatter object created by unumf_openFromSkeletonAndLocale or similar.
+ * @param value The numeric string to be formatted.
+ * @param valueLen The length of the numeric string, or -1 if it is NUL-terminated.
+ * @param uresult The object that will be mutated to store the result; see unumf_openResult.
+ * @param ec Set if an error occurs.
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
@@ -460,12 +504,13 @@ unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32
  * If bufferCapacity is greater than the required length, a terminating NUL is written.
  * If bufferCapacity is less than the required length, an error code is set.
  *
- * If NULL is passed as the buffer argument, the required length is returned without setting an error.
- *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uresult The object containing the formatted number.
+ * @param buffer Where to save the string output.
+ * @param bufferCapacity The number of UChars available in the buffer.
+ * @param ec Set if an error occurs.
  * @return The required length.
- *
  * @draft ICU 62
  */
 U_DRAFT int32_t U_EXPORT2
@@ -481,10 +526,13 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
  * only ever return the first occurrence. Use unumf_resultGetAllFields() to access all occurrences of an
  * attribute.
  *
+ * @param uresult The object containing the formatted number.
  * @param fpos
  *         A pointer to a UFieldPosition. On input, position->field is read. On output,
  *         position->beginIndex and position->endIndex indicate the beginning and ending indices of field
  *         number position->field, if such a field exists.
+ * @param ec Set if an error occurs.
+ * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
 unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
@@ -496,6 +544,7 @@ unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UEr
  *
  * If you need information on only one field, consider using unumf_resultGetField().
  *
+ * @param uresult The object containing the formatted number.
  * @param fpositer
  *         A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration
  *         information already present in the UFieldPositionIterator is deleted, and the iterator is reset
@@ -504,6 +553,8 @@ unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UEr
  *         the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot
  *         overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a
  *         grouping separator field for ',' and an integer field encompassing the entire string.
+ * @param ec Set if an error occurs.
+ * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
 unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
@@ -515,6 +566,7 @@ unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uformatter An object created by unumf_openFromSkeletonAndLocale().
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
@@ -526,10 +578,52 @@ unumf_close(UNumberFormatter* uformatter);
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
+ * @param uresult An object created by unumf_openResult().
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
-unumf_closeResult(const UFormattedNumber* uresult);
+unumf_closeResult(UFormattedNumber* uresult);
+
+
+#if U_SHOW_CPLUSPLUS_API
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUNumberFormatterPointer
+ * "Smart pointer" class; closes a UNumberFormatter via unumf_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUNumberFormatterPointer uformatter(unumf_openFromSkeletonAndLocale(...));
+ * // no need to explicitly call unumf_close()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 62
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberFormatterPointer, UNumberFormatter, unumf_close);
+
+/**
+ * \class LocalUNumberFormatterPointer
+ * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ * <pre>
+ * LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
+ * // no need to explicitly call unumf_closeResult()
+ * </pre>
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 62
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult);
+
+U_NAMESPACE_END
+#endif // U_SHOW_CPLUSPLUS_API
 
 
 #endif //__UNUMBERFORMATTER_H__
index 0e6aa54e19b3337666bcf8e8ef16569fc90b0e67..47a389e664874a784e970351be7cacc392c21869 100644 (file)
@@ -147,8 +147,10 @@ static void TestExampleCode() {
     unumf_formatDouble(uformatter, 5142.3, uresult, &ec);
     assertSuccess("There should not be a failure in the example code", &ec);
 
-    // Export the string:
+    // Export the string to a malloc'd buffer:
     int32_t len = unumf_resultToString(uresult, NULL, 0, &ec);
+    assertTrue("No buffer yet", ec == U_BUFFER_OVERFLOW_ERROR);
+    ec = U_ZERO_ERROR;
     UChar* buffer = (UChar*) uprv_malloc((len+1)*sizeof(UChar));
     unumf_resultToString(uresult, buffer, len+1, &ec);
     assertSuccess("There should not be a failure in the example code", &ec);
index 594788287b8b2479fd0214c4a0e24abcf2355a3b..d1a5defdb65460521b7e41fb1e4bf9ce242533b1 100644 (file)
@@ -70,6 +70,7 @@ class NumberFormatterApiTest : public IntlTest {
     void errors();
     void validRanges();
     void copyMove();
+    void localPointerCAPI();
 
     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
 
index d81a1cf1333661415a7210c74cad613af77be093..0b22e57c4a27e908ee6fa31d2df98178e9af8ffd 100644 (file)
@@ -84,6 +84,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
         TESTCASE_AUTO(errors);
         TESTCASE_AUTO(validRanges);
         TESTCASE_AUTO(copyMove);
+        TESTCASE_AUTO(localPointerCAPI);
     TESTCASE_AUTO_END;
 }
 
@@ -2206,6 +2207,28 @@ void NumberFormatterApiTest::copyMove() {
     assertEquals("FormattedNumber move assignment", u"20%", result.toString());
 }
 
+void NumberFormatterApiTest::localPointerCAPI() {
+    // NOTE: This is also the sample code in unumberformatter.h
+    UErrorCode ec = U_ZERO_ERROR;
+
+    // Setup:
+    LocalUNumberFormatterPointer uformatter(unumf_openFromSkeletonAndLocale(u"percent", -1, "en", &ec));
+    LocalUFormattedNumberPointer uresult(unumf_openResult(&ec));
+    assertSuccess("", ec);
+
+    // Format a decimal number:
+    unumf_formatDecimal(uformatter.getAlias(), "9.87E-3", -1, uresult.getAlias(), &ec);
+    assertSuccess("", ec);
+
+    // Get the location of the percent sign:
+    UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
+    unumf_resultGetField(uresult.getAlias(), &ufpos, &ec);
+    assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
+    assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
+
+    // No need to do any cleanup since we are using LocalPointer.
+}
+
 
 void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage, const char16_t* uskeleton,
                                                     const UnlocalizedNumberFormatter& f, Locale locale,