summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/dtitvinf.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /intl/icu/source/i18n/dtitvinf.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'intl/icu/source/i18n/dtitvinf.cpp')
-rw-r--r--intl/icu/source/i18n/dtitvinf.cpp814
1 files changed, 814 insertions, 0 deletions
diff --git a/intl/icu/source/i18n/dtitvinf.cpp b/intl/icu/source/i18n/dtitvinf.cpp
new file mode 100644
index 0000000000..3733d04518
--- /dev/null
+++ b/intl/icu/source/i18n/dtitvinf.cpp
@@ -0,0 +1,814 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*******************************************************************************
+* Copyright (C) 2008-2016, International Business Machines Corporation and
+* others. All Rights Reserved.
+*******************************************************************************
+*
+* File DTITVINF.CPP
+*
+*******************************************************************************
+*/
+
+#include "unicode/dtitvinf.h"
+
+
+#if !UCONFIG_NO_FORMATTING
+
+//TODO: define it in compiler time
+//#define DTITVINF_DEBUG 1
+
+
+#ifdef DTITVINF_DEBUG
+#include <iostream>
+#endif
+
+#include "cmemory.h"
+#include "cstring.h"
+#include "unicode/msgfmt.h"
+#include "unicode/uloc.h"
+#include "unicode/ures.h"
+#include "dtitv_impl.h"
+#include "charstr.h"
+#include "hash.h"
+#include "gregoimp.h"
+#include "uresimp.h"
+#include "hash.h"
+#include "gregoimp.h"
+#include "uresimp.h"
+
+
+U_NAMESPACE_BEGIN
+
+
+#ifdef DTITVINF_DEBUG
+#define PRINTMESG(msg) UPRV_BLOCK_MACRO_BEGIN { \
+ std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; \
+} UPRV_BLOCK_MACRO_END
+#endif
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
+
+static const char gCalendarTag[]="calendar";
+static const char gGregorianTag[]="gregorian";
+static const char gIntervalDateTimePatternTag[]="intervalFormats";
+static const char gFallbackPatternTag[]="fallback";
+
+// {0}
+static const char16_t gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
+// {1}
+static const char16_t gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
+
+// default fall-back
+static const char16_t gDefaultFallbackPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, EN_DASH, SPACE, LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET, 0};
+
+DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
+: fFallbackIntervalPattern(gDefaultFallbackPattern),
+ fFirstDateInPtnIsLaterDate(false),
+ fIntervalPatterns(nullptr)
+{
+ fIntervalPatterns = initHash(status);
+}
+
+
+
+DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
+: fFallbackIntervalPattern(gDefaultFallbackPattern),
+ fFirstDateInPtnIsLaterDate(false),
+ fIntervalPatterns(nullptr)
+{
+ initializeData(locale, status);
+}
+
+
+
+void
+DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
+ UCalendarDateFields lrgDiffCalUnit,
+ const UnicodeString& intervalPattern,
+ UErrorCode& status) {
+
+ if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
+ setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
+ setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
+ } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
+ lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
+ setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
+ } else {
+ setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
+ }
+}
+
+
+void
+DateIntervalInfo::setFallbackIntervalPattern(
+ const UnicodeString& fallbackPattern,
+ UErrorCode& status) {
+ if ( U_FAILURE(status) ) {
+ return;
+ }
+ int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
+ UPRV_LENGTHOF(gFirstPattern), 0);
+ int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
+ UPRV_LENGTHOF(gSecondPattern), 0);
+ if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return;
+ }
+ if ( firstPatternIndex > secondPatternIndex ) {
+ fFirstDateInPtnIsLaterDate = true;
+ }
+ fFallbackIntervalPattern = fallbackPattern;
+}
+
+
+
+DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
+: UObject(dtitvinf),
+ fIntervalPatterns(nullptr)
+{
+ *this = dtitvinf;
+}
+
+
+
+DateIntervalInfo&
+DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
+ if ( this == &dtitvinf ) {
+ return *this;
+ }
+
+ UErrorCode status = U_ZERO_ERROR;
+ deleteHash(fIntervalPatterns);
+ fIntervalPatterns = initHash(status);
+ copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
+ if ( U_FAILURE(status) ) {
+ return *this;
+ }
+
+ fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
+ fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
+ return *this;
+}
+
+
+DateIntervalInfo*
+DateIntervalInfo::clone() const {
+ return new DateIntervalInfo(*this);
+}
+
+
+DateIntervalInfo::~DateIntervalInfo() {
+ deleteHash(fIntervalPatterns);
+ fIntervalPatterns = nullptr;
+}
+
+
+bool
+DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
+ bool equal = (
+ fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
+ fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
+
+ if ( equal ) {
+ equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
+ }
+
+ return equal;
+}
+
+
+UnicodeString&
+DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
+ UCalendarDateFields field,
+ UnicodeString& result,
+ UErrorCode& status) const {
+ if ( U_FAILURE(status) ) {
+ return result;
+ }
+
+ const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
+ if ( patternsOfOneSkeleton != nullptr ) {
+ IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
+ if ( U_FAILURE(status) ) {
+ return result;
+ }
+ const UnicodeString& intervalPattern = patternsOfOneSkeleton[index];
+ if ( !intervalPattern.isEmpty() ) {
+ result = intervalPattern;
+ }
+ }
+ return result;
+}
+
+
+UBool
+DateIntervalInfo::getDefaultOrder() const {
+ return fFirstDateInPtnIsLaterDate;
+}
+
+
+UnicodeString&
+DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
+ result = fFallbackIntervalPattern;
+ return result;
+}
+
+#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
+
+
+static const int32_t PATH_PREFIX_LENGTH = 17;
+static const char16_t PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS,
+ LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS};
+static const int32_t PATH_SUFFIX_LENGTH = 16;
+static const char16_t PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A,
+ LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S};
+
+/**
+ * Sink for enumerating all of the date interval skeletons.
+ */
+struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
+
+ // Output data
+ DateIntervalInfo &dateIntervalInfo;
+
+ // Next calendar type
+ UnicodeString nextCalendarType;
+
+ DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
+ : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
+ virtual ~DateIntervalSink();
+
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) override {
+ if (U_FAILURE(errorCode)) { return; }
+
+ // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
+ ResourceTable dateIntervalData = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) {
+ if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
+ continue;
+ }
+
+ // Handle aliases and tables. Ignore the rest.
+ if (value.getType() == URES_ALIAS) {
+ // Get the calendar type for the alias path.
+ const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ nextCalendarType.remove();
+ getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
+
+ if (U_FAILURE(errorCode)) {
+ resetNextCalendarType();
+ }
+ break;
+
+ } else if (value.getType() == URES_TABLE) {
+ // Iterate over all the skeletons in the 'intervalFormat' table.
+ ResourceTable skeletonData = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) {
+ if (value.getType() == URES_TABLE) {
+ // Process the skeleton
+ processSkeletonTable(key, value, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Processes the patterns for a skeleton table
+ */
+ void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+
+ // Iterate over all the patterns in the current skeleton table
+ const char *currentSkeleton = key;
+ ResourceTable patternData = value.getTable(errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) {
+ if (value.getType() == URES_STRING) {
+ // Process the key
+ UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
+
+ // If the calendar field has a valid value
+ if (calendarField < UCAL_FIELD_COUNT) {
+ // Set the interval pattern
+ setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+ }
+ }
+ }
+ }
+
+ /**
+ * Extracts the calendar type from the path.
+ */
+ static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
+ UErrorCode &errorCode) {
+ if (U_FAILURE(errorCode)) { return; }
+
+ if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
+ errorCode = U_INVALID_FORMAT_ERROR;
+ return;
+ }
+
+ path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
+ }
+
+ /**
+ * Validates and processes the pattern letter
+ */
+ UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
+ // Check that patternLetter is just one letter
+ char c0;
+ if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
+ // Check that the pattern letter is accepted
+ if (c0 == 'G') {
+ return UCAL_ERA;
+ } else if (c0 == 'y') {
+ return UCAL_YEAR;
+ } else if (c0 == 'M') {
+ return UCAL_MONTH;
+ } else if (c0 == 'd') {
+ return UCAL_DATE;
+ } else if (c0 == 'a') {
+ return UCAL_AM_PM;
+ } else if (c0 == 'B') {
+ // TODO: Using AM/PM as a proxy for flexible day period isn't really correct, but it's close
+ return UCAL_AM_PM;
+ } else if (c0 == 'h' || c0 == 'H') {
+ return UCAL_HOUR;
+ } else if (c0 == 'm') {
+ return UCAL_MINUTE;
+ }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
+ }
+ return UCAL_FIELD_COUNT;
+ }
+
+ /**
+ * Stores the interval pattern for the current skeleton in the internal data structure
+ * if it's not present.
+ */
+ void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit,
+ const ResourceValue &value, UErrorCode &errorCode) {
+ // Check if the pattern has already been stored on the data structure
+ IntervalPatternIndex index =
+ dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
+ if (U_FAILURE(errorCode)) { return; }
+
+ UnicodeString skeleton(currentSkeleton, -1, US_INV);
+ UnicodeString* patternsOfOneSkeleton =
+ (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
+
+ if (patternsOfOneSkeleton == nullptr || patternsOfOneSkeleton[index].isEmpty()) {
+ UnicodeString pattern = value.getUnicodeString(errorCode);
+ dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
+ pattern, errorCode);
+ }
+ }
+
+ const UnicodeString &getNextCalendarType() {
+ return nextCalendarType;
+ }
+
+ void resetNextCalendarType() {
+ nextCalendarType.setToBogus();
+ }
+};
+
+// Virtual destructors must be defined out of line.
+DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
+
+
+
+void
+DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
+{
+ fIntervalPatterns = initHash(status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ const char *locName = locale.getName();
+
+ // Get the correct calendar type
+ const char * calendarTypeToUse = gGregorianTag; // initial default
+ char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
+ char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
+ // obtain a locale that always has the calendar key value that should be used
+ (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, nullptr,
+ "calendar", "calendar", locName, nullptr, false, &status);
+ localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
+ // now get the calendar key value from that locale
+ int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
+ ULOC_KEYWORDS_CAPACITY, &status);
+ if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
+ calendarTypeToUse = calendarType;
+ }
+ status = U_ZERO_ERROR;
+
+ // Instantiate the resource bundles
+ UResourceBundle *rb, *calBundle;
+ rb = ures_open(nullptr, locName, &status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, nullptr, &status);
+
+
+ if (U_SUCCESS(status)) {
+ UResourceBundle *calTypeBundle, *itvDtPtnResource;
+
+ // Get the fallback pattern
+ const char16_t* resStr = nullptr;
+ int32_t resStrLen = 0;
+ calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, nullptr, &status);
+ itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
+ gIntervalDateTimePatternTag, nullptr, &status);
+ // TODO(ICU-20400): After the fixing, we should find the "fallback" from
+ // the rb directly by the path "calendar/${calendar}/intervalFormats/fallback".
+ if ( U_SUCCESS(status) ) {
+ resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
+ &resStrLen, &status);
+ }
+
+ if ( U_SUCCESS(status) && (resStr != nullptr)) {
+ UnicodeString pattern = UnicodeString(true, resStr, resStrLen);
+ setFallbackIntervalPattern(pattern, status);
+ }
+ ures_close(itvDtPtnResource);
+ ures_close(calTypeBundle);
+
+
+ // Instantiate the sink
+ DateIntervalSink sink(*this, calendarTypeToUse);
+ const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
+
+ // Already loaded calendar types
+ Hashtable loadedCalendarTypes(false, status);
+
+ if (U_SUCCESS(status)) {
+ while (!calendarTypeToUseUString.isBogus()) {
+ // Set an error when a loop is detected
+ if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
+ status = U_INVALID_FORMAT_ERROR;
+ break;
+ }
+
+ // Register the calendar type to avoid loops
+ loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
+ if (U_FAILURE(status)) { break; }
+
+ // Get the calendar string
+ CharString calTypeBuffer;
+ calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
+ if (U_FAILURE(status)) { break; }
+ const char *calType = calTypeBuffer.data();
+
+ // Reset the next calendar type to load.
+ sink.resetNextCalendarType();
+
+ // Get all resources for this calendar type
+ ures_getAllItemsWithFallback(calBundle, calType, sink, status);
+ }
+ }
+ }
+
+ // Close the opened resource bundles
+ ures_close(calBundle);
+ ures_close(rb);
+}
+
+void
+DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
+ UCalendarDateFields lrgDiffCalUnit,
+ const UnicodeString& intervalPattern,
+ UErrorCode& status) {
+ IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
+ if ( U_FAILURE(status) ) {
+ return;
+ }
+ UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
+ UBool emptyHash = false;
+ if ( patternsOfOneSkeleton == nullptr ) {
+ patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
+ if (patternsOfOneSkeleton == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ emptyHash = true;
+ }
+
+ patternsOfOneSkeleton[index] = intervalPattern;
+ if ( emptyHash ) {
+ fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
+ }
+}
+
+
+
+void
+DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
+ int32_t* skeletonFieldWidth) {
+ const int8_t PATTERN_CHAR_BASE = 0x41;
+ int32_t i;
+ for ( i = 0; i < skeleton.length(); ++i ) {
+ // it is an ASCII char in skeleton
+ int8_t ch = (int8_t)skeleton.charAt(i);
+ ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
+ }
+}
+
+
+
+UBool
+DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
+ char patternLetter) {
+ if ( patternLetter == 'M' ) {
+ if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
+ (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+const UnicodeString*
+DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
+ int8_t& bestMatchDistanceInfo) const {
+#ifdef DTITVINF_DEBUG
+ char result[1000];
+ char result_1[1000];
+ char mesg[2000];
+ skeleton.extract(0, skeleton.length(), result, "UTF-8");
+ snprintf(mesg, sizeof(mesg), "in getBestSkeleton: skeleton: %s; \n", result);
+ PRINTMESG(mesg)
+#endif
+
+
+ int32_t inputSkeletonFieldWidth[] =
+ {
+ // A B C D E F G H I J K L M N O
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // P Q R S T U V W X Y Z
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // a b c d e f g h i j k l m n o
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // p q r s t u v w x y z
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ int32_t skeletonFieldWidth[] =
+ {
+ // A B C D E F G H I J K L M N O
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // P Q R S T U V W X Y Z
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // a b c d e f g h i j k l m n o
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ // p q r s t u v w x y z
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+
+ const int32_t DIFFERENT_FIELD = 0x1000;
+ const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
+ const int32_t BASE = 0x41;
+
+ // hack for certain alternate characters
+ // resource bundles only have time skeletons containing 'v', 'h', and 'H'
+ // but not time skeletons containing 'z', 'K', or 'k'
+ // the skeleton may also include 'a' or 'b', which never occur in the resource bundles, so strip them out too
+ UBool replacedAlternateChars = false;
+ const UnicodeString* inputSkeleton = &skeleton;
+ UnicodeString copySkeleton;
+ if ( skeleton.indexOf(LOW_Z) != -1 || skeleton.indexOf(LOW_K) != -1 || skeleton.indexOf(CAP_K) != -1 || skeleton.indexOf(LOW_A) != -1 || skeleton.indexOf(LOW_B) != -1 ) {
+ copySkeleton = skeleton;
+ copySkeleton.findAndReplace(UnicodeString(LOW_Z), UnicodeString(LOW_V));
+ copySkeleton.findAndReplace(UnicodeString(LOW_K), UnicodeString(CAP_H));
+ copySkeleton.findAndReplace(UnicodeString(CAP_K), UnicodeString(LOW_H));
+ copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString());
+ copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString());
+ inputSkeleton = &copySkeleton;
+ replacedAlternateChars = true;
+ }
+
+ parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
+ int32_t bestDistance = MAX_POSITIVE_INT;
+ const UnicodeString* bestSkeleton = nullptr;
+
+ // 0 means exact the same skeletons;
+ // 1 means having the same field, but with different length,
+ // 2 means only z/v, h/K, or H/k differs
+ // -1 means having different field.
+ bestMatchDistanceInfo = 0;
+ int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
+
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* elem = nullptr;
+ while ( (elem = fIntervalPatterns->nextElement(pos)) != nullptr ) {
+ const UHashTok keyTok = elem->key;
+ UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer;
+#ifdef DTITVINF_DEBUG
+ skeleton->extract(0, skeleton->length(), result, "UTF-8");
+ snprintf(mesg, sizeof(mesg), "available skeletons: skeleton: %s; \n", result);
+ PRINTMESG(mesg)
+#endif
+
+ // clear skeleton field width
+ int8_t i;
+ for ( i = 0; i < fieldLength; ++i ) {
+ skeletonFieldWidth[i] = 0;
+ }
+ parseSkeleton(*newSkeleton, skeletonFieldWidth);
+ // calculate distance
+ int32_t distance = 0;
+ int8_t fieldDifference = 1;
+ for ( i = 0; i < fieldLength; ++i ) {
+ int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
+ int32_t fieldWidth = skeletonFieldWidth[i];
+ if ( inputFieldWidth == fieldWidth ) {
+ continue;
+ }
+ if ( inputFieldWidth == 0 ) {
+ fieldDifference = -1;
+ distance += DIFFERENT_FIELD;
+ } else if ( fieldWidth == 0 ) {
+ fieldDifference = -1;
+ distance += DIFFERENT_FIELD;
+ } else if (stringNumeric(inputFieldWidth, fieldWidth,
+ (char)(i+BASE) ) ) {
+ distance += STRING_NUMERIC_DIFFERENCE;
+ } else {
+ distance += (inputFieldWidth > fieldWidth) ?
+ (inputFieldWidth - fieldWidth) :
+ (fieldWidth - inputFieldWidth);
+ }
+ }
+ if ( distance < bestDistance ) {
+ bestSkeleton = newSkeleton;
+ bestDistance = distance;
+ bestMatchDistanceInfo = fieldDifference;
+ }
+ if ( distance == 0 ) {
+ bestMatchDistanceInfo = 0;
+ break;
+ }
+ }
+ if ( replacedAlternateChars && bestMatchDistanceInfo != -1 ) {
+ bestMatchDistanceInfo = 2;
+ }
+ return bestSkeleton;
+}
+
+
+
+DateIntervalInfo::IntervalPatternIndex
+DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
+ UErrorCode& status) {
+ if ( U_FAILURE(status) ) {
+ return kIPI_MAX_INDEX;
+ }
+ IntervalPatternIndex index = kIPI_MAX_INDEX;
+ switch ( field ) {
+ case UCAL_ERA:
+ index = kIPI_ERA;
+ break;
+ case UCAL_YEAR:
+ index = kIPI_YEAR;
+ break;
+ case UCAL_MONTH:
+ index = kIPI_MONTH;
+ break;
+ case UCAL_DATE:
+ case UCAL_DAY_OF_WEEK:
+ //case UCAL_DAY_OF_MONTH:
+ index = kIPI_DATE;
+ break;
+ case UCAL_AM_PM:
+ index = kIPI_AM_PM;
+ break;
+ case UCAL_HOUR:
+ case UCAL_HOUR_OF_DAY:
+ index = kIPI_HOUR;
+ break;
+ case UCAL_MINUTE:
+ index = kIPI_MINUTE;
+ break;
+ case UCAL_SECOND:
+ index = kIPI_SECOND;
+ break;
+ case UCAL_MILLISECOND:
+ index = kIPI_MILLISECOND;
+ break;
+ default:
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+ return index;
+}
+
+
+
+void
+DateIntervalInfo::deleteHash(Hashtable* hTable)
+{
+ if ( hTable == nullptr ) {
+ return;
+ }
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* element = nullptr;
+ while ( (element = hTable->nextElement(pos)) != nullptr ) {
+ const UHashTok valueTok = element->value;
+ const UnicodeString* value = (UnicodeString*)valueTok.pointer;
+ delete[] value;
+ }
+ delete fIntervalPatterns;
+}
+
+
+U_CDECL_BEGIN
+
+/**
+ * set hash table value comparator
+ *
+ * @param val1 one value in comparison
+ * @param val2 the other value in comparison
+ * @return true if 2 values are the same, false otherwise
+ */
+static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
+
+static UBool
+U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
+ const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
+ const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
+ UBool ret = true;
+ int8_t i;
+ for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret ; ++i ) {
+ ret = (pattern1[i] == pattern2[i]);
+ }
+ return ret;
+}
+
+U_CDECL_END
+
+
+Hashtable*
+DateIntervalInfo::initHash(UErrorCode& status) {
+ if ( U_FAILURE(status) ) {
+ return nullptr;
+ }
+ Hashtable* hTable;
+ if ( (hTable = new Hashtable(false, status)) == nullptr ) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
+ if ( U_FAILURE(status) ) {
+ delete hTable;
+ return nullptr;
+ }
+ hTable->setValueComparator(dtitvinfHashTableValueComparator);
+ return hTable;
+}
+
+
+void
+DateIntervalInfo::copyHash(const Hashtable* source,
+ Hashtable* target,
+ UErrorCode& status) {
+ if ( U_FAILURE(status) ) {
+ return;
+ }
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* element = nullptr;
+ if ( source ) {
+ while ( (element = source->nextElement(pos)) != nullptr ) {
+ const UHashTok keyTok = element->key;
+ const UnicodeString* key = (UnicodeString*)keyTok.pointer;
+ const UHashTok valueTok = element->value;
+ const UnicodeString* value = (UnicodeString*)valueTok.pointer;
+ UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
+ if (copy == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ int8_t i;
+ for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
+ copy[i] = value[i];
+ }
+ target->put(UnicodeString(*key), copy, status);
+ if ( U_FAILURE(status) ) {
+ return;
+ }
+ }
+ }
+}
+
+
+U_NAMESPACE_END
+
+#endif