]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/dtptngen.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / dtptngen.cpp
index 3e10f4277aa2854129df9d4ca80f0aac772e5d9a..c0cf9079529708e9f9a1777c215a2873b99bf264 100644 (file)
@@ -1,6 +1,6 @@
 /*
 *******************************************************************************
-* Copyright (C) 2007-2014, International Business Machines Corporation and
+* Copyright (C) 2007-2016, International Business Machines Corporation and
 * others. All Rights Reserved.
 *******************************************************************************
 *
@@ -16,7 +16,7 @@
 #include "unicode/decimfmt.h"
 #include "unicode/dtfmtsym.h"
 #include "unicode/dtptngen.h"
-#include "unicode/msgfmt.h"
+#include "unicode/simpleformatter.h"
 #include "unicode/smpdtfmt.h"
 #include "unicode/udat.h"
 #include "unicode/udatpg.h"
 #include "unicode/ustring.h"
 #include "unicode/rep.h"
 #include "cpputils.h"
-#include "ucln_in.h"
 #include "mutex.h"
+#include "umutex.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "locbased.h"
 #include "gregoimp.h"
 #include "hash.h"
+#include "uhash.h"
 #include "uresimp.h"
 #include "dtptngen_impl.h"
-
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
+#include "ucln_in.h"
+#include "charstr.h"
 
 #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
 /**
@@ -127,7 +128,6 @@ static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_
 
 U_NAMESPACE_BEGIN
 
-
 // *****************************************************************************
 // class DateTimePatternGenerator
 // *****************************************************************************
@@ -206,6 +206,8 @@ static const dtTypeElem dtTypes[] = {
     {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
     {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
     {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
+    {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour
+    {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12/24 hour no AM/PM
     {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[]
  };
 
@@ -248,15 +250,12 @@ DateTimePatternGenerator::createInstance(UErrorCode& status) {
 
 DateTimePatternGenerator* U_EXPORT2
 DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) {
-    DateTimePatternGenerator *result = new DateTimePatternGenerator(locale, status);
-    if (result == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-    }
     if (U_FAILURE(status)) {
-        delete result;
-        result = NULL;
+        return NULL;
     }
-    return result;
+    LocalPointer<DateTimePatternGenerator> result(
+            new DateTimePatternGenerator(locale, status), status);
+    return U_SUCCESS(status) ? result.orphan() : NULL;
 }
 
 DateTimePatternGenerator*  U_EXPORT2
@@ -315,6 +314,10 @@ DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerato
 
 DateTimePatternGenerator&
 DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
+    // reflexive case
+    if (&other == this) {
+        return *this;
+    }
     pLocale = other.pLocale;
     fDefaultHourFormatChar = other.fDefaultHourFormatChar;
     *fp = *(other.fp);
@@ -383,6 +386,34 @@ DateTimePatternGenerator::~DateTimePatternGenerator() {
     if (skipMatcher != NULL) delete skipMatcher;
 }
 
+namespace {
+
+UInitOnce initOnce = U_INITONCE_INITIALIZER;
+UHashtable *localeToAllowedHourFormatsMap = NULL;
+
+// Value deleter for hashmap.
+void deleteAllowedHourFormats(void *ptr) {
+    uprv_free(ptr);
+}
+
+// Close hashmap at cleanup.
+UBool allowedHourFormatsCleanup() {
+    uhash_close(localeToAllowedHourFormatsMap);
+    return TRUE;
+}
+
+enum AllowedHourFormat{
+    ALLOWED_HOUR_FORMAT_UNKNOWN = -1,
+    ALLOWED_HOUR_FORMAT_h,
+    ALLOWED_HOUR_FORMAT_H,
+    ALLOWED_HOUR_FORMAT_hb,
+    ALLOWED_HOUR_FORMAT_Hb,
+    ALLOWED_HOUR_FORMAT_hB,
+    ALLOWED_HOUR_FORMAT_HB
+};
+
+}  // namespace
+
 void
 DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) {
     //const char *baseLangName = locale.getBaseName(); // unused
@@ -397,19 +428,206 @@ DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) {
     addCLDRData(locale, status);
     setDateTimeFromCalendar(locale, status);
     setDecimalSymbols(locale, status);
+    umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
+    getAllowedHourFormats(locale, status);
 } // DateTimePatternGenerator::initData
 
+namespace {
+
+struct AllowedHourFormatsSink : public ResourceTableSink {
+    // Initialize sub-sinks.
+    AllowedHourFormatsSink() : localeSink(*this), allowedListSink(*this) {}
+    virtual ~AllowedHourFormatsSink();
+
+    // Entry point.
+    virtual ResourceTableSink *getOrCreateTableSink(const char *key, int32_t, UErrorCode &status) {
+        if (U_FAILURE(status)) { return NULL; }
+
+        locale = key;
+        return &localeSink;
+    }
+
+    struct LocaleSink : public ResourceTableSink {
+        AllowedHourFormatsSink &outer;
+        LocaleSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+        virtual ~LocaleSink();
+
+        virtual void put(const char *key, const ResourceValue &value, UErrorCode &status) {
+            if (U_FAILURE(status)) { return; }
+
+            if (uprv_strcmp(key, "allowed") == 0) {
+                outer.allowedFormats = static_cast<int32_t *>(uprv_malloc(2 * sizeof(int32_t)));
+                outer.allowedFormatsLength = 1;
+                if (outer.allowedFormats == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    return;
+                }
+                outer.allowedFormats[0] = outer.getHourFormatFromUnicodeString(
+                    value.getUnicodeString(status));
+            }
+        }
+
+        virtual ResourceArraySink *getOrCreateArraySink(const char *key, int32_t size, UErrorCode &status) {
+            if (U_FAILURE(status)) { return NULL; }
+
+            if (uprv_strcmp(key, "allowed") == 0) {
+                outer.allowedFormats = static_cast<int32_t *>(uprv_malloc((size + 1) * sizeof(int32_t)));
+                outer.allowedFormatsLength = size;
+                if (outer.allowedFormats == NULL) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    return NULL;
+                } else {
+                    return &outer.allowedListSink;
+                }
+            }
+            return NULL;
+        }
+
+        virtual void leave(UErrorCode &status) {
+            if (U_FAILURE(status) || outer.allowedFormats == NULL) { return; }
+
+            outer.allowedFormats[outer.allowedFormatsLength] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+            uhash_put(localeToAllowedHourFormatsMap, const_cast<char *>(outer.locale), outer.allowedFormats, &status);
+            outer.allowedFormats = NULL;
+        }
+    } localeSink;
+
+    struct AllowedListSink : public ResourceArraySink {
+        AllowedHourFormatsSink &outer;
+        AllowedListSink(AllowedHourFormatsSink &outer) : outer(outer) {}
+        virtual ~AllowedListSink();
+
+        virtual void put(int32_t index, const ResourceValue &value, UErrorCode &status) {
+            if (U_FAILURE(status)) { return; }
+
+            outer.allowedFormats[index] = outer.getHourFormatFromUnicodeString(
+                value.getUnicodeString(status));
+        }
+    } allowedListSink;
+
+    const char *locale;
+    int32_t *allowedFormats;
+    int32_t allowedFormatsLength;
+
+    AllowedHourFormat getHourFormatFromUnicodeString(UnicodeString s) {
+        if (s.length() == 1) {
+            if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; }
+            if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; }
+        } else if (s.length() == 2) {
+            if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
+            if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; }
+            if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
+            if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; }
+        }
+
+        return ALLOWED_HOUR_FORMAT_UNKNOWN;
+    }
+};
+
+}  // namespace
+
+AllowedHourFormatsSink::~AllowedHourFormatsSink() {}
+AllowedHourFormatsSink::LocaleSink::~LocaleSink() {}
+AllowedHourFormatsSink::AllowedListSink::~AllowedListSink() {}
+
+void DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
+    if (U_FAILURE(status)) { return; }
+    localeToAllowedHourFormatsMap = uhash_open(
+        uhash_hashChars, uhash_compareChars, NULL, &status);
+    uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
+    LocalUResourceBundlePointer rb(ures_openDirect(NULL, "supplementalData", &status));
+
+    AllowedHourFormatsSink sink;
+    // TODO: Currently in the enumeration each table allocates a new array.
+    // Try to reduce the number of memory allocations. Consider storing a
+    // UVector32 with the concatenation of all of the sub-arrays, put the start index
+    // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
+    // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
+    // object. Remember to clean up the vector, too.
+    ures_getAllTableItemsWithFallback(rb.getAlias(), "timeData", sink, status);
+
+    ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup);
+}
+
+void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status) {
+    if (U_FAILURE(status)) { return; }
+
+    const char *localeID = locale.getName();
+    char maxLocaleID[ULOC_FULLNAME_CAPACITY];
+    int32_t length = uloc_addLikelySubtags(localeID, maxLocaleID, ULOC_FULLNAME_CAPACITY, &status);
+    if (U_FAILURE(status)) {
+        return;
+    } else if (length == ULOC_FULLNAME_CAPACITY) {  // no room for NUL
+        status = U_BUFFER_OVERFLOW_ERROR;
+        return;
+    }
+    Locale maxLocale = Locale(maxLocaleID);
+
+    const char *country = maxLocale.getCountry();
+    if (*country == '\0') { country = "001"; }
+    const char *language = maxLocale.getLanguage();
+
+    CharString langCountry;
+    langCountry.append(language, uprv_strlen(language), status);
+    langCountry.append('_', status);
+    langCountry.append(country, uprv_strlen(country), status);
+
+    int32_t *allowedFormats;
+    allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, langCountry.data());
+    if (allowedFormats == NULL) {
+        allowedFormats = (int32_t *)uhash_get(localeToAllowedHourFormatsMap, const_cast<char *>(country));
+    }
+
+    if (allowedFormats != NULL) {  // Lookup is successful
+        for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
+            fAllowedHourFormats[i] = allowedFormats[i];
+            if (allowedFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
+                break;
+            }
+        }
+    } else {  // Lookup failed, twice
+        fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
+        fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
+    }
+}
+
 UnicodeString
 DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
 /*status*/) {
-    dtMatcher->set(pattern, fp);
-    return dtMatcher->getSkeletonPtr()->getSkeleton();
+    FormatParser fp;
+    DateTimeMatcher matcher;
+    PtnSkeleton localSkeleton;
+    matcher.set(pattern, &fp, localSkeleton);
+    return localSkeleton.getSkeleton();
+}
+
+UnicodeString
+DateTimePatternGenerator::staticGetSkeleton(
+        const UnicodeString& pattern, UErrorCode& /*status*/) {
+    FormatParser fp;
+    DateTimeMatcher matcher;
+    PtnSkeleton localSkeleton;
+    matcher.set(pattern, &fp, localSkeleton);
+    return localSkeleton.getSkeleton();
 }
 
 UnicodeString
 DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) {
-    dtMatcher->set(pattern, fp);
-    return dtMatcher->getSkeletonPtr()->getBaseSkeleton();
+    FormatParser fp;
+    DateTimeMatcher matcher;
+    PtnSkeleton localSkeleton;
+    matcher.set(pattern, &fp, localSkeleton);
+    return localSkeleton.getBaseSkeleton();
+}
+
+UnicodeString
+DateTimePatternGenerator::staticGetBaseSkeleton(
+        const UnicodeString& pattern, UErrorCode& /*status*/) {
+    FormatParser fp;
+    DateTimeMatcher matcher;
+    PtnSkeleton localSkeleton;
+    matcher.set(pattern, &fp, localSkeleton);
+    return localSkeleton.getBaseSkeleton();
 }
 
 void
@@ -511,10 +729,10 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
     const char *key=NULL;
     int32_t i;
 
-    UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, LENGTHOF(UDATPG_ItemFormat)-1);  // Read-only alias.
+    UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1);  // Read-only alias.
 
     err = U_ZERO_ERROR;
-    
+
     fDefaultHourFormatChar = 0;
     for (i=0; i<UDATPG_FIELD_COUNT; ++i ) {
         appendItemNames[i]=CAP_F;
@@ -713,7 +931,7 @@ DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) {
             }
         }
         // Go to the top of the loop to process contents of calTypeBundle
-    }    
+    }
 
     if (hackPattern.length()>0) {
         hackTimes(hackPattern, err);
@@ -778,7 +996,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
     int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
     int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
 
-    // Replace hour metacharacters 'j' and 'J', set flags as necessary
+    // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
     UnicodeString patternFormCopy = UnicodeString(patternForm);
     UChar hourFormatSkeletonCharForLowJ = fDefaultHourFormatChar;
     switch (options & UADATPG_FORCE_HOUR_CYCLE_MASK) {
@@ -795,6 +1013,26 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
         } else if (!inQuoted) {
             if (patChr == LOW_J) {
                 patternFormCopy.setCharAt(patPos, hourFormatSkeletonCharForLowJ);
+            } else if (patChr == CAP_C) {
+                AllowedHourFormat preferred;
+                if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
+                    preferred = (AllowedHourFormat)fAllowedHourFormats[0];
+                } else {
+                    status = U_INVALID_FORMAT_ERROR;
+                    return UnicodeString();
+                }
+
+                if (preferred == ALLOWED_HOUR_FORMAT_H || preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_Hb) {
+                    patternFormCopy.setCharAt(patPos, CAP_H);
+                } else {
+                    patternFormCopy.setCharAt(patPos, LOW_H);
+                }
+
+                if (preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_hB) {
+                    flags |= kDTPGSkeletonUsesCapB;
+                } else if (preferred == ALLOWED_HOUR_FORMAT_Hb || preferred == ALLOWED_HOUR_FORMAT_hb) {
+                    flags |= kDTPGSkeletonUsesLowB;
+                }
             } else if (patChr == CAP_J) {
                 // Get pattern for skeleton with H, then in adjustFieldTypes
                 // replace hour pattern characters as necessary.
@@ -830,8 +1068,7 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
     resultPattern.remove();
     status = U_ZERO_ERROR;
     dtFormat=getDateTimeFormat();
-    Formattable dateTimeObject[] = { timePattern, datePattern };
-    resultPattern = MessageFormat::format(dtFormat, dateTimeObject, 2, resultPattern, status );
+    SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status);
     return resultPattern;
 }
 
@@ -1079,6 +1316,19 @@ DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern,
             }
             const dtTypeElem *row = &dtTypes[canonicalIndex];
             int32_t typeValue = row->field;
+
+            // Handle special day periods.
+            if (typeValue == UDATPG_DAYPERIOD_FIELD && flags != 0) {
+                UChar c = NONE;  // '0'
+                if (flags & kDTPGSkeletonUsesCapB) { c = CAP_B; }
+                if (flags & kDTPGSkeletonUsesLowB) { c = LOW_B; }
+
+                if (c != NONE) {
+                    for (int32_t i = 0; i < field.length(); ++i)
+                    field.setCharAt(i, c);
+                }
+            }
+
             if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) {
                 UnicodeString newField=dtMatcher->skeleton.original[UDATPG_FRACTIONAL_SECOND_FIELD];
                 field = field + decimal + newField;
@@ -1189,13 +1439,13 @@ DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags,
             int32_t topField=getTopBitNumber(foundMask);
             UnicodeString appendName;
             getAppendName((UDateTimePatternField)topField, appendName);
-            const Formattable formatPattern[] = {
-                resultPattern,
-                tempPattern,
-                appendName
+            const UnicodeString *values[3] = {
+                &resultPattern,
+                &tempPattern,
+                &appendName
             };
-            UnicodeString emptyStr;
-            resultPattern = MessageFormat::format(appendItemFormats[topField], formatPattern, 3, emptyStr, err);
+            SimpleFormatter(appendItemFormats[topField], 2, 3, err).
+                    formatAndReplace(values, 3, resultPattern, NULL, 0, err);
             lastMissingFieldMask = distanceInfo->missingFieldMask;
         }
     }
@@ -1244,7 +1494,7 @@ DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) {
     if(U_FAILURE(status)){
         return;
     }
-    int32_t pos = -1;
+    int32_t pos = UHASH_FIRST;
     const UHashElement* elem = NULL;
     // walk through the hash table and create a deep clone
     while((elem = other->nextElement(pos))!= NULL){
@@ -1360,7 +1610,7 @@ PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 return;
             }
-
+            curElem->skeletonWasSpecified = otherElem->skeletonWasSpecified;
             if (prevElem!=NULL) {
                 prevElem->next=curElem;
             }
@@ -1867,7 +2117,7 @@ FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) {
 }
 
 UBool
-FormatParser::isQuoteLiteral(const UnicodeString& s) const {
+FormatParser::isQuoteLiteral(const UnicodeString& s) {
     return (UBool)(s.charAt(0)==SINGLE_QUOTE);
 }