diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /intl/icu/source/i18n/dtitvinf.cpp | |
parent | Initial commit. (diff) | |
download | firefox-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.cpp | 814 |
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 = ©Skeleton; + 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 |