dtitvinf.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*******************************************************************************
  4. * Copyright (C) 2008-2016, International Business Machines Corporation and
  5. * others. All Rights Reserved.
  6. *******************************************************************************
  7. *
  8. * File DTITVINF.CPP
  9. *
  10. *******************************************************************************
  11. */
  12. #include "unicode/dtitvinf.h"
  13. #if !UCONFIG_NO_FORMATTING
  14. //TODO: define it in compiler time
  15. //#define DTITVINF_DEBUG 1
  16. #ifdef DTITVINF_DEBUG
  17. #include <iostream>
  18. #endif
  19. #include "cmemory.h"
  20. #include "cstring.h"
  21. #include "unicode/msgfmt.h"
  22. #include "unicode/uloc.h"
  23. #include "unicode/ures.h"
  24. #include "dtitv_impl.h"
  25. #include "charstr.h"
  26. #include "hash.h"
  27. #include "gregoimp.h"
  28. #include "uresimp.h"
  29. #include "hash.h"
  30. #include "gregoimp.h"
  31. #include "uresimp.h"
  32. U_NAMESPACE_BEGIN
  33. #ifdef DTITVINF_DEBUG
  34. #define PRINTMESG(msg) UPRV_BLOCK_MACRO_BEGIN { \
  35. std::cout << "(" << __FILE__ << ":" << __LINE__ << ") " << msg << "\n"; \
  36. } UPRV_BLOCK_MACRO_END
  37. #endif
  38. UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateIntervalInfo)
  39. static const char gCalendarTag[]="calendar";
  40. static const char gGregorianTag[]="gregorian";
  41. static const char gIntervalDateTimePatternTag[]="intervalFormats";
  42. static const char gFallbackPatternTag[]="fallback";
  43. // {0}
  44. static const char16_t gFirstPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET};
  45. // {1}
  46. static const char16_t gSecondPattern[] = {LEFT_CURLY_BRACKET, DIGIT_ONE, RIGHT_CURLY_BRACKET};
  47. // default fall-back
  48. 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};
  49. DateIntervalInfo::DateIntervalInfo(UErrorCode& status)
  50. : fFallbackIntervalPattern(gDefaultFallbackPattern),
  51. fFirstDateInPtnIsLaterDate(false),
  52. fIntervalPatterns(nullptr)
  53. {
  54. fIntervalPatterns = initHash(status);
  55. }
  56. DateIntervalInfo::DateIntervalInfo(const Locale& locale, UErrorCode& status)
  57. : fFallbackIntervalPattern(gDefaultFallbackPattern),
  58. fFirstDateInPtnIsLaterDate(false),
  59. fIntervalPatterns(nullptr)
  60. {
  61. initializeData(locale, status);
  62. }
  63. void
  64. DateIntervalInfo::setIntervalPattern(const UnicodeString& skeleton,
  65. UCalendarDateFields lrgDiffCalUnit,
  66. const UnicodeString& intervalPattern,
  67. UErrorCode& status) {
  68. if ( lrgDiffCalUnit == UCAL_HOUR_OF_DAY ) {
  69. setIntervalPatternInternally(skeleton, UCAL_AM_PM, intervalPattern, status);
  70. setIntervalPatternInternally(skeleton, UCAL_HOUR, intervalPattern, status);
  71. } else if ( lrgDiffCalUnit == UCAL_DAY_OF_MONTH ||
  72. lrgDiffCalUnit == UCAL_DAY_OF_WEEK ) {
  73. setIntervalPatternInternally(skeleton, UCAL_DATE, intervalPattern, status);
  74. } else {
  75. setIntervalPatternInternally(skeleton, lrgDiffCalUnit, intervalPattern, status);
  76. }
  77. }
  78. void
  79. DateIntervalInfo::setFallbackIntervalPattern(
  80. const UnicodeString& fallbackPattern,
  81. UErrorCode& status) {
  82. if ( U_FAILURE(status) ) {
  83. return;
  84. }
  85. int32_t firstPatternIndex = fallbackPattern.indexOf(gFirstPattern,
  86. UPRV_LENGTHOF(gFirstPattern), 0);
  87. int32_t secondPatternIndex = fallbackPattern.indexOf(gSecondPattern,
  88. UPRV_LENGTHOF(gSecondPattern), 0);
  89. if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
  90. status = U_ILLEGAL_ARGUMENT_ERROR;
  91. return;
  92. }
  93. if ( firstPatternIndex > secondPatternIndex ) {
  94. fFirstDateInPtnIsLaterDate = true;
  95. }
  96. fFallbackIntervalPattern = fallbackPattern;
  97. }
  98. DateIntervalInfo::DateIntervalInfo(const DateIntervalInfo& dtitvinf)
  99. : UObject(dtitvinf),
  100. fIntervalPatterns(nullptr)
  101. {
  102. *this = dtitvinf;
  103. }
  104. DateIntervalInfo&
  105. DateIntervalInfo::operator=(const DateIntervalInfo& dtitvinf) {
  106. if ( this == &dtitvinf ) {
  107. return *this;
  108. }
  109. UErrorCode status = U_ZERO_ERROR;
  110. deleteHash(fIntervalPatterns);
  111. fIntervalPatterns = initHash(status);
  112. copyHash(dtitvinf.fIntervalPatterns, fIntervalPatterns, status);
  113. if ( U_FAILURE(status) ) {
  114. return *this;
  115. }
  116. fFallbackIntervalPattern = dtitvinf.fFallbackIntervalPattern;
  117. fFirstDateInPtnIsLaterDate = dtitvinf.fFirstDateInPtnIsLaterDate;
  118. return *this;
  119. }
  120. DateIntervalInfo*
  121. DateIntervalInfo::clone() const {
  122. return new DateIntervalInfo(*this);
  123. }
  124. DateIntervalInfo::~DateIntervalInfo() {
  125. deleteHash(fIntervalPatterns);
  126. fIntervalPatterns = nullptr;
  127. }
  128. bool
  129. DateIntervalInfo::operator==(const DateIntervalInfo& other) const {
  130. bool equal = (
  131. fFallbackIntervalPattern == other.fFallbackIntervalPattern &&
  132. fFirstDateInPtnIsLaterDate == other.fFirstDateInPtnIsLaterDate );
  133. if ( equal ) {
  134. equal = fIntervalPatterns->equals(*(other.fIntervalPatterns));
  135. }
  136. return equal;
  137. }
  138. UnicodeString&
  139. DateIntervalInfo::getIntervalPattern(const UnicodeString& skeleton,
  140. UCalendarDateFields field,
  141. UnicodeString& result,
  142. UErrorCode& status) const {
  143. if ( U_FAILURE(status) ) {
  144. return result;
  145. }
  146. const UnicodeString* patternsOfOneSkeleton = (UnicodeString*) fIntervalPatterns->get(skeleton);
  147. if ( patternsOfOneSkeleton != nullptr ) {
  148. IntervalPatternIndex index = calendarFieldToIntervalIndex(field, status);
  149. if ( U_FAILURE(status) ) {
  150. return result;
  151. }
  152. const UnicodeString& intervalPattern = patternsOfOneSkeleton[index];
  153. if ( !intervalPattern.isEmpty() ) {
  154. result = intervalPattern;
  155. }
  156. }
  157. return result;
  158. }
  159. UBool
  160. DateIntervalInfo::getDefaultOrder() const {
  161. return fFirstDateInPtnIsLaterDate;
  162. }
  163. UnicodeString&
  164. DateIntervalInfo::getFallbackIntervalPattern(UnicodeString& result) const {
  165. result = fFallbackIntervalPattern;
  166. return result;
  167. }
  168. #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)
  169. static const int32_t PATH_PREFIX_LENGTH = 17;
  170. static const char16_t PATH_PREFIX[] = {SOLIDUS, CAP_L, CAP_O, CAP_C, CAP_A, CAP_L, CAP_E, SOLIDUS,
  171. LOW_C, LOW_A, LOW_L, LOW_E, LOW_N, LOW_D, LOW_A, LOW_R, SOLIDUS};
  172. static const int32_t PATH_SUFFIX_LENGTH = 16;
  173. static const char16_t PATH_SUFFIX[] = {SOLIDUS, LOW_I, LOW_N, LOW_T, LOW_E, LOW_R, LOW_V, LOW_A,
  174. LOW_L, CAP_F, LOW_O, LOW_R, LOW_M, LOW_A, LOW_T, LOW_S};
  175. /**
  176. * Sink for enumerating all of the date interval skeletons.
  177. */
  178. struct DateIntervalInfo::DateIntervalSink : public ResourceSink {
  179. // Output data
  180. DateIntervalInfo &dateIntervalInfo;
  181. // Next calendar type
  182. UnicodeString nextCalendarType;
  183. DateIntervalSink(DateIntervalInfo &diInfo, const char *currentCalendarType)
  184. : dateIntervalInfo(diInfo), nextCalendarType(currentCalendarType, -1, US_INV) { }
  185. virtual ~DateIntervalSink();
  186. virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &errorCode) override {
  187. if (U_FAILURE(errorCode)) { return; }
  188. // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
  189. ResourceTable dateIntervalData = value.getTable(errorCode);
  190. if (U_FAILURE(errorCode)) { return; }
  191. for (int32_t i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) {
  192. if (uprv_strcmp(key, gIntervalDateTimePatternTag) != 0) {
  193. continue;
  194. }
  195. // Handle aliases and tables. Ignore the rest.
  196. if (value.getType() == URES_ALIAS) {
  197. // Get the calendar type for the alias path.
  198. const UnicodeString &aliasPath = value.getAliasUnicodeString(errorCode);
  199. if (U_FAILURE(errorCode)) { return; }
  200. nextCalendarType.remove();
  201. getCalendarTypeFromPath(aliasPath, nextCalendarType, errorCode);
  202. if (U_FAILURE(errorCode)) {
  203. resetNextCalendarType();
  204. }
  205. break;
  206. } else if (value.getType() == URES_TABLE) {
  207. // Iterate over all the skeletons in the 'intervalFormat' table.
  208. ResourceTable skeletonData = value.getTable(errorCode);
  209. if (U_FAILURE(errorCode)) { return; }
  210. for (int32_t j = 0; skeletonData.getKeyAndValue(j, key, value); j++) {
  211. if (value.getType() == URES_TABLE) {
  212. // Process the skeleton
  213. processSkeletonTable(key, value, errorCode);
  214. if (U_FAILURE(errorCode)) { return; }
  215. }
  216. }
  217. break;
  218. }
  219. }
  220. }
  221. /**
  222. * Processes the patterns for a skeleton table
  223. */
  224. void processSkeletonTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
  225. if (U_FAILURE(errorCode)) { return; }
  226. // Iterate over all the patterns in the current skeleton table
  227. const char *currentSkeleton = key;
  228. ResourceTable patternData = value.getTable(errorCode);
  229. if (U_FAILURE(errorCode)) { return; }
  230. for (int32_t k = 0; patternData.getKeyAndValue(k, key, value); k++) {
  231. if (value.getType() == URES_STRING) {
  232. // Process the key
  233. UCalendarDateFields calendarField = validateAndProcessPatternLetter(key);
  234. // If the calendar field has a valid value
  235. if (calendarField < UCAL_FIELD_COUNT) {
  236. // Set the interval pattern
  237. setIntervalPatternIfAbsent(currentSkeleton, calendarField, value, errorCode);
  238. if (U_FAILURE(errorCode)) { return; }
  239. }
  240. }
  241. }
  242. }
  243. /**
  244. * Extracts the calendar type from the path.
  245. */
  246. static void getCalendarTypeFromPath(const UnicodeString &path, UnicodeString &calendarType,
  247. UErrorCode &errorCode) {
  248. if (U_FAILURE(errorCode)) { return; }
  249. if (!path.startsWith(PATH_PREFIX, PATH_PREFIX_LENGTH) || !path.endsWith(PATH_SUFFIX, PATH_SUFFIX_LENGTH)) {
  250. errorCode = U_INVALID_FORMAT_ERROR;
  251. return;
  252. }
  253. path.extractBetween(PATH_PREFIX_LENGTH, path.length() - PATH_SUFFIX_LENGTH, calendarType);
  254. }
  255. /**
  256. * Validates and processes the pattern letter
  257. */
  258. UCalendarDateFields validateAndProcessPatternLetter(const char *patternLetter) {
  259. // Check that patternLetter is just one letter
  260. char c0;
  261. if ((c0 = patternLetter[0]) != 0 && patternLetter[1] == 0) {
  262. // Check that the pattern letter is accepted
  263. if (c0 == 'G') {
  264. return UCAL_ERA;
  265. } else if (c0 == 'y') {
  266. return UCAL_YEAR;
  267. } else if (c0 == 'M') {
  268. return UCAL_MONTH;
  269. } else if (c0 == 'd') {
  270. return UCAL_DATE;
  271. } else if (c0 == 'a') {
  272. return UCAL_AM_PM;
  273. } else if (c0 == 'B') {
  274. // TODO: Using AM/PM as a proxy for flexible day period isn't really correct, but it's close
  275. return UCAL_AM_PM;
  276. } else if (c0 == 'h' || c0 == 'H') {
  277. return UCAL_HOUR;
  278. } else if (c0 == 'm') {
  279. return UCAL_MINUTE;
  280. }// TODO(ticket:12190): Why icu4c doesn't accept the calendar field "s" but icu4j does?
  281. }
  282. return UCAL_FIELD_COUNT;
  283. }
  284. /**
  285. * Stores the interval pattern for the current skeleton in the internal data structure
  286. * if it's not present.
  287. */
  288. void setIntervalPatternIfAbsent(const char *currentSkeleton, UCalendarDateFields lrgDiffCalUnit,
  289. const ResourceValue &value, UErrorCode &errorCode) {
  290. // Check if the pattern has already been stored on the data structure
  291. IntervalPatternIndex index =
  292. dateIntervalInfo.calendarFieldToIntervalIndex(lrgDiffCalUnit, errorCode);
  293. if (U_FAILURE(errorCode)) { return; }
  294. UnicodeString skeleton(currentSkeleton, -1, US_INV);
  295. UnicodeString* patternsOfOneSkeleton =
  296. (UnicodeString*)(dateIntervalInfo.fIntervalPatterns->get(skeleton));
  297. if (patternsOfOneSkeleton == nullptr || patternsOfOneSkeleton[index].isEmpty()) {
  298. UnicodeString pattern = value.getUnicodeString(errorCode);
  299. dateIntervalInfo.setIntervalPatternInternally(skeleton, lrgDiffCalUnit,
  300. pattern, errorCode);
  301. }
  302. }
  303. const UnicodeString &getNextCalendarType() {
  304. return nextCalendarType;
  305. }
  306. void resetNextCalendarType() {
  307. nextCalendarType.setToBogus();
  308. }
  309. };
  310. // Virtual destructors must be defined out of line.
  311. DateIntervalInfo::DateIntervalSink::~DateIntervalSink() {}
  312. void
  313. DateIntervalInfo::initializeData(const Locale& locale, UErrorCode& status)
  314. {
  315. fIntervalPatterns = initHash(status);
  316. if (U_FAILURE(status)) {
  317. return;
  318. }
  319. const char *locName = locale.getName();
  320. // Get the correct calendar type
  321. const char * calendarTypeToUse = gGregorianTag; // initial default
  322. char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well
  323. char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
  324. // obtain a locale that always has the calendar key value that should be used
  325. (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, nullptr,
  326. "calendar", "calendar", locName, nullptr, false, &status);
  327. localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
  328. // now get the calendar key value from that locale
  329. int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType,
  330. ULOC_KEYWORDS_CAPACITY, &status);
  331. if (U_SUCCESS(status) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) {
  332. calendarTypeToUse = calendarType;
  333. }
  334. status = U_ZERO_ERROR;
  335. // Instantiate the resource bundles
  336. UResourceBundle *rb, *calBundle;
  337. rb = ures_open(nullptr, locName, &status);
  338. if (U_FAILURE(status)) {
  339. return;
  340. }
  341. calBundle = ures_getByKeyWithFallback(rb, gCalendarTag, nullptr, &status);
  342. if (U_SUCCESS(status)) {
  343. UResourceBundle *calTypeBundle, *itvDtPtnResource;
  344. // Get the fallback pattern
  345. const char16_t* resStr = nullptr;
  346. int32_t resStrLen = 0;
  347. calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, nullptr, &status);
  348. itvDtPtnResource = ures_getByKeyWithFallback(calTypeBundle,
  349. gIntervalDateTimePatternTag, nullptr, &status);
  350. // TODO(ICU-20400): After the fixing, we should find the "fallback" from
  351. // the rb directly by the path "calendar/${calendar}/intervalFormats/fallback".
  352. if ( U_SUCCESS(status) ) {
  353. resStr = ures_getStringByKeyWithFallback(itvDtPtnResource, gFallbackPatternTag,
  354. &resStrLen, &status);
  355. }
  356. if ( U_SUCCESS(status) && (resStr != nullptr)) {
  357. UnicodeString pattern = UnicodeString(true, resStr, resStrLen);
  358. setFallbackIntervalPattern(pattern, status);
  359. }
  360. ures_close(itvDtPtnResource);
  361. ures_close(calTypeBundle);
  362. // Instantiate the sink
  363. DateIntervalSink sink(*this, calendarTypeToUse);
  364. const UnicodeString &calendarTypeToUseUString = sink.getNextCalendarType();
  365. // Already loaded calendar types
  366. Hashtable loadedCalendarTypes(false, status);
  367. if (U_SUCCESS(status)) {
  368. while (!calendarTypeToUseUString.isBogus()) {
  369. // Set an error when a loop is detected
  370. if (loadedCalendarTypes.geti(calendarTypeToUseUString) == 1) {
  371. status = U_INVALID_FORMAT_ERROR;
  372. break;
  373. }
  374. // Register the calendar type to avoid loops
  375. loadedCalendarTypes.puti(calendarTypeToUseUString, 1, status);
  376. if (U_FAILURE(status)) { break; }
  377. // Get the calendar string
  378. CharString calTypeBuffer;
  379. calTypeBuffer.appendInvariantChars(calendarTypeToUseUString, status);
  380. if (U_FAILURE(status)) { break; }
  381. const char *calType = calTypeBuffer.data();
  382. // Reset the next calendar type to load.
  383. sink.resetNextCalendarType();
  384. // Get all resources for this calendar type
  385. ures_getAllItemsWithFallback(calBundle, calType, sink, status);
  386. }
  387. }
  388. }
  389. // Close the opened resource bundles
  390. ures_close(calBundle);
  391. ures_close(rb);
  392. }
  393. void
  394. DateIntervalInfo::setIntervalPatternInternally(const UnicodeString& skeleton,
  395. UCalendarDateFields lrgDiffCalUnit,
  396. const UnicodeString& intervalPattern,
  397. UErrorCode& status) {
  398. IntervalPatternIndex index = calendarFieldToIntervalIndex(lrgDiffCalUnit,status);
  399. if ( U_FAILURE(status) ) {
  400. return;
  401. }
  402. UnicodeString* patternsOfOneSkeleton = (UnicodeString*)(fIntervalPatterns->get(skeleton));
  403. UBool emptyHash = false;
  404. if ( patternsOfOneSkeleton == nullptr ) {
  405. patternsOfOneSkeleton = new UnicodeString[kIPI_MAX_INDEX];
  406. if (patternsOfOneSkeleton == nullptr) {
  407. status = U_MEMORY_ALLOCATION_ERROR;
  408. return;
  409. }
  410. emptyHash = true;
  411. }
  412. patternsOfOneSkeleton[index] = intervalPattern;
  413. if ( emptyHash ) {
  414. fIntervalPatterns->put(skeleton, patternsOfOneSkeleton, status);
  415. }
  416. }
  417. void
  418. DateIntervalInfo::parseSkeleton(const UnicodeString& skeleton,
  419. int32_t* skeletonFieldWidth) {
  420. const int8_t PATTERN_CHAR_BASE = 0x41;
  421. int32_t i;
  422. for ( i = 0; i < skeleton.length(); ++i ) {
  423. // it is an ASCII char in skeleton
  424. int8_t ch = (int8_t)skeleton.charAt(i);
  425. ++skeletonFieldWidth[ch - PATTERN_CHAR_BASE];
  426. }
  427. }
  428. UBool
  429. DateIntervalInfo::stringNumeric(int32_t fieldWidth, int32_t anotherFieldWidth,
  430. char patternLetter) {
  431. if ( patternLetter == 'M' ) {
  432. if ( (fieldWidth <= 2 && anotherFieldWidth > 2) ||
  433. (fieldWidth > 2 && anotherFieldWidth <= 2 )) {
  434. return true;
  435. }
  436. }
  437. return false;
  438. }
  439. const UnicodeString*
  440. DateIntervalInfo::getBestSkeleton(const UnicodeString& skeleton,
  441. int8_t& bestMatchDistanceInfo) const {
  442. #ifdef DTITVINF_DEBUG
  443. char result[1000];
  444. char result_1[1000];
  445. char mesg[2000];
  446. skeleton.extract(0, skeleton.length(), result, "UTF-8");
  447. snprintf(mesg, sizeof(mesg), "in getBestSkeleton: skeleton: %s; \n", result);
  448. PRINTMESG(mesg)
  449. #endif
  450. int32_t inputSkeletonFieldWidth[] =
  451. {
  452. // A B C D E F G H I J K L M N O
  453. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  454. // P Q R S T U V W X Y Z
  455. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  456. // a b c d e f g h i j k l m n o
  457. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  458. // p q r s t u v w x y z
  459. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  460. };
  461. int32_t skeletonFieldWidth[] =
  462. {
  463. // A B C D E F G H I J K L M N O
  464. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  465. // P Q R S T U V W X Y Z
  466. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  467. // a b c d e f g h i j k l m n o
  468. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  469. // p q r s t u v w x y z
  470. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  471. };
  472. const int32_t DIFFERENT_FIELD = 0x1000;
  473. const int32_t STRING_NUMERIC_DIFFERENCE = 0x100;
  474. const int32_t BASE = 0x41;
  475. // hack for certain alternate characters
  476. // resource bundles only have time skeletons containing 'v', 'h', and 'H'
  477. // but not time skeletons containing 'z', 'K', or 'k'
  478. // the skeleton may also include 'a' or 'b', which never occur in the resource bundles, so strip them out too
  479. UBool replacedAlternateChars = false;
  480. const UnicodeString* inputSkeleton = &skeleton;
  481. UnicodeString copySkeleton;
  482. 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 ) {
  483. copySkeleton = skeleton;
  484. copySkeleton.findAndReplace(UnicodeString(LOW_Z), UnicodeString(LOW_V));
  485. copySkeleton.findAndReplace(UnicodeString(LOW_K), UnicodeString(CAP_H));
  486. copySkeleton.findAndReplace(UnicodeString(CAP_K), UnicodeString(LOW_H));
  487. copySkeleton.findAndReplace(UnicodeString(LOW_A), UnicodeString());
  488. copySkeleton.findAndReplace(UnicodeString(LOW_B), UnicodeString());
  489. inputSkeleton = &copySkeleton;
  490. replacedAlternateChars = true;
  491. }
  492. parseSkeleton(*inputSkeleton, inputSkeletonFieldWidth);
  493. int32_t bestDistance = MAX_POSITIVE_INT;
  494. const UnicodeString* bestSkeleton = nullptr;
  495. // 0 means exact the same skeletons;
  496. // 1 means having the same field, but with different length,
  497. // 2 means only z/v, h/K, or H/k differs
  498. // -1 means having different field.
  499. bestMatchDistanceInfo = 0;
  500. int8_t fieldLength = UPRV_LENGTHOF(skeletonFieldWidth);
  501. int32_t pos = UHASH_FIRST;
  502. const UHashElement* elem = nullptr;
  503. while ( (elem = fIntervalPatterns->nextElement(pos)) != nullptr ) {
  504. const UHashTok keyTok = elem->key;
  505. UnicodeString* newSkeleton = (UnicodeString*)keyTok.pointer;
  506. #ifdef DTITVINF_DEBUG
  507. skeleton->extract(0, skeleton->length(), result, "UTF-8");
  508. snprintf(mesg, sizeof(mesg), "available skeletons: skeleton: %s; \n", result);
  509. PRINTMESG(mesg)
  510. #endif
  511. // clear skeleton field width
  512. int8_t i;
  513. for ( i = 0; i < fieldLength; ++i ) {
  514. skeletonFieldWidth[i] = 0;
  515. }
  516. parseSkeleton(*newSkeleton, skeletonFieldWidth);
  517. // calculate distance
  518. int32_t distance = 0;
  519. int8_t fieldDifference = 1;
  520. for ( i = 0; i < fieldLength; ++i ) {
  521. int32_t inputFieldWidth = inputSkeletonFieldWidth[i];
  522. int32_t fieldWidth = skeletonFieldWidth[i];
  523. if ( inputFieldWidth == fieldWidth ) {
  524. continue;
  525. }
  526. if ( inputFieldWidth == 0 ) {
  527. fieldDifference = -1;
  528. distance += DIFFERENT_FIELD;
  529. } else if ( fieldWidth == 0 ) {
  530. fieldDifference = -1;
  531. distance += DIFFERENT_FIELD;
  532. } else if (stringNumeric(inputFieldWidth, fieldWidth,
  533. (char)(i+BASE) ) ) {
  534. distance += STRING_NUMERIC_DIFFERENCE;
  535. } else {
  536. distance += (inputFieldWidth > fieldWidth) ?
  537. (inputFieldWidth - fieldWidth) :
  538. (fieldWidth - inputFieldWidth);
  539. }
  540. }
  541. if ( distance < bestDistance ) {
  542. bestSkeleton = newSkeleton;
  543. bestDistance = distance;
  544. bestMatchDistanceInfo = fieldDifference;
  545. }
  546. if ( distance == 0 ) {
  547. bestMatchDistanceInfo = 0;
  548. break;
  549. }
  550. }
  551. if ( replacedAlternateChars && bestMatchDistanceInfo != -1 ) {
  552. bestMatchDistanceInfo = 2;
  553. }
  554. return bestSkeleton;
  555. }
  556. DateIntervalInfo::IntervalPatternIndex
  557. DateIntervalInfo::calendarFieldToIntervalIndex(UCalendarDateFields field,
  558. UErrorCode& status) {
  559. if ( U_FAILURE(status) ) {
  560. return kIPI_MAX_INDEX;
  561. }
  562. IntervalPatternIndex index = kIPI_MAX_INDEX;
  563. switch ( field ) {
  564. case UCAL_ERA:
  565. index = kIPI_ERA;
  566. break;
  567. case UCAL_YEAR:
  568. index = kIPI_YEAR;
  569. break;
  570. case UCAL_MONTH:
  571. index = kIPI_MONTH;
  572. break;
  573. case UCAL_DATE:
  574. case UCAL_DAY_OF_WEEK:
  575. //case UCAL_DAY_OF_MONTH:
  576. index = kIPI_DATE;
  577. break;
  578. case UCAL_AM_PM:
  579. index = kIPI_AM_PM;
  580. break;
  581. case UCAL_HOUR:
  582. case UCAL_HOUR_OF_DAY:
  583. index = kIPI_HOUR;
  584. break;
  585. case UCAL_MINUTE:
  586. index = kIPI_MINUTE;
  587. break;
  588. case UCAL_SECOND:
  589. index = kIPI_SECOND;
  590. break;
  591. case UCAL_MILLISECOND:
  592. index = kIPI_MILLISECOND;
  593. break;
  594. default:
  595. status = U_ILLEGAL_ARGUMENT_ERROR;
  596. }
  597. return index;
  598. }
  599. void
  600. DateIntervalInfo::deleteHash(Hashtable* hTable)
  601. {
  602. if ( hTable == nullptr ) {
  603. return;
  604. }
  605. int32_t pos = UHASH_FIRST;
  606. const UHashElement* element = nullptr;
  607. while ( (element = hTable->nextElement(pos)) != nullptr ) {
  608. const UHashTok valueTok = element->value;
  609. const UnicodeString* value = (UnicodeString*)valueTok.pointer;
  610. delete[] value;
  611. }
  612. delete fIntervalPatterns;
  613. }
  614. U_CDECL_BEGIN
  615. /**
  616. * set hash table value comparator
  617. *
  618. * @param val1 one value in comparison
  619. * @param val2 the other value in comparison
  620. * @return true if 2 values are the same, false otherwise
  621. */
  622. static UBool U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2);
  623. static UBool
  624. U_CALLCONV dtitvinfHashTableValueComparator(UHashTok val1, UHashTok val2) {
  625. const UnicodeString* pattern1 = (UnicodeString*)val1.pointer;
  626. const UnicodeString* pattern2 = (UnicodeString*)val2.pointer;
  627. UBool ret = true;
  628. int8_t i;
  629. for ( i = 0; i < DateIntervalInfo::kMaxIntervalPatternIndex && ret ; ++i ) {
  630. ret = (pattern1[i] == pattern2[i]);
  631. }
  632. return ret;
  633. }
  634. U_CDECL_END
  635. Hashtable*
  636. DateIntervalInfo::initHash(UErrorCode& status) {
  637. if ( U_FAILURE(status) ) {
  638. return nullptr;
  639. }
  640. Hashtable* hTable;
  641. if ( (hTable = new Hashtable(false, status)) == nullptr ) {
  642. status = U_MEMORY_ALLOCATION_ERROR;
  643. return nullptr;
  644. }
  645. if ( U_FAILURE(status) ) {
  646. delete hTable;
  647. return nullptr;
  648. }
  649. hTable->setValueComparator(dtitvinfHashTableValueComparator);
  650. return hTable;
  651. }
  652. void
  653. DateIntervalInfo::copyHash(const Hashtable* source,
  654. Hashtable* target,
  655. UErrorCode& status) {
  656. if ( U_FAILURE(status) ) {
  657. return;
  658. }
  659. int32_t pos = UHASH_FIRST;
  660. const UHashElement* element = nullptr;
  661. if ( source ) {
  662. while ( (element = source->nextElement(pos)) != nullptr ) {
  663. const UHashTok keyTok = element->key;
  664. const UnicodeString* key = (UnicodeString*)keyTok.pointer;
  665. const UHashTok valueTok = element->value;
  666. const UnicodeString* value = (UnicodeString*)valueTok.pointer;
  667. UnicodeString* copy = new UnicodeString[kIPI_MAX_INDEX];
  668. if (copy == nullptr) {
  669. status = U_MEMORY_ALLOCATION_ERROR;
  670. return;
  671. }
  672. int8_t i;
  673. for ( i = 0; i < kIPI_MAX_INDEX; ++i ) {
  674. copy[i] = value[i];
  675. }
  676. target->put(UnicodeString(*key), copy, status);
  677. if ( U_FAILURE(status) ) {
  678. return;
  679. }
  680. }
  681. }
  682. }
  683. U_NAMESPACE_END
  684. #endif