LCOV - code coverage report
Current view: top level - intl/icu/source/i18n - measfmt.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 613 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 71 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // © 2016 and later: Unicode, Inc. and others.
       2             : // License & terms of use: http://www.unicode.org/copyright.html
       3             : /*
       4             : **********************************************************************
       5             : * Copyright (c) 2004-2016, International Business Machines
       6             : * Corporation and others.  All Rights Reserved.
       7             : **********************************************************************
       8             : * Author: Alan Liu
       9             : * Created: April 20, 2004
      10             : * Since: ICU 3.0
      11             : **********************************************************************
      12             : */
      13             : #include "utypeinfo.h"  // for 'typeid' to work
      14             : #include "unicode/utypes.h"
      15             : 
      16             : #if !UCONFIG_NO_FORMATTING
      17             : 
      18             : #include "unicode/measfmt.h"
      19             : #include "unicode/numfmt.h"
      20             : #include "currfmt.h"
      21             : #include "unicode/localpointer.h"
      22             : #include "resource.h"
      23             : #include "unicode/simpleformatter.h"
      24             : #include "quantityformatter.h"
      25             : #include "unicode/plurrule.h"
      26             : #include "unicode/decimfmt.h"
      27             : #include "uresimp.h"
      28             : #include "unicode/ures.h"
      29             : #include "unicode/ustring.h"
      30             : #include "ureslocs.h"
      31             : #include "cstring.h"
      32             : #include "mutex.h"
      33             : #include "ucln_in.h"
      34             : #include "unicode/listformatter.h"
      35             : #include "charstr.h"
      36             : #include "unicode/putil.h"
      37             : #include "unicode/smpdtfmt.h"
      38             : #include "uassert.h"
      39             : 
      40             : #include "sharednumberformat.h"
      41             : #include "sharedpluralrules.h"
      42             : #include "standardplural.h"
      43             : #include "unifiedcache.h"
      44             : 
      45             : #define MEAS_UNIT_COUNT 135
      46             : #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
      47             : 
      48             : U_NAMESPACE_BEGIN
      49             : 
      50           0 : UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
      51             : 
      52             : // Used to format durations like 5:47 or 21:35:42.
      53           0 : class NumericDateFormatters : public UMemory {
      54             : public:
      55             :     // Formats like H:mm
      56             :     SimpleDateFormat hourMinute;
      57             : 
      58             :     // formats like M:ss
      59             :     SimpleDateFormat minuteSecond;
      60             : 
      61             :     // formats like H:mm:ss
      62             :     SimpleDateFormat hourMinuteSecond;
      63             : 
      64             :     // Constructor that takes the actual patterns for hour-minute,
      65             :     // minute-second, and hour-minute-second respectively.
      66           0 :     NumericDateFormatters(
      67             :             const UnicodeString &hm,
      68             :             const UnicodeString &ms,
      69             :             const UnicodeString &hms,
      70           0 :             UErrorCode &status) : 
      71             :             hourMinute(hm, status),
      72             :             minuteSecond(ms, status), 
      73           0 :             hourMinuteSecond(hms, status) {
      74           0 :         const TimeZone *gmt = TimeZone::getGMT();
      75           0 :         hourMinute.setTimeZone(*gmt);
      76           0 :         minuteSecond.setTimeZone(*gmt);
      77           0 :         hourMinuteSecond.setTimeZone(*gmt);
      78           0 :     }
      79             : private:
      80             :     NumericDateFormatters(const NumericDateFormatters &other);
      81             :     NumericDateFormatters &operator=(const NumericDateFormatters &other);
      82             : };
      83             : 
      84           0 : static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) {
      85           0 :     if (width >= WIDTH_INDEX_COUNT) {
      86           0 :         return UMEASFMT_WIDTH_NARROW;
      87             :     }
      88           0 :     return width;
      89             : }
      90             : 
      91             : /**
      92             :  * Instances contain all MeasureFormat specific data for a particular locale.
      93             :  * This data is cached. It is never copied, but is shared via shared pointers.
      94             :  *
      95             :  * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
      96             :  * complete sets of unit & per patterns,
      97             :  * to correspond to the resource data and its aliases.
      98             :  *
      99             :  * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
     100             :  */
     101             : class MeasureFormatCacheData : public SharedObject {
     102             : public:
     103             :     static const int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
     104             :     static const int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
     105             : 
     106             :     /**
     107             :      * Redirection data from root-bundle, top-level sideways aliases.
     108             :      * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
     109             :      * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
     110             :      */
     111             :     UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
     112             :     /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
     113             :     SimpleFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT];
     114             :     const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
     115             :     SimpleFormatter perFormatters[WIDTH_INDEX_COUNT];
     116             : 
     117             :     MeasureFormatCacheData();
     118             :     virtual ~MeasureFormatCacheData();
     119             : 
     120           0 :     UBool hasPerFormatter(int32_t width) const {
     121             :         // TODO: Create a more obvious way to test if the per-formatter has been set?
     122             :         // Use pointers, check for NULL? Or add an isValid() method?
     123           0 :         return perFormatters[width].getArgumentLimit() == 2;
     124             :     }
     125             : 
     126           0 :     void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
     127           0 :         delete currencyFormats[widthIndex];
     128           0 :         currencyFormats[widthIndex] = nfToAdopt;
     129           0 :     }
     130           0 :     const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const {
     131           0 :         return currencyFormats[getRegularWidth(width)];
     132             :     }
     133           0 :     void adoptIntegerFormat(NumberFormat *nfToAdopt) {
     134           0 :         delete integerFormat;
     135           0 :         integerFormat = nfToAdopt;
     136           0 :     }
     137           0 :     const NumberFormat *getIntegerFormat() const {
     138           0 :         return integerFormat;
     139             :     }
     140           0 :     void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
     141           0 :         delete numericDateFormatters;
     142           0 :         numericDateFormatters = formattersToAdopt;
     143           0 :     }
     144           0 :     const NumericDateFormatters *getNumericDateFormatters() const {
     145           0 :         return numericDateFormatters;
     146             :     }
     147             : 
     148             : private:
     149             :     NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
     150             :     NumberFormat *integerFormat;
     151             :     NumericDateFormatters *numericDateFormatters;
     152             :     MeasureFormatCacheData(const MeasureFormatCacheData &other);
     153             :     MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
     154             : };
     155             : 
     156           0 : MeasureFormatCacheData::MeasureFormatCacheData() {
     157           0 :     for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
     158           0 :         widthFallback[i] = UMEASFMT_WIDTH_COUNT;
     159             :     }
     160           0 :     for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
     161           0 :         currencyFormats[i] = NULL;
     162             :     }
     163           0 :     uprv_memset(patterns, 0, sizeof(patterns));
     164           0 :     uprv_memset(dnams, 0, sizeof(dnams));
     165           0 :     integerFormat = NULL;
     166           0 :     numericDateFormatters = NULL;
     167           0 : }
     168             : 
     169           0 : MeasureFormatCacheData::~MeasureFormatCacheData() {
     170           0 :     for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
     171           0 :         delete currencyFormats[i];
     172             :     }
     173           0 :     for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
     174           0 :         for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
     175           0 :             for (int32_t k = 0; k < PATTERN_COUNT; ++k) {
     176           0 :                 delete patterns[i][j][k];
     177             :             }
     178             :         }
     179             :     }
     180             :     // Note: the contents of 'dnams' are pointers into the resource bundle
     181           0 :     delete integerFormat;
     182           0 :     delete numericDateFormatters;
     183           0 : }
     184             : 
     185           0 : static UBool isCurrency(const MeasureUnit &unit) {
     186           0 :     return (uprv_strcmp(unit.getType(), "currency") == 0);
     187             : }
     188             : 
     189           0 : static UBool getString(
     190             :         const UResourceBundle *resource,
     191             :         UnicodeString &result,
     192             :         UErrorCode &status) {
     193           0 :     int32_t len = 0;
     194           0 :     const UChar *resStr = ures_getString(resource, &len, &status);
     195           0 :     if (U_FAILURE(status)) {
     196           0 :         return FALSE;
     197             :     }
     198           0 :     result.setTo(TRUE, resStr, len);
     199           0 :     return TRUE;
     200             : }
     201             : 
     202             : namespace {
     203             : 
     204             : static const UChar g_LOCALE_units[] = {
     205             :     0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
     206             :     0x75, 0x6E, 0x69, 0x74, 0x73
     207             : };
     208             : static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
     209             : static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
     210             : 
     211             : /**
     212             :  * Sink for enumerating all of the measurement unit display names.
     213             :  * Contains inner sink classes, each one corresponding to a type of resource table.
     214             :  * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
     215             :  *
     216             :  * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
     217             :  * Only store a value if it is still missing, that is, it has not been overridden.
     218             :  *
     219             :  * C++: Each inner sink class has a reference to the main outer sink.
     220             :  * Java: Use non-static inner classes instead.
     221             :  */
     222             : struct UnitDataSink : public ResourceSink {
     223             : 
     224             :     // Output data.
     225             :     MeasureFormatCacheData &cacheData;
     226             : 
     227             :     // Path to current data.
     228             :     UMeasureFormatWidth width;
     229             :     const char *type;
     230             :     int32_t unitIndex;
     231             : 
     232           0 :     UnitDataSink(MeasureFormatCacheData &outputData)
     233           0 :             : cacheData(outputData),
     234           0 :               width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
     235             :     ~UnitDataSink();
     236             : 
     237           0 :     void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
     238             :                                 int32_t minPlaceholders, UErrorCode &errorCode) {
     239           0 :         SimpleFormatter **patterns = &cacheData.patterns[unitIndex][width][0];
     240           0 :         if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
     241           0 :             if (minPlaceholders >= 0) {
     242           0 :                 patterns[index] = new SimpleFormatter(
     243           0 :                         value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode);
     244             :             }
     245           0 :             if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
     246           0 :                 errorCode = U_MEMORY_ALLOCATION_ERROR;
     247             :             }
     248             :         }
     249           0 :     }
     250             : 
     251           0 :     void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) {
     252           0 :         if (cacheData.dnams[unitIndex][width] == NULL) {
     253             :             int32_t length;
     254           0 :             cacheData.dnams[unitIndex][width] = value.getString(length, errorCode);
     255             :         }
     256           0 :     }
     257             : 
     258             :     /**
     259             :      * Consume a display pattern. For example,
     260             :      * unitsShort/duration/hour contains other{"{0} hrs"}.
     261             :      */
     262           0 :     void consumePattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
     263           0 :         if (U_FAILURE(errorCode)) { return; }
     264           0 :         if (uprv_strcmp(key, "dnam") == 0) {
     265             :             // The display name for the unit in the current width.
     266           0 :             setDnamIfAbsent(value, errorCode);
     267           0 :         } else if (uprv_strcmp(key, "per") == 0) {
     268             :             // For example, "{0}/h".
     269           0 :             setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode);
     270             :         } else {
     271             :             // The key must be one of the plural form strings. For example:
     272             :             // one{"{0} hr"}
     273             :             // other{"{0} hrs"}
     274           0 :             setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0,
     275           0 :                                     errorCode);
     276             :         }
     277             :     }
     278             : 
     279             :     /**
     280             :      * Consume a table of per-unit tables. For example,
     281             :      * unitsShort/duration contains tables for duration-unit subtypes day & hour.
     282             :      */
     283           0 :     void consumeSubtypeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
     284           0 :         if (U_FAILURE(errorCode)) { return; }
     285           0 :         unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(type, key);
     286           0 :         if (unitIndex < 0) {
     287             :             // TODO: How to handle unexpected data?
     288             :             // See http://bugs.icu-project.org/trac/ticket/12597
     289           0 :             return;
     290             :         }
     291             : 
     292             :         // We no longer handle units like "coordinate" here (which do not have plural variants)
     293           0 :         if (value.getType() == URES_TABLE) {
     294             :             // Units that have plural variants
     295           0 :             ResourceTable patternTableTable = value.getTable(errorCode);
     296           0 :             if (U_FAILURE(errorCode)) { return; }
     297           0 :             for (int i = 0; patternTableTable.getKeyAndValue(i, key, value); ++i) {
     298           0 :                 consumePattern(key, value, errorCode);
     299             :             }
     300             :         } else {
     301             :             // TODO: How to handle unexpected data?
     302             :             // See http://bugs.icu-project.org/trac/ticket/12597
     303           0 :             return;
     304             :         }
     305             :     }
     306             : 
     307             :     /**
     308             :      * Consume compound x-per-y display pattern. For example,
     309             :      * unitsShort/compound/per may be "{0}/{1}".
     310             :      */
     311           0 :     void consumeCompoundPattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
     312           0 :         if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) {
     313           0 :             cacheData.perFormatters[width].
     314           0 :                     applyPatternMinMaxArguments(value.getUnicodeString(errorCode), 2, 2, errorCode);
     315             :         }
     316           0 :     }
     317             : 
     318             :     /**
     319             :      * Consume a table of unit type tables. For example,
     320             :      * unitsShort contains tables for area & duration.
     321             :      * It also contains a table for the compound/per pattern.
     322             :      */
     323           0 :     void consumeUnitTypesTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
     324           0 :         if (U_FAILURE(errorCode)) { return; }
     325           0 :         if (uprv_strcmp(key, "currency") == 0) {
     326             :             // Skip.
     327           0 :         } else if (uprv_strcmp(key, "compound") == 0) {
     328           0 :             if (!cacheData.hasPerFormatter(width)) {
     329           0 :                 ResourceTable compoundTable = value.getTable(errorCode);
     330           0 :                 if (U_FAILURE(errorCode)) { return; }
     331           0 :                 for (int i = 0; compoundTable.getKeyAndValue(i, key, value); ++i) {
     332           0 :                     consumeCompoundPattern(key, value, errorCode);
     333             :                 }
     334             :             }
     335           0 :         } else if (uprv_strcmp(key, "coordinate") == 0) {
     336             :             // special handling but we need to determine what that is
     337             :         } else {
     338           0 :             type = key;
     339           0 :             ResourceTable subtypeTable = value.getTable(errorCode);
     340           0 :             if (U_FAILURE(errorCode)) { return; }
     341           0 :             for (int i = 0; subtypeTable.getKeyAndValue(i, key, value); ++i) {
     342           0 :                 consumeSubtypeTable(key, value, errorCode);
     343             :             }
     344             :         }
     345             :     }
     346             : 
     347           0 :     void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
     348             :         // Handle aliases like
     349             :         // units:alias{"/LOCALE/unitsShort"}
     350             :         // which should only occur in the root bundle.
     351           0 :         UMeasureFormatWidth sourceWidth = widthFromKey(key);
     352           0 :         if (sourceWidth == UMEASFMT_WIDTH_COUNT) {
     353             :             // Alias from something we don't care about.
     354           0 :             return;
     355             :         }
     356           0 :         UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode);
     357           0 :         if (targetWidth == UMEASFMT_WIDTH_COUNT) {
     358             :             // We do not recognize what to fall back to.
     359           0 :             errorCode = U_INVALID_FORMAT_ERROR;
     360           0 :             return;
     361             :         }
     362             :         // Check that we do not fall back to another fallback.
     363           0 :         if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) {
     364           0 :             errorCode = U_INVALID_FORMAT_ERROR;
     365           0 :             return;
     366             :         }
     367           0 :         cacheData.widthFallback[sourceWidth] = targetWidth;
     368             :     }
     369             : 
     370           0 :     void consumeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
     371           0 :         if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) {
     372           0 :             ResourceTable unitTypesTable = value.getTable(errorCode);
     373           0 :             if (U_FAILURE(errorCode)) { return; }
     374           0 :             for (int i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
     375           0 :                 consumeUnitTypesTable(key, value, errorCode);
     376             :             }
     377             :         }
     378             :     }
     379             : 
     380           0 :     static UMeasureFormatWidth widthFromKey(const char *key) {
     381           0 :         if (uprv_strncmp(key, "units", 5) == 0) {
     382           0 :             key += 5;
     383           0 :             if (*key == 0) {
     384           0 :                 return UMEASFMT_WIDTH_WIDE;
     385           0 :             } else if (uprv_strcmp(key, "Short") == 0) {
     386           0 :                 return UMEASFMT_WIDTH_SHORT;
     387           0 :             } else if (uprv_strcmp(key, "Narrow") == 0) {
     388           0 :                 return UMEASFMT_WIDTH_NARROW;
     389             :             }
     390             :         }
     391           0 :         return UMEASFMT_WIDTH_COUNT;
     392             :     }
     393             : 
     394           0 :     static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) {
     395             :         int32_t length;
     396           0 :         const UChar *s = value.getAliasString(length, errorCode);
     397             :         // For example: "/LOCALE/unitsShort"
     398           0 :         if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) {
     399           0 :             s += 13;
     400           0 :             length -= 13;
     401           0 :             if (*s == 0) {
     402           0 :                 return UMEASFMT_WIDTH_WIDE;
     403           0 :             } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) {
     404           0 :                 return UMEASFMT_WIDTH_SHORT;
     405           0 :             } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) {
     406           0 :                 return UMEASFMT_WIDTH_NARROW;
     407             :             }
     408             :         }
     409           0 :         return UMEASFMT_WIDTH_COUNT;
     410             :     }
     411             : 
     412           0 :     virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
     413             :             UErrorCode &errorCode) {
     414             :         // Main entry point to sink
     415           0 :         ResourceTable widthsTable = value.getTable(errorCode);
     416           0 :         if (U_FAILURE(errorCode)) { return; }
     417           0 :         for (int i = 0; widthsTable.getKeyAndValue(i, key, value); ++i) {
     418           0 :             if (value.getType() == URES_ALIAS) {
     419           0 :                 consumeAlias(key, value, errorCode);
     420             :             } else {
     421           0 :                 consumeTable(key, value, errorCode);
     422             :             }
     423             :         }
     424             :     }
     425             : };
     426             : 
     427             : // Virtual destructors must be defined out of line.
     428           0 : UnitDataSink::~UnitDataSink() {}
     429             : 
     430             : }  // namespace
     431             : 
     432           0 : static UBool loadMeasureUnitData(
     433             :         const UResourceBundle *resource,
     434             :         MeasureFormatCacheData &cacheData,
     435             :         UErrorCode &status) {
     436           0 :     UnitDataSink sink(cacheData);
     437           0 :     ures_getAllItemsWithFallback(resource, "", sink, status);
     438           0 :     return U_SUCCESS(status);
     439             : }
     440             : 
     441           0 : static UnicodeString loadNumericDateFormatterPattern(
     442             :         const UResourceBundle *resource,
     443             :         const char *pattern,
     444             :         UErrorCode &status) {
     445           0 :     UnicodeString result;
     446           0 :     if (U_FAILURE(status)) {
     447           0 :         return result;
     448             :     }
     449           0 :     CharString chs;
     450           0 :     chs.append("durationUnits", status)
     451           0 :             .append("/", status).append(pattern, status);
     452             :     LocalUResourceBundlePointer patternBundle(
     453             :             ures_getByKeyWithFallback(
     454             :                 resource,
     455           0 :                 chs.data(),
     456             :                 NULL,
     457           0 :                 &status));
     458           0 :     if (U_FAILURE(status)) {
     459           0 :         return result;
     460             :     }
     461           0 :     getString(patternBundle.getAlias(), result, status);
     462             :     // Replace 'h' with 'H'
     463           0 :     int32_t len = result.length();
     464           0 :     UChar *buffer = result.getBuffer(len);
     465           0 :     for (int32_t i = 0; i < len; ++i) {
     466           0 :         if (buffer[i] == 0x68) { // 'h'
     467           0 :             buffer[i] = 0x48; // 'H'
     468             :         }
     469             :     }
     470           0 :     result.releaseBuffer(len);
     471           0 :     return result;
     472             : }
     473             : 
     474           0 : static NumericDateFormatters *loadNumericDateFormatters(
     475             :         const UResourceBundle *resource,
     476             :         UErrorCode &status) {
     477           0 :     if (U_FAILURE(status)) {
     478           0 :         return NULL;
     479             :     }
     480             :     NumericDateFormatters *result = new NumericDateFormatters(
     481           0 :         loadNumericDateFormatterPattern(resource, "hm", status),
     482           0 :         loadNumericDateFormatterPattern(resource, "ms", status),
     483           0 :         loadNumericDateFormatterPattern(resource, "hms", status),
     484           0 :         status);
     485           0 :     if (U_FAILURE(status)) {
     486           0 :         delete result;
     487           0 :         return NULL;
     488             :     }
     489           0 :     return result;
     490             : }
     491             : 
     492             : template<> U_I18N_API
     493           0 : const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject(
     494             :         const void * /*unused*/, UErrorCode &status) const {
     495           0 :     const char *localeId = fLoc.getName();
     496           0 :     LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status));
     497             :     static UNumberFormatStyle currencyStyles[] = {
     498             :             UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
     499           0 :     LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status);
     500           0 :     if (U_FAILURE(status)) {
     501           0 :         return NULL;
     502             :     }
     503           0 :     if (!loadMeasureUnitData(
     504           0 :             unitsBundle.getAlias(),
     505             :             *result,
     506             :             status)) {
     507           0 :         return NULL;
     508             :     }
     509           0 :     result->adoptNumericDateFormatters(loadNumericDateFormatters(
     510           0 :             unitsBundle.getAlias(), status));
     511           0 :     if (U_FAILURE(status)) {
     512           0 :         return NULL;
     513             :     }
     514             : 
     515           0 :     for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
     516             :         // NumberFormat::createInstance can erase warning codes from status, so pass it
     517             :         // a separate status instance
     518           0 :         UErrorCode localStatus = U_ZERO_ERROR;
     519           0 :         result->adoptCurrencyFormat(i, NumberFormat::createInstance(
     520           0 :                 localeId, currencyStyles[i], localStatus));
     521           0 :         if (localStatus != U_ZERO_ERROR) {
     522           0 :             status = localStatus;
     523             :         }
     524           0 :         if (U_FAILURE(status)) {
     525           0 :             return NULL;
     526             :         }
     527             :     }
     528           0 :     NumberFormat *inf = NumberFormat::createInstance(
     529           0 :             localeId, UNUM_DECIMAL, status);
     530           0 :     if (U_FAILURE(status)) {
     531           0 :         return NULL;
     532             :     }
     533           0 :     inf->setMaximumFractionDigits(0);
     534           0 :     DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf);
     535           0 :     if (decfmt != NULL) {
     536           0 :         decfmt->setRoundingMode(DecimalFormat::kRoundDown);
     537             :     }
     538           0 :     result->adoptIntegerFormat(inf);
     539           0 :     result->addRef();
     540           0 :     return result.orphan();
     541             : }
     542             : 
     543           0 : static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
     544           0 :     return uprv_strcmp(mu.getType(), "duration") == 0 &&
     545           0 :             uprv_strcmp(mu.getSubtype(), tu) == 0;
     546             : }
     547             : 
     548             : // Converts a composite measure into hours-minutes-seconds and stores at hms
     549             : // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
     550             : // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
     551             : // contains hours-minutes, this function would return 3.
     552             : //
     553             : // If measures cannot be converted into hours, minutes, seconds or if amounts
     554             : // are negative, or if hours, minutes, seconds are out of order, returns 0.
     555           0 : static int32_t toHMS(
     556             :         const Measure *measures,
     557             :         int32_t measureCount,
     558             :         Formattable *hms,
     559             :         UErrorCode &status) {
     560           0 :     if (U_FAILURE(status)) {
     561           0 :         return 0;
     562             :     }
     563           0 :     int32_t result = 0;
     564           0 :     if (U_FAILURE(status)) {
     565           0 :         return 0;
     566             :     }
     567             :     // We use copy constructor to ensure that both sides of equality operator
     568             :     // are instances of MeasureUnit base class and not a subclass. Otherwise,
     569             :     // operator== will immediately return false.
     570           0 :     for (int32_t i = 0; i < measureCount; ++i) {
     571           0 :         if (isTimeUnit(measures[i].getUnit(), "hour")) {
     572             :             // hour must come first
     573           0 :             if (result >= 1) {
     574           0 :                 return 0;
     575             :             }
     576           0 :             hms[0] = measures[i].getNumber();
     577           0 :             if (hms[0].getDouble() < 0.0) {
     578           0 :                 return 0;
     579             :             }
     580           0 :             result |= 1;
     581           0 :         } else if (isTimeUnit(measures[i].getUnit(), "minute")) {
     582             :             // minute must come after hour
     583           0 :             if (result >= 2) {
     584           0 :                 return 0;
     585             :             }
     586           0 :             hms[1] = measures[i].getNumber();
     587           0 :             if (hms[1].getDouble() < 0.0) {
     588           0 :                 return 0;
     589             :             }
     590           0 :             result |= 2;
     591           0 :         } else if (isTimeUnit(measures[i].getUnit(), "second")) {
     592             :             // second must come after hour and minute
     593           0 :             if (result >= 4) {
     594           0 :                 return 0;
     595             :             }
     596           0 :             hms[2] = measures[i].getNumber();
     597           0 :             if (hms[2].getDouble() < 0.0) {
     598           0 :                 return 0;
     599             :             }
     600           0 :             result |= 4;
     601             :         } else {
     602           0 :             return 0;
     603             :         }
     604             :     }
     605           0 :     return result;
     606             : }
     607             : 
     608             : 
     609           0 : MeasureFormat::MeasureFormat(
     610           0 :         const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
     611             :         : cache(NULL),
     612             :           numberFormat(NULL),
     613             :           pluralRules(NULL),
     614             :           width(w),
     615           0 :           listFormatter(NULL) {
     616           0 :     initMeasureFormat(locale, w, NULL, status);
     617           0 : }
     618             : 
     619           0 : MeasureFormat::MeasureFormat(
     620             :         const Locale &locale,
     621             :         UMeasureFormatWidth w,
     622             :         NumberFormat *nfToAdopt,
     623           0 :         UErrorCode &status) 
     624             :         : cache(NULL),
     625             :           numberFormat(NULL),
     626             :           pluralRules(NULL),
     627             :           width(w),
     628           0 :           listFormatter(NULL) {
     629           0 :     initMeasureFormat(locale, w, nfToAdopt, status);
     630           0 : }
     631             : 
     632           0 : MeasureFormat::MeasureFormat(const MeasureFormat &other) :
     633             :         Format(other),
     634           0 :         cache(other.cache),
     635           0 :         numberFormat(other.numberFormat),
     636           0 :         pluralRules(other.pluralRules),
     637           0 :         width(other.width),
     638           0 :         listFormatter(NULL) {
     639           0 :     cache->addRef();
     640           0 :     numberFormat->addRef();
     641           0 :     pluralRules->addRef();
     642           0 :     if (other.listFormatter != NULL) {
     643           0 :         listFormatter = new ListFormatter(*other.listFormatter);
     644             :     }
     645           0 : }
     646             : 
     647           0 : MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
     648           0 :     if (this == &other) {
     649           0 :         return *this;
     650             :     }
     651           0 :     Format::operator=(other);
     652           0 :     SharedObject::copyPtr(other.cache, cache);
     653           0 :     SharedObject::copyPtr(other.numberFormat, numberFormat);
     654           0 :     SharedObject::copyPtr(other.pluralRules, pluralRules);
     655           0 :     width = other.width;
     656           0 :     delete listFormatter;
     657           0 :     if (other.listFormatter != NULL) {
     658           0 :         listFormatter = new ListFormatter(*other.listFormatter);
     659             :     } else {
     660           0 :         listFormatter = NULL;
     661             :     }
     662           0 :     return *this;
     663             : }
     664             : 
     665           0 : MeasureFormat::MeasureFormat() :
     666             :         cache(NULL),
     667             :         numberFormat(NULL),
     668             :         pluralRules(NULL),
     669             :         width(UMEASFMT_WIDTH_SHORT),
     670           0 :         listFormatter(NULL) {
     671           0 : }
     672             : 
     673           0 : MeasureFormat::~MeasureFormat() {
     674           0 :     if (cache != NULL) {
     675           0 :         cache->removeRef();
     676             :     }
     677           0 :     if (numberFormat != NULL) {
     678           0 :         numberFormat->removeRef();
     679             :     }
     680           0 :     if (pluralRules != NULL) {
     681           0 :         pluralRules->removeRef();
     682             :     }
     683           0 :     delete listFormatter;
     684           0 : }
     685             : 
     686           0 : UBool MeasureFormat::operator==(const Format &other) const {
     687           0 :     if (this == &other) { // Same object, equal
     688           0 :         return TRUE;
     689             :     }
     690           0 :     if (!Format::operator==(other)) {
     691           0 :         return FALSE;
     692             :     }
     693           0 :     const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other);
     694             : 
     695             :     // Note: Since the ListFormatter depends only on Locale and width, we
     696             :     // don't have to check it here.
     697             : 
     698             :     // differing widths aren't equivalent
     699           0 :     if (width != rhs.width) {
     700           0 :         return FALSE;
     701             :     }
     702             :     // Width the same check locales.
     703             :     // We don't need to check locales if both objects have same cache.
     704           0 :     if (cache != rhs.cache) {
     705           0 :         UErrorCode status = U_ZERO_ERROR;
     706           0 :         const char *localeId = getLocaleID(status);
     707           0 :         const char *rhsLocaleId = rhs.getLocaleID(status);
     708           0 :         if (U_FAILURE(status)) {
     709             :             // On failure, assume not equal
     710           0 :             return FALSE;
     711             :         }
     712           0 :         if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
     713           0 :             return FALSE;
     714             :         }
     715             :     }
     716             :     // Locales same, check NumberFormat if shared data differs.
     717             :     return (
     718           0 :             numberFormat == rhs.numberFormat ||
     719           0 :             **numberFormat == **rhs.numberFormat);
     720             : }
     721             : 
     722           0 : Format *MeasureFormat::clone() const {
     723           0 :     return new MeasureFormat(*this);
     724             : }
     725             : 
     726           0 : UnicodeString &MeasureFormat::format(
     727             :         const Formattable &obj,
     728             :         UnicodeString &appendTo,
     729             :         FieldPosition &pos,
     730             :         UErrorCode &status) const {
     731           0 :     if (U_FAILURE(status)) return appendTo;
     732           0 :     if (obj.getType() == Formattable::kObject) {
     733           0 :         const UObject* formatObj = obj.getObject();
     734           0 :         const Measure* amount = dynamic_cast<const Measure*>(formatObj);
     735           0 :         if (amount != NULL) {
     736             :             return formatMeasure(
     737           0 :                     *amount, **numberFormat, appendTo, pos, status);
     738             :         }
     739             :     }
     740           0 :     status = U_ILLEGAL_ARGUMENT_ERROR;
     741           0 :     return appendTo;
     742             : }
     743             : 
     744           0 : void MeasureFormat::parseObject(
     745             :         const UnicodeString & /*source*/,
     746             :         Formattable & /*result*/,
     747             :         ParsePosition& /*pos*/) const {
     748           0 :     return;
     749             : }
     750             : 
     751           0 : UnicodeString &MeasureFormat::formatMeasurePerUnit(
     752             :         const Measure &measure,
     753             :         const MeasureUnit &perUnit,
     754             :         UnicodeString &appendTo,
     755             :         FieldPosition &pos,
     756             :         UErrorCode &status) const {
     757           0 :     if (U_FAILURE(status)) {
     758           0 :         return appendTo;
     759             :     }
     760             :     MeasureUnit *resolvedUnit =
     761           0 :             MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit);
     762           0 :     if (resolvedUnit != NULL) {
     763           0 :         Measure newMeasure(measure.getNumber(), resolvedUnit, status);
     764             :         return formatMeasure(
     765           0 :                 newMeasure, **numberFormat, appendTo, pos, status);
     766             :     }
     767           0 :     FieldPosition fpos(pos.getField());
     768           0 :     UnicodeString result;
     769             :     int32_t offset = withPerUnitAndAppend(
     770             :             formatMeasure(
     771           0 :                     measure, **numberFormat, result, fpos, status),
     772             :             perUnit,
     773             :             appendTo,
     774           0 :             status);
     775           0 :     if (U_FAILURE(status)) {
     776           0 :         return appendTo;
     777             :     }
     778           0 :     if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
     779           0 :         pos.setBeginIndex(fpos.getBeginIndex() + offset);
     780           0 :         pos.setEndIndex(fpos.getEndIndex() + offset);
     781             :     }
     782           0 :     return appendTo;
     783             : }
     784             : 
     785           0 : UnicodeString &MeasureFormat::formatMeasures(
     786             :         const Measure *measures,
     787             :         int32_t measureCount,
     788             :         UnicodeString &appendTo,
     789             :         FieldPosition &pos,
     790             :         UErrorCode &status) const {
     791           0 :     if (U_FAILURE(status)) {
     792           0 :         return appendTo;
     793             :     }
     794           0 :     if (measureCount == 0) {
     795           0 :         return appendTo;
     796             :     }
     797           0 :     if (measureCount == 1) {
     798           0 :         return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
     799             :     }
     800           0 :     if (width == UMEASFMT_WIDTH_NUMERIC) {
     801           0 :         Formattable hms[3];
     802           0 :         int32_t bitMap = toHMS(measures, measureCount, hms, status);
     803           0 :         if (bitMap > 0) {
     804           0 :             return formatNumeric(hms, bitMap, appendTo, status);
     805             :         }
     806             :     }
     807           0 :     if (pos.getField() != FieldPosition::DONT_CARE) {
     808             :         return formatMeasuresSlowTrack(
     809           0 :                 measures, measureCount, appendTo, pos, status);
     810             :     }
     811           0 :     UnicodeString *results = new UnicodeString[measureCount];
     812           0 :     if (results == NULL) {
     813           0 :         status = U_MEMORY_ALLOCATION_ERROR;
     814           0 :         return appendTo;
     815             :     }
     816           0 :     for (int32_t i = 0; i < measureCount; ++i) {
     817           0 :         const NumberFormat *nf = cache->getIntegerFormat();
     818           0 :         if (i == measureCount - 1) {
     819           0 :             nf = numberFormat->get();
     820             :         }
     821             :         formatMeasure(
     822           0 :                 measures[i],
     823             :                 *nf,
     824           0 :                 results[i],
     825             :                 pos,
     826           0 :                 status);
     827             :     }
     828           0 :     listFormatter->format(results, measureCount, appendTo, status);
     829           0 :     delete [] results;
     830           0 :     return appendTo;
     831             : }
     832             : 
     833           0 : UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const {
     834           0 :     UMeasureFormatWidth width = getRegularWidth(this->width);
     835           0 :     const UChar* const* styleToDnam = cache->dnams[unit.getIndex()];
     836           0 :     const UChar* dnam = styleToDnam[width];
     837           0 :     if (dnam == NULL) {
     838           0 :         int32_t fallbackWidth = cache->widthFallback[width];
     839           0 :         dnam = styleToDnam[fallbackWidth];
     840             :     }
     841             : 
     842           0 :     UnicodeString result;
     843           0 :     if (dnam == NULL) {
     844           0 :         result.setToBogus();
     845             :     } else {
     846           0 :         result.setTo(dnam, -1);
     847             :     }
     848           0 :     return result;
     849             : }
     850             : 
     851           0 : void MeasureFormat::initMeasureFormat(
     852             :         const Locale &locale,
     853             :         UMeasureFormatWidth w,
     854             :         NumberFormat *nfToAdopt,
     855             :         UErrorCode &status) {
     856             :     static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
     857           0 :     LocalPointer<NumberFormat> nf(nfToAdopt);
     858           0 :     if (U_FAILURE(status)) {
     859           0 :         return;
     860             :     }
     861           0 :     const char *name = locale.getName();
     862           0 :     setLocaleIDs(name, name);
     863             : 
     864           0 :     UnifiedCache::getByLocale(locale, cache, status);
     865           0 :     if (U_FAILURE(status)) {
     866           0 :         return;
     867             :     }
     868             : 
     869             :     const SharedPluralRules *pr = PluralRules::createSharedInstance(
     870           0 :             locale, UPLURAL_TYPE_CARDINAL, status);
     871           0 :     if (U_FAILURE(status)) {
     872           0 :         return;
     873             :     }
     874           0 :     SharedObject::copyPtr(pr, pluralRules);
     875           0 :     pr->removeRef();
     876           0 :     if (nf.isNull()) {
     877             :         const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
     878           0 :                 locale, UNUM_DECIMAL, status);
     879           0 :         if (U_FAILURE(status)) {
     880           0 :             return;
     881             :         }
     882           0 :         SharedObject::copyPtr(shared, numberFormat);
     883           0 :         shared->removeRef();
     884             :     } else {
     885           0 :         adoptNumberFormat(nf.orphan(), status);
     886           0 :         if (U_FAILURE(status)) {
     887           0 :             return;
     888             :         }
     889             :     }
     890           0 :     width = w;
     891           0 :     delete listFormatter;
     892           0 :     listFormatter = ListFormatter::createInstance(
     893             :             locale,
     894           0 :             listStyles[getRegularWidth(width)],
     895             :             status);
     896             : }
     897             : 
     898           0 : void MeasureFormat::adoptNumberFormat(
     899             :         NumberFormat *nfToAdopt, UErrorCode &status) {
     900           0 :     LocalPointer<NumberFormat> nf(nfToAdopt);
     901           0 :     if (U_FAILURE(status)) {
     902           0 :         return;
     903             :     }
     904           0 :     SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
     905           0 :     if (shared == NULL) {
     906           0 :         status = U_MEMORY_ALLOCATION_ERROR;
     907           0 :         return;
     908             :     }
     909           0 :     nf.orphan();
     910           0 :     SharedObject::copyPtr(shared, numberFormat);
     911             : }
     912             : 
     913           0 : UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
     914           0 :     if (U_FAILURE(status) || locale == getLocale(status)) {
     915           0 :         return FALSE;
     916             :     }
     917           0 :     initMeasureFormat(locale, width, NULL, status);
     918           0 :     return U_SUCCESS(status);
     919             : } 
     920             : 
     921           0 : const NumberFormat &MeasureFormat::getNumberFormat() const {
     922           0 :     return **numberFormat;
     923             : }
     924             : 
     925           0 : const PluralRules &MeasureFormat::getPluralRules() const {
     926           0 :     return **pluralRules;
     927             : }
     928             : 
     929           0 : Locale MeasureFormat::getLocale(UErrorCode &status) const {
     930           0 :     return Format::getLocale(ULOC_VALID_LOCALE, status);
     931             : }
     932             : 
     933           0 : const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
     934           0 :     return Format::getLocaleID(ULOC_VALID_LOCALE, status);
     935             : }
     936             : 
     937           0 : UnicodeString &MeasureFormat::formatMeasure(
     938             :         const Measure &measure,
     939             :         const NumberFormat &nf,
     940             :         UnicodeString &appendTo,
     941             :         FieldPosition &pos,
     942             :         UErrorCode &status) const {
     943           0 :     if (U_FAILURE(status)) {
     944           0 :         return appendTo;
     945             :     }
     946           0 :     const Formattable& amtNumber = measure.getNumber();
     947           0 :     const MeasureUnit& amtUnit = measure.getUnit();
     948           0 :     if (isCurrency(amtUnit)) {
     949             :         UChar isoCode[4];
     950           0 :         u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
     951           0 :         return cache->getCurrencyFormat(width)->format(
     952           0 :                 new CurrencyAmount(amtNumber, isoCode, status),
     953             :                 appendTo,
     954             :                 pos,
     955           0 :                 status);
     956             :     }
     957           0 :     UnicodeString formattedNumber;
     958           0 :     StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
     959           0 :             amtNumber, nf, **pluralRules, formattedNumber, pos, status);
     960           0 :     const SimpleFormatter *formatter = getPluralFormatter(amtUnit, width, pluralForm, status);
     961           0 :     return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status);
     962             : }
     963             : 
     964             : // Formats hours-minutes-seconds as 5:37:23 or similar.
     965           0 : UnicodeString &MeasureFormat::formatNumeric(
     966             :         const Formattable *hms,  // always length 3
     967             :         int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
     968             :         UnicodeString &appendTo,
     969             :         UErrorCode &status) const {
     970           0 :     if (U_FAILURE(status)) {
     971           0 :         return appendTo;
     972             :     }
     973             :     UDate millis = 
     974           0 :         (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
     975           0 :              + uprv_trunc(hms[1].getDouble(status))) * 60.0
     976           0 :                   + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
     977           0 :     switch (bitMap) {
     978             :     case 5: // hs
     979             :     case 7: // hms
     980             :         return formatNumeric(
     981             :                 millis,
     982           0 :                 cache->getNumericDateFormatters()->hourMinuteSecond,
     983             :                 UDAT_SECOND_FIELD,
     984           0 :                 hms[2],
     985             :                 appendTo,
     986           0 :                 status);
     987             :         break;
     988             :     case 6: // ms
     989             :         return formatNumeric(
     990             :                 millis,
     991           0 :                 cache->getNumericDateFormatters()->minuteSecond,
     992             :                 UDAT_SECOND_FIELD,
     993           0 :                 hms[2],
     994             :                 appendTo,
     995           0 :                 status);
     996             :         break;
     997             :     case 3: // hm
     998             :         return formatNumeric(
     999             :                 millis,
    1000           0 :                 cache->getNumericDateFormatters()->hourMinute,
    1001             :                 UDAT_MINUTE_FIELD,
    1002           0 :                 hms[1],
    1003             :                 appendTo,
    1004           0 :                 status);
    1005             :         break;
    1006             :     default:
    1007           0 :         status = U_INTERNAL_PROGRAM_ERROR;
    1008           0 :         return appendTo;
    1009             :         break;
    1010             :     }
    1011             :     return appendTo;
    1012             : }
    1013             : 
    1014           0 : static void appendRange(
    1015             :         const UnicodeString &src,
    1016             :         int32_t start,
    1017             :         int32_t end,
    1018             :         UnicodeString &dest) {
    1019           0 :     dest.append(src, start, end - start);
    1020           0 : }
    1021             : 
    1022           0 : static void appendRange(
    1023             :         const UnicodeString &src,
    1024             :         int32_t end,
    1025             :         UnicodeString &dest) {
    1026           0 :     dest.append(src, end, src.length() - end);
    1027           0 : }
    1028             : 
    1029             : // Formats time like 5:37:23
    1030           0 : UnicodeString &MeasureFormat::formatNumeric(
    1031             :         UDate date, // Time since epoch 1:30:00 would be 5400000
    1032             :         const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss
    1033             :         UDateFormatField smallestField, // seconds in 5:37:23.5
    1034             :         const Formattable &smallestAmount, // 23.5 for 5:37:23.5
    1035             :         UnicodeString &appendTo,
    1036             :         UErrorCode &status) const {
    1037           0 :     if (U_FAILURE(status)) {
    1038           0 :         return appendTo;
    1039             :     }
    1040             :     // Format the smallest amount with this object's NumberFormat
    1041           0 :     UnicodeString smallestAmountFormatted;
    1042             : 
    1043             :     // We keep track of the integer part of smallest amount so that
    1044             :     // we can replace it later so that we get '0:00:09.3' instead of
    1045             :     // '0:00:9.3'
    1046           0 :     FieldPosition intFieldPosition(UNUM_INTEGER_FIELD);
    1047           0 :     (*numberFormat)->format(
    1048           0 :             smallestAmount, smallestAmountFormatted, intFieldPosition, status);
    1049           0 :     if (
    1050           0 :             intFieldPosition.getBeginIndex() == 0 &&
    1051           0 :             intFieldPosition.getEndIndex() == 0) {
    1052           0 :         status = U_INTERNAL_PROGRAM_ERROR;
    1053           0 :         return appendTo;
    1054             :     }
    1055             : 
    1056             :     // Format time. draft becomes something like '5:30:45'
    1057           0 :     FieldPosition smallestFieldPosition(smallestField);
    1058           0 :     UnicodeString draft;
    1059           0 :     dateFmt.format(date, draft, smallestFieldPosition, status);
    1060             : 
    1061             :     // If we find field for smallest amount replace it with the formatted
    1062             :     // smallest amount from above taking care to replace the integer part
    1063             :     // with what is in original time. For example, If smallest amount
    1064             :     // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
    1065             :     // and replacing yields 0:00:09.35
    1066           0 :     if (smallestFieldPosition.getBeginIndex() != 0 ||
    1067           0 :             smallestFieldPosition.getEndIndex() != 0) {
    1068           0 :         appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
    1069           0 :         appendRange(
    1070             :                 smallestAmountFormatted,
    1071             :                 0,
    1072             :                 intFieldPosition.getBeginIndex(),
    1073           0 :                 appendTo);
    1074           0 :         appendRange(
    1075             :                 draft,
    1076             :                 smallestFieldPosition.getBeginIndex(),
    1077             :                 smallestFieldPosition.getEndIndex(),
    1078           0 :                 appendTo);
    1079           0 :         appendRange(
    1080             :                 smallestAmountFormatted,
    1081             :                 intFieldPosition.getEndIndex(),
    1082           0 :                 appendTo);
    1083           0 :         appendRange(
    1084             :                 draft,
    1085             :                 smallestFieldPosition.getEndIndex(),
    1086           0 :                 appendTo);
    1087             :     } else {
    1088           0 :         appendTo.append(draft);
    1089             :     }
    1090           0 :     return appendTo;
    1091             : }
    1092             : 
    1093           0 : const SimpleFormatter *MeasureFormat::getFormatterOrNull(
    1094             :         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const {
    1095           0 :     width = getRegularWidth(width);
    1096             :     SimpleFormatter *const (*unitPatterns)[MeasureFormatCacheData::PATTERN_COUNT] =
    1097           0 :             &cache->patterns[unit.getIndex()][0];
    1098           0 :     if (unitPatterns[width][index] != NULL) {
    1099           0 :         return unitPatterns[width][index];
    1100             :     }
    1101           0 :     int32_t fallbackWidth = cache->widthFallback[width];
    1102           0 :     if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) {
    1103           0 :         return unitPatterns[fallbackWidth][index];
    1104             :     }
    1105           0 :     return NULL;
    1106             : }
    1107             : 
    1108           0 : const SimpleFormatter *MeasureFormat::getFormatter(
    1109             :         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
    1110             :         UErrorCode &errorCode) const {
    1111           0 :     if (U_FAILURE(errorCode)) {
    1112           0 :         return NULL;
    1113             :     }
    1114           0 :     const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
    1115           0 :     if (pattern == NULL) {
    1116           0 :         errorCode = U_MISSING_RESOURCE_ERROR;
    1117             :     }
    1118           0 :     return pattern;
    1119             : }
    1120             : 
    1121           0 : const SimpleFormatter *MeasureFormat::getPluralFormatter(
    1122             :         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
    1123             :         UErrorCode &errorCode) const {
    1124           0 :     if (U_FAILURE(errorCode)) {
    1125           0 :         return NULL;
    1126             :     }
    1127           0 :     if (index != StandardPlural::OTHER) {
    1128           0 :         const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
    1129           0 :         if (pattern != NULL) {
    1130           0 :             return pattern;
    1131             :         }
    1132             :     }
    1133           0 :     return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
    1134             : }
    1135             : 
    1136           0 : const SimpleFormatter *MeasureFormat::getPerFormatter(
    1137             :         UMeasureFormatWidth width,
    1138             :         UErrorCode &status) const {
    1139           0 :     if (U_FAILURE(status)) {
    1140           0 :         return NULL;
    1141             :     }
    1142           0 :     width = getRegularWidth(width);
    1143           0 :     const SimpleFormatter * perFormatters = cache->perFormatters;
    1144           0 :     if (perFormatters[width].getArgumentLimit() == 2) {
    1145           0 :         return &perFormatters[width];
    1146             :     }
    1147           0 :     int32_t fallbackWidth = cache->widthFallback[width];
    1148           0 :     if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
    1149           0 :             perFormatters[fallbackWidth].getArgumentLimit() == 2) {
    1150           0 :         return &perFormatters[fallbackWidth];
    1151             :     }
    1152           0 :     status = U_MISSING_RESOURCE_ERROR;
    1153           0 :     return NULL;
    1154             : }
    1155             : 
    1156           0 : int32_t MeasureFormat::withPerUnitAndAppend(
    1157             :         const UnicodeString &formatted,
    1158             :         const MeasureUnit &perUnit,
    1159             :         UnicodeString &appendTo,
    1160             :         UErrorCode &status) const {
    1161           0 :     int32_t offset = -1;
    1162           0 :     if (U_FAILURE(status)) {
    1163           0 :         return offset;
    1164             :     }
    1165             :     const SimpleFormatter *perUnitFormatter =
    1166           0 :             getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT_INDEX);
    1167           0 :     if (perUnitFormatter != NULL) {
    1168           0 :         const UnicodeString *params[] = {&formatted};
    1169             :         perUnitFormatter->formatAndAppend(
    1170             :                 params,
    1171             :                 UPRV_LENGTHOF(params),
    1172             :                 appendTo,
    1173             :                 &offset,
    1174             :                 1,
    1175           0 :                 status);
    1176           0 :         return offset;
    1177             :     }
    1178           0 :     const SimpleFormatter *perFormatter = getPerFormatter(width, status);
    1179             :     const SimpleFormatter *pattern =
    1180           0 :             getPluralFormatter(perUnit, width, StandardPlural::ONE, status);
    1181           0 :     if (U_FAILURE(status)) {
    1182           0 :         return offset;
    1183             :     }
    1184           0 :     UnicodeString perUnitString = pattern->getTextWithNoArguments();
    1185           0 :     perUnitString.trim();
    1186           0 :     const UnicodeString *params[] = {&formatted, &perUnitString};
    1187             :     perFormatter->formatAndAppend(
    1188             :             params,
    1189             :             UPRV_LENGTHOF(params),
    1190             :             appendTo,
    1191             :             &offset,
    1192             :             1,
    1193           0 :             status);
    1194           0 :     return offset;
    1195             : }
    1196             : 
    1197           0 : UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
    1198             :         const Measure *measures,
    1199             :         int32_t measureCount,
    1200             :         UnicodeString& appendTo,
    1201             :         FieldPosition& pos,
    1202             :         UErrorCode& status) const {
    1203           0 :     if (U_FAILURE(status)) {
    1204           0 :         return appendTo;
    1205             :     }
    1206           0 :     FieldPosition dontCare(FieldPosition::DONT_CARE);
    1207           0 :     FieldPosition fpos(pos.getField());
    1208           0 :     UnicodeString *results = new UnicodeString[measureCount];
    1209           0 :     int32_t fieldPositionFoundIndex = -1;
    1210           0 :     for (int32_t i = 0; i < measureCount; ++i) {
    1211           0 :         const NumberFormat *nf = cache->getIntegerFormat();
    1212           0 :         if (i == measureCount - 1) {
    1213           0 :             nf = numberFormat->get();
    1214             :         }
    1215           0 :         if (fieldPositionFoundIndex == -1) {
    1216           0 :             formatMeasure(measures[i], *nf, results[i], fpos, status);
    1217           0 :             if (U_FAILURE(status)) {
    1218           0 :                 delete [] results;
    1219           0 :                 return appendTo;
    1220             :             }
    1221           0 :             if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
    1222           0 :                 fieldPositionFoundIndex = i;
    1223             :             }
    1224             :         } else {
    1225           0 :             formatMeasure(measures[i], *nf, results[i], dontCare, status);
    1226             :         }
    1227             :     }
    1228             :     int32_t offset;
    1229           0 :     listFormatter->format(
    1230             :             results,
    1231             :             measureCount,
    1232             :             appendTo,
    1233             :             fieldPositionFoundIndex,
    1234             :             offset,
    1235           0 :             status);
    1236           0 :     if (U_FAILURE(status)) {
    1237           0 :         delete [] results;
    1238           0 :         return appendTo;
    1239             :     }
    1240           0 :     if (offset != -1) {
    1241           0 :         pos.setBeginIndex(fpos.getBeginIndex() + offset);
    1242           0 :         pos.setEndIndex(fpos.getEndIndex() + offset);
    1243             :     }
    1244           0 :     delete [] results;
    1245           0 :     return appendTo;
    1246             : }
    1247             : 
    1248           0 : MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
    1249             :                                                    UErrorCode& ec) {
    1250           0 :     CurrencyFormat* fmt = NULL;
    1251           0 :     if (U_SUCCESS(ec)) {
    1252           0 :         fmt = new CurrencyFormat(locale, ec);
    1253           0 :         if (U_FAILURE(ec)) {
    1254           0 :             delete fmt;
    1255           0 :             fmt = NULL;
    1256             :         }
    1257             :     }
    1258           0 :     return fmt;
    1259             : }
    1260             : 
    1261           0 : MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
    1262           0 :     if (U_FAILURE(ec)) {
    1263           0 :         return NULL;
    1264             :     }
    1265           0 :     return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
    1266             : }
    1267             : 
    1268             : U_NAMESPACE_END
    1269             : 
    1270             : #endif /* #if !UCONFIG_NO_FORMATTING */

Generated by: LCOV version 1.13