tmutfmt.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. *******************************************************************************
  5. * Copyright (C) 2008-2015, Google, International Business Machines Corporation
  6. * and others. All Rights Reserved.
  7. *******************************************************************************
  8. */
  9. #include "unicode/tmutfmt.h"
  10. #if !UCONFIG_NO_FORMATTING
  11. #include "unicode/decimfmt.h"
  12. #include "unicode/localpointer.h"
  13. #include "plurrule_impl.h"
  14. #include "uvector.h"
  15. #include "charstr.h"
  16. #include "cmemory.h"
  17. #include "cstring.h"
  18. #include "hash.h"
  19. #include "uresimp.h"
  20. #include "ureslocs.h"
  21. #include "unicode/msgfmt.h"
  22. #include "uassert.h"
  23. #define LEFT_CURLY_BRACKET ((char16_t)0x007B)
  24. #define RIGHT_CURLY_BRACKET ((char16_t)0x007D)
  25. #define SPACE ((char16_t)0x0020)
  26. #define DIGIT_ZERO ((char16_t)0x0030)
  27. #define LOW_S ((char16_t)0x0073)
  28. #define LOW_M ((char16_t)0x006D)
  29. #define LOW_I ((char16_t)0x0069)
  30. #define LOW_N ((char16_t)0x006E)
  31. #define LOW_H ((char16_t)0x0068)
  32. #define LOW_W ((char16_t)0x0077)
  33. #define LOW_D ((char16_t)0x0064)
  34. #define LOW_Y ((char16_t)0x0079)
  35. #define LOW_Z ((char16_t)0x007A)
  36. #define LOW_E ((char16_t)0x0065)
  37. #define LOW_R ((char16_t)0x0072)
  38. #define LOW_O ((char16_t)0x006F)
  39. #define LOW_N ((char16_t)0x006E)
  40. #define LOW_T ((char16_t)0x0074)
  41. //TODO: define in compile time
  42. //#define TMUTFMT_DEBUG 1
  43. #ifdef TMUTFMT_DEBUG
  44. #include <iostream>
  45. #endif
  46. U_NAMESPACE_BEGIN
  47. UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeUnitFormat)
  48. static const char gUnitsTag[] = "units";
  49. static const char gShortUnitsTag[] = "unitsShort";
  50. static const char gTimeUnitYear[] = "year";
  51. static const char gTimeUnitMonth[] = "month";
  52. static const char gTimeUnitDay[] = "day";
  53. static const char gTimeUnitWeek[] = "week";
  54. static const char gTimeUnitHour[] = "hour";
  55. static const char gTimeUnitMinute[] = "minute";
  56. static const char gTimeUnitSecond[] = "second";
  57. static const char gPluralCountOther[] = "other";
  58. static const char16_t DEFAULT_PATTERN_FOR_SECOND[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_S, 0};
  59. static const char16_t DEFAULT_PATTERN_FOR_MINUTE[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, LOW_I, LOW_N, 0};
  60. static const char16_t DEFAULT_PATTERN_FOR_HOUR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_H, 0};
  61. static const char16_t DEFAULT_PATTERN_FOR_WEEK[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_W, 0};
  62. static const char16_t DEFAULT_PATTERN_FOR_DAY[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_D, 0};
  63. static const char16_t DEFAULT_PATTERN_FOR_MONTH[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_M, 0};
  64. static const char16_t DEFAULT_PATTERN_FOR_YEAR[] = {LEFT_CURLY_BRACKET, DIGIT_ZERO, RIGHT_CURLY_BRACKET, SPACE, LOW_Y, 0};
  65. static const char16_t PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
  66. static const char16_t PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
  67. static const char16_t PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
  68. TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
  69. initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, nullptr, status);
  70. create(UTMUTFMT_FULL_STYLE, status);
  71. }
  72. TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
  73. initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
  74. create(UTMUTFMT_FULL_STYLE, status);
  75. }
  76. TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
  77. switch (style) {
  78. case UTMUTFMT_FULL_STYLE:
  79. initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
  80. break;
  81. case UTMUTFMT_ABBREVIATED_STYLE:
  82. initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, nullptr, status);
  83. break;
  84. default:
  85. initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, nullptr, status);
  86. break;
  87. }
  88. create(style, status);
  89. }
  90. TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
  91. : MeasureFormat(other),
  92. fStyle(other.fStyle)
  93. {
  94. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  95. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  96. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  97. UErrorCode status = U_ZERO_ERROR;
  98. fTimeUnitToCountToPatterns[i] = initHash(status);
  99. if (U_SUCCESS(status)) {
  100. copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
  101. } else {
  102. delete fTimeUnitToCountToPatterns[i];
  103. fTimeUnitToCountToPatterns[i] = nullptr;
  104. }
  105. }
  106. }
  107. TimeUnitFormat::~TimeUnitFormat() {
  108. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  109. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  110. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  111. deleteHash(fTimeUnitToCountToPatterns[i]);
  112. fTimeUnitToCountToPatterns[i] = nullptr;
  113. }
  114. }
  115. TimeUnitFormat*
  116. TimeUnitFormat::clone() const {
  117. return new TimeUnitFormat(*this);
  118. }
  119. TimeUnitFormat&
  120. TimeUnitFormat::operator=(const TimeUnitFormat& other) {
  121. if (this == &other) {
  122. return *this;
  123. }
  124. MeasureFormat::operator=(other);
  125. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  126. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  127. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  128. deleteHash(fTimeUnitToCountToPatterns[i]);
  129. fTimeUnitToCountToPatterns[i] = nullptr;
  130. }
  131. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  132. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  133. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  134. UErrorCode status = U_ZERO_ERROR;
  135. fTimeUnitToCountToPatterns[i] = initHash(status);
  136. if (U_SUCCESS(status)) {
  137. copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
  138. } else {
  139. delete fTimeUnitToCountToPatterns[i];
  140. fTimeUnitToCountToPatterns[i] = nullptr;
  141. }
  142. }
  143. fStyle = other.fStyle;
  144. return *this;
  145. }
  146. void
  147. TimeUnitFormat::parseObject(const UnicodeString& source,
  148. Formattable& result,
  149. ParsePosition& pos) const {
  150. Formattable resultNumber(0.0);
  151. UBool withNumberFormat = false;
  152. TimeUnit::UTimeUnitFields resultTimeUnit = TimeUnit::UTIMEUNIT_FIELD_COUNT;
  153. int32_t oldPos = pos.getIndex();
  154. int32_t newPos = -1;
  155. int32_t longestParseDistance = 0;
  156. UnicodeString* countOfLongestMatch = nullptr;
  157. #ifdef TMUTFMT_DEBUG
  158. char res[1000];
  159. source.extract(0, source.length(), res, "UTF-8");
  160. std::cout << "parse source: " << res << "\n";
  161. #endif
  162. // parse by iterating through all available patterns
  163. // and looking for the longest match.
  164. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  165. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  166. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  167. Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
  168. int32_t elemPos = UHASH_FIRST;
  169. const UHashElement* elem = nullptr;
  170. while ((elem = countToPatterns->nextElement(elemPos)) != nullptr){
  171. const UHashTok keyTok = elem->key;
  172. UnicodeString* count = (UnicodeString*)keyTok.pointer;
  173. #ifdef TMUTFMT_DEBUG
  174. count->extract(0, count->length(), res, "UTF-8");
  175. std::cout << "parse plural count: " << res << "\n";
  176. #endif
  177. const UHashTok valueTok = elem->value;
  178. // the value is a pair of MessageFormat*
  179. MessageFormat** patterns = (MessageFormat**)valueTok.pointer;
  180. for (UTimeUnitFormatStyle style = UTMUTFMT_FULL_STYLE; style < UTMUTFMT_FORMAT_STYLE_COUNT;
  181. style = (UTimeUnitFormatStyle)(style + 1)) {
  182. MessageFormat* pattern = patterns[style];
  183. pos.setErrorIndex(-1);
  184. pos.setIndex(oldPos);
  185. // see if we can parse
  186. Formattable parsed;
  187. pattern->parseObject(source, parsed, pos);
  188. if (pos.getErrorIndex() != -1 || pos.getIndex() == oldPos) {
  189. continue;
  190. }
  191. #ifdef TMUTFMT_DEBUG
  192. std::cout << "parsed.getType: " << parsed.getType() << "\n";
  193. #endif
  194. Formattable tmpNumber(0.0);
  195. if (pattern->getArgTypeCount() != 0) {
  196. Formattable& temp = parsed[0];
  197. if (temp.getType() == Formattable::kString) {
  198. UnicodeString tmpString;
  199. UErrorCode pStatus = U_ZERO_ERROR;
  200. getNumberFormatInternal().parse(temp.getString(tmpString), tmpNumber, pStatus);
  201. if (U_FAILURE(pStatus)) {
  202. continue;
  203. }
  204. } else if (temp.isNumeric()) {
  205. tmpNumber = temp;
  206. } else {
  207. continue;
  208. }
  209. }
  210. int32_t parseDistance = pos.getIndex() - oldPos;
  211. if (parseDistance > longestParseDistance) {
  212. if (pattern->getArgTypeCount() != 0) {
  213. resultNumber = tmpNumber;
  214. withNumberFormat = true;
  215. } else {
  216. withNumberFormat = false;
  217. }
  218. resultTimeUnit = i;
  219. newPos = pos.getIndex();
  220. longestParseDistance = parseDistance;
  221. countOfLongestMatch = count;
  222. }
  223. }
  224. }
  225. }
  226. /* After find the longest match, parse the number.
  227. * Result number could be null for the pattern without number pattern.
  228. * such as unit pattern in Arabic.
  229. * When result number is null, use plural rule to set the number.
  230. */
  231. if (withNumberFormat == false && longestParseDistance != 0) {
  232. // set the number using plurrual count
  233. if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ZERO, 4)) {
  234. resultNumber = Formattable(0.0);
  235. } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_ONE, 3)) {
  236. resultNumber = Formattable(1.0);
  237. } else if (0 == countOfLongestMatch->compare(PLURAL_COUNT_TWO, 3)) {
  238. resultNumber = Formattable(2.0);
  239. } else {
  240. // should not happen.
  241. // TODO: how to handle?
  242. resultNumber = Formattable(3.0);
  243. }
  244. }
  245. if (longestParseDistance == 0) {
  246. pos.setIndex(oldPos);
  247. pos.setErrorIndex(0);
  248. } else {
  249. UErrorCode status = U_ZERO_ERROR;
  250. LocalPointer<TimeUnitAmount> tmutamt(new TimeUnitAmount(resultNumber, resultTimeUnit, status), status);
  251. if (U_SUCCESS(status)) {
  252. result.adoptObject(tmutamt.orphan());
  253. pos.setIndex(newPos);
  254. pos.setErrorIndex(-1);
  255. } else {
  256. pos.setIndex(oldPos);
  257. pos.setErrorIndex(0);
  258. }
  259. }
  260. }
  261. void
  262. TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
  263. // fTimeUnitToCountToPatterns[] must have its elements initialized to nullptr first
  264. // before checking for failure status.
  265. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  266. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  267. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  268. fTimeUnitToCountToPatterns[i] = nullptr;
  269. }
  270. if (U_FAILURE(status)) {
  271. return;
  272. }
  273. if (style < UTMUTFMT_FULL_STYLE || style >= UTMUTFMT_FORMAT_STYLE_COUNT) {
  274. status = U_ILLEGAL_ARGUMENT_ERROR;
  275. return;
  276. }
  277. fStyle = style;
  278. //TODO: format() and parseObj() are const member functions,
  279. //so, can not do lazy initialization in C++.
  280. //setup has to be done in constructors.
  281. //and here, the behavior is not consistent with Java.
  282. //In Java, create an empty instance does not setup locale as
  283. //default locale. If it followed by setNumberFormat(),
  284. //in format(), the locale will set up as the locale in fNumberFormat.
  285. //But in C++, this sets the locale as the default locale.
  286. setup(status);
  287. }
  288. void
  289. TimeUnitFormat::setup(UErrorCode& err) {
  290. initDataMembers(err);
  291. UVector pluralCounts(nullptr, uhash_compareUnicodeString, 6, err);
  292. LocalPointer<StringEnumeration> keywords(getPluralRules().getKeywords(err), err);
  293. if (U_FAILURE(err)) {
  294. return;
  295. }
  296. UnicodeString* pluralCount;
  297. while ((pluralCount = const_cast<UnicodeString*>(keywords->snext(err))) != nullptr) {
  298. pluralCounts.addElement(pluralCount, err);
  299. }
  300. readFromCurrentLocale(UTMUTFMT_FULL_STYLE, gUnitsTag, pluralCounts, err);
  301. checkConsistency(UTMUTFMT_FULL_STYLE, gUnitsTag, err);
  302. readFromCurrentLocale(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, pluralCounts, err);
  303. checkConsistency(UTMUTFMT_ABBREVIATED_STYLE, gShortUnitsTag, err);
  304. }
  305. void
  306. TimeUnitFormat::initDataMembers(UErrorCode& err){
  307. if (U_FAILURE(err)) {
  308. return;
  309. }
  310. for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
  311. i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
  312. i = (TimeUnit::UTimeUnitFields)(i+1)) {
  313. deleteHash(fTimeUnitToCountToPatterns[i]);
  314. fTimeUnitToCountToPatterns[i] = nullptr;
  315. }
  316. }
  317. struct TimeUnitFormatReadSink : public ResourceSink {
  318. TimeUnitFormat *timeUnitFormatObj;
  319. const UVector &pluralCounts;
  320. UTimeUnitFormatStyle style;
  321. UBool beenHere;
  322. TimeUnitFormatReadSink(TimeUnitFormat *timeUnitFormatObj,
  323. const UVector &pluralCounts, UTimeUnitFormatStyle style) :
  324. timeUnitFormatObj(timeUnitFormatObj), pluralCounts(pluralCounts),
  325. style(style), beenHere(false){}
  326. virtual ~TimeUnitFormatReadSink();
  327. virtual void put(const char *key, ResourceValue &value, UBool, UErrorCode &errorCode) override {
  328. // Skip all put() calls except the first one -- discard all fallback data.
  329. if (beenHere) {
  330. return;
  331. } else {
  332. beenHere = true;
  333. }
  334. ResourceTable units = value.getTable(errorCode);
  335. if (U_FAILURE(errorCode)) { return; }
  336. for (int32_t i = 0; units.getKeyAndValue(i, key, value); ++i) {
  337. const char* timeUnitName = key;
  338. if (timeUnitName == nullptr) {
  339. continue;
  340. }
  341. TimeUnit::UTimeUnitFields timeUnitField = TimeUnit::UTIMEUNIT_FIELD_COUNT;
  342. if ( uprv_strcmp(timeUnitName, gTimeUnitYear) == 0 ) {
  343. timeUnitField = TimeUnit::UTIMEUNIT_YEAR;
  344. } else if ( uprv_strcmp(timeUnitName, gTimeUnitMonth) == 0 ) {
  345. timeUnitField = TimeUnit::UTIMEUNIT_MONTH;
  346. } else if ( uprv_strcmp(timeUnitName, gTimeUnitDay) == 0 ) {
  347. timeUnitField = TimeUnit::UTIMEUNIT_DAY;
  348. } else if ( uprv_strcmp(timeUnitName, gTimeUnitHour) == 0 ) {
  349. timeUnitField = TimeUnit::UTIMEUNIT_HOUR;
  350. } else if ( uprv_strcmp(timeUnitName, gTimeUnitMinute) == 0 ) {
  351. timeUnitField = TimeUnit::UTIMEUNIT_MINUTE;
  352. } else if ( uprv_strcmp(timeUnitName, gTimeUnitSecond) == 0 ) {
  353. timeUnitField = TimeUnit::UTIMEUNIT_SECOND;
  354. } else if ( uprv_strcmp(timeUnitName, gTimeUnitWeek) == 0 ) {
  355. timeUnitField = TimeUnit::UTIMEUNIT_WEEK;
  356. } else {
  357. continue;
  358. }
  359. LocalPointer<Hashtable> localCountToPatterns;
  360. Hashtable *countToPatterns =
  361. timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField];
  362. if (countToPatterns == nullptr) {
  363. localCountToPatterns.adoptInsteadAndCheckErrorCode(
  364. timeUnitFormatObj->initHash(errorCode), errorCode);
  365. countToPatterns = localCountToPatterns.getAlias();
  366. if (U_FAILURE(errorCode)) {
  367. return;
  368. }
  369. }
  370. ResourceTable countsToPatternTable = value.getTable(errorCode);
  371. if (U_FAILURE(errorCode)) {
  372. continue;
  373. }
  374. for (int32_t j = 0; countsToPatternTable.getKeyAndValue(j, key, value); ++j) {
  375. errorCode = U_ZERO_ERROR;
  376. UnicodeString pattern = value.getUnicodeString(errorCode);
  377. if (U_FAILURE(errorCode)) {
  378. continue;
  379. }
  380. UnicodeString pluralCountUniStr(key, -1, US_INV);
  381. if (!pluralCounts.contains(&pluralCountUniStr)) {
  382. continue;
  383. }
  384. LocalPointer<MessageFormat> messageFormat(new MessageFormat(
  385. pattern, timeUnitFormatObj->getLocale(errorCode), errorCode), errorCode);
  386. if (U_FAILURE(errorCode)) {
  387. return;
  388. }
  389. MessageFormat** formatters =
  390. (MessageFormat**)countToPatterns->get(pluralCountUniStr);
  391. if (formatters == nullptr) {
  392. LocalMemory<MessageFormat *> localFormatters(
  393. (MessageFormat **)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
  394. if (localFormatters.isNull()) {
  395. errorCode = U_MEMORY_ALLOCATION_ERROR;
  396. return;
  397. }
  398. localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
  399. localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
  400. countToPatterns->put(pluralCountUniStr, localFormatters.getAlias(), errorCode);
  401. if (U_FAILURE(errorCode)) {
  402. return;
  403. }
  404. formatters = localFormatters.orphan();
  405. }
  406. formatters[style] = messageFormat.orphan();
  407. }
  408. if (timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] == nullptr) {
  409. timeUnitFormatObj->fTimeUnitToCountToPatterns[timeUnitField] = localCountToPatterns.orphan();
  410. }
  411. }
  412. }
  413. };
  414. TimeUnitFormatReadSink::~TimeUnitFormatReadSink() {}
  415. void
  416. TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* key,
  417. const UVector& pluralCounts, UErrorCode& err) {
  418. if (U_FAILURE(err)) {
  419. return;
  420. }
  421. // fill timeUnitToCountToPatterns from resource file
  422. // err is used to indicate wrong status except missing resource.
  423. // status is an error code used in resource lookup.
  424. // status does not affect "err".
  425. UErrorCode status = U_ZERO_ERROR;
  426. LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, getLocaleID(status), &status));
  427. LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
  428. ures_getByKey(unitsRes.getAlias(), "duration", unitsRes.getAlias(), &status);
  429. if (U_FAILURE(status)) {
  430. return;
  431. }
  432. TimeUnitFormatReadSink sink(this, pluralCounts, style);
  433. ures_getAllItemsWithFallback(unitsRes.getAlias(), "", sink, status);
  434. }
  435. void
  436. TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UErrorCode& err) {
  437. if (U_FAILURE(err)) {
  438. return;
  439. }
  440. // there should be patterns for each plural rule in each time unit.
  441. // For each time unit,
  442. // for each plural rule, following is unit pattern fall-back rule:
  443. // ( for example: "one" hour )
  444. // look for its unit pattern in its locale tree.
  445. // if pattern is not found in its own locale, such as de_DE,
  446. // look for the pattern in its parent, such as de,
  447. // keep looking till found or till root.
  448. // if the pattern is not found in root either,
  449. // fallback to plural count "other",
  450. // look for the pattern of "other" in the locale tree:
  451. // "de_DE" to "de" to "root".
  452. // If not found, fall back to value of
  453. // static variable DEFAULT_PATTERN_FOR_xxx, such as "{0} h".
  454. //
  455. // Following is consistency check to create pattern for each
  456. // plural rule in each time unit using above fall-back rule.
  457. //
  458. LocalPointer<StringEnumeration> keywords(
  459. getPluralRules().getKeywords(err), err);
  460. const UnicodeString* pluralCount;
  461. while (U_SUCCESS(err) && (pluralCount = keywords->snext(err)) != nullptr) {
  462. for (int32_t i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
  463. // for each time unit,
  464. // get all the patterns for each plural rule in this locale.
  465. Hashtable* countToPatterns = fTimeUnitToCountToPatterns[i];
  466. if ( countToPatterns == nullptr ) {
  467. fTimeUnitToCountToPatterns[i] = countToPatterns = initHash(err);
  468. if (U_FAILURE(err)) {
  469. return;
  470. }
  471. }
  472. MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
  473. if( formatters == nullptr || formatters[style] == nullptr ) {
  474. // look through parents
  475. const char* localeName = getLocaleID(err);
  476. CharString pluralCountChars;
  477. pluralCountChars.appendInvariantChars(*pluralCount, err);
  478. searchInLocaleChain(style, key, localeName,
  479. (TimeUnit::UTimeUnitFields)i,
  480. *pluralCount, pluralCountChars.data(),
  481. countToPatterns, err);
  482. }
  483. // TODO: what to do with U_FAILURE(err) at this point.
  484. // As is, the outer loop continues to run, but does nothing.
  485. }
  486. }
  487. }
  488. // srcPluralCount is the original plural count on which the pattern is
  489. // searched for.
  490. // searchPluralCount is the fallback plural count.
  491. // For example, to search for pattern for ""one" hour",
  492. // "one" is the srcPluralCount,
  493. // if the pattern is not found even in root, fallback to
  494. // using patterns of plural count "other",
  495. // then, "other" is the searchPluralCount.
  496. void
  497. TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key, const char* localeName,
  498. TimeUnit::UTimeUnitFields srcTimeUnitField,
  499. const UnicodeString& srcPluralCount,
  500. const char* searchPluralCount,
  501. Hashtable* countToPatterns,
  502. UErrorCode& err) {
  503. if (U_FAILURE(err)) {
  504. return;
  505. }
  506. UErrorCode status = U_ZERO_ERROR;
  507. char parentLocale[ULOC_FULLNAME_CAPACITY];
  508. uprv_strcpy(parentLocale, localeName);
  509. int32_t locNameLen;
  510. U_ASSERT(countToPatterns != nullptr);
  511. while ((locNameLen = uloc_getParent(parentLocale, parentLocale,
  512. ULOC_FULLNAME_CAPACITY, &status)) >= 0){
  513. // look for pattern for srcPluralCount in locale tree
  514. LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_UNIT, parentLocale, &status));
  515. LocalUResourceBundlePointer unitsRes(ures_getByKey(rb.getAlias(), key, nullptr, &status));
  516. const char* timeUnitName = getTimeUnitName(srcTimeUnitField, status);
  517. LocalUResourceBundlePointer countsToPatternRB(ures_getByKey(unitsRes.getAlias(), timeUnitName, nullptr, &status));
  518. const char16_t* pattern;
  519. int32_t ptLength;
  520. pattern = ures_getStringByKeyWithFallback(countsToPatternRB.getAlias(), searchPluralCount, &ptLength, &status);
  521. if (U_SUCCESS(status)) {
  522. //found
  523. LocalPointer<MessageFormat> messageFormat(
  524. new MessageFormat(UnicodeString(true, pattern, ptLength), getLocale(err), err), err);
  525. if (U_FAILURE(err)) {
  526. return;
  527. }
  528. MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
  529. if (formatters == nullptr) {
  530. LocalMemory<MessageFormat *> localFormatters(
  531. (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
  532. formatters = localFormatters.getAlias();
  533. localFormatters[UTMUTFMT_FULL_STYLE] = nullptr;
  534. localFormatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
  535. countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
  536. if (U_FAILURE(err)) {
  537. return;
  538. }
  539. }
  540. //delete formatters[style];
  541. formatters[style] = messageFormat.orphan();
  542. return;
  543. }
  544. status = U_ZERO_ERROR;
  545. if (locNameLen == 0) {
  546. break;
  547. }
  548. }
  549. // if no unitsShort resource was found even after fallback to root locale
  550. // then search the units resource fallback from the current level to root
  551. if ( locNameLen == 0 && uprv_strcmp(key, gShortUnitsTag) == 0) {
  552. #ifdef TMUTFMT_DEBUG
  553. std::cout << "loop into searchInLocaleChain since Short-Long-Alternative \n";
  554. #endif
  555. CharString pLocale(localeName, -1, err);
  556. // Add an underscore at the tail of locale name,
  557. // so that searchInLocaleChain will check the current locale before falling back
  558. pLocale.append('_', err);
  559. searchInLocaleChain(style, gUnitsTag, pLocale.data(), srcTimeUnitField, srcPluralCount,
  560. searchPluralCount, countToPatterns, err);
  561. if (U_FAILURE(err)) {
  562. return;
  563. }
  564. MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
  565. if (formatters != nullptr && formatters[style] != nullptr) {
  566. return;
  567. }
  568. }
  569. // if not found the pattern for this plural count at all,
  570. // fall-back to plural count "other"
  571. if ( uprv_strcmp(searchPluralCount, gPluralCountOther) == 0 ) {
  572. // set default fall back the same as the resource in root
  573. LocalPointer<MessageFormat> messageFormat;
  574. const char16_t *pattern = nullptr;
  575. if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_SECOND ) {
  576. pattern = DEFAULT_PATTERN_FOR_SECOND;
  577. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MINUTE ) {
  578. pattern = DEFAULT_PATTERN_FOR_MINUTE;
  579. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_HOUR ) {
  580. pattern = DEFAULT_PATTERN_FOR_HOUR;
  581. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_WEEK ) {
  582. pattern = DEFAULT_PATTERN_FOR_WEEK;
  583. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_DAY ) {
  584. pattern = DEFAULT_PATTERN_FOR_DAY;
  585. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_MONTH ) {
  586. pattern = DEFAULT_PATTERN_FOR_MONTH;
  587. } else if ( srcTimeUnitField == TimeUnit::UTIMEUNIT_YEAR ) {
  588. pattern = DEFAULT_PATTERN_FOR_YEAR;
  589. }
  590. if (pattern != nullptr) {
  591. messageFormat.adoptInsteadAndCheckErrorCode(
  592. new MessageFormat(UnicodeString(true, pattern, -1), getLocale(err), err), err);
  593. }
  594. if (U_FAILURE(err)) {
  595. return;
  596. }
  597. MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
  598. if (formatters == nullptr) {
  599. LocalMemory<MessageFormat *> localFormatters (
  600. (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*)));
  601. if (localFormatters.isNull()) {
  602. err = U_MEMORY_ALLOCATION_ERROR;
  603. return;
  604. }
  605. formatters = localFormatters.getAlias();
  606. formatters[UTMUTFMT_FULL_STYLE] = nullptr;
  607. formatters[UTMUTFMT_ABBREVIATED_STYLE] = nullptr;
  608. countToPatterns->put(srcPluralCount, localFormatters.orphan(), err);
  609. }
  610. if (U_SUCCESS(err)) {
  611. //delete formatters[style];
  612. formatters[style] = messageFormat.orphan();
  613. }
  614. } else {
  615. // fall back to rule "other", and search in parents
  616. searchInLocaleChain(style, key, localeName, srcTimeUnitField, srcPluralCount,
  617. gPluralCountOther, countToPatterns, err);
  618. }
  619. }
  620. void
  621. TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
  622. if (setMeasureFormatLocale(locale, status)) {
  623. setup(status);
  624. }
  625. }
  626. void
  627. TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
  628. if (U_FAILURE(status)) {
  629. return;
  630. }
  631. adoptNumberFormat(format.clone(), status);
  632. }
  633. void
  634. TimeUnitFormat::deleteHash(Hashtable* htable) {
  635. int32_t pos = UHASH_FIRST;
  636. const UHashElement* element = nullptr;
  637. if ( htable ) {
  638. while ( (element = htable->nextElement(pos)) != nullptr ) {
  639. const UHashTok valueTok = element->value;
  640. const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
  641. delete value[UTMUTFMT_FULL_STYLE];
  642. delete value[UTMUTFMT_ABBREVIATED_STYLE];
  643. //delete[] value;
  644. uprv_free(value);
  645. }
  646. }
  647. delete htable;
  648. }
  649. void
  650. TimeUnitFormat::copyHash(const Hashtable* source, Hashtable* target, UErrorCode& status) {
  651. if ( U_FAILURE(status) ) {
  652. return;
  653. }
  654. int32_t pos = UHASH_FIRST;
  655. const UHashElement* element = nullptr;
  656. if ( source ) {
  657. while ( (element = source->nextElement(pos)) != nullptr ) {
  658. const UHashTok keyTok = element->key;
  659. const UnicodeString* key = (UnicodeString*)keyTok.pointer;
  660. const UHashTok valueTok = element->value;
  661. const MessageFormat** value = (const MessageFormat**)valueTok.pointer;
  662. MessageFormat** newVal = (MessageFormat**)uprv_malloc(UTMUTFMT_FORMAT_STYLE_COUNT*sizeof(MessageFormat*));
  663. newVal[0] = value[0]->clone();
  664. newVal[1] = value[1]->clone();
  665. target->put(UnicodeString(*key), newVal, status);
  666. if ( U_FAILURE(status) ) {
  667. delete newVal[0];
  668. delete newVal[1];
  669. uprv_free(newVal);
  670. return;
  671. }
  672. }
  673. }
  674. }
  675. U_CDECL_BEGIN
  676. /**
  677. * set hash table value comparator
  678. *
  679. * @param val1 one value in comparison
  680. * @param val2 the other value in comparison
  681. * @return true if 2 values are the same, false otherwise
  682. */
  683. static UBool U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2);
  684. static UBool
  685. U_CALLCONV tmutfmtHashTableValueComparator(UHashTok val1, UHashTok val2) {
  686. const MessageFormat** pattern1 = (const MessageFormat**)val1.pointer;
  687. const MessageFormat** pattern2 = (const MessageFormat**)val2.pointer;
  688. return *pattern1[0] == *pattern2[0] && *pattern1[1] == *pattern2[1];
  689. }
  690. U_CDECL_END
  691. Hashtable*
  692. TimeUnitFormat::initHash(UErrorCode& status) {
  693. if ( U_FAILURE(status) ) {
  694. return nullptr;
  695. }
  696. Hashtable* hTable;
  697. if ( (hTable = new Hashtable(true, status)) == nullptr ) {
  698. status = U_MEMORY_ALLOCATION_ERROR;
  699. return nullptr;
  700. }
  701. if ( U_FAILURE(status) ) {
  702. delete hTable;
  703. return nullptr;
  704. }
  705. hTable->setValueComparator(tmutfmtHashTableValueComparator);
  706. return hTable;
  707. }
  708. const char*
  709. TimeUnitFormat::getTimeUnitName(TimeUnit::UTimeUnitFields unitField,
  710. UErrorCode& status) {
  711. if (U_FAILURE(status)) {
  712. return nullptr;
  713. }
  714. switch (unitField) {
  715. case TimeUnit::UTIMEUNIT_YEAR:
  716. return gTimeUnitYear;
  717. case TimeUnit::UTIMEUNIT_MONTH:
  718. return gTimeUnitMonth;
  719. case TimeUnit::UTIMEUNIT_DAY:
  720. return gTimeUnitDay;
  721. case TimeUnit::UTIMEUNIT_WEEK:
  722. return gTimeUnitWeek;
  723. case TimeUnit::UTIMEUNIT_HOUR:
  724. return gTimeUnitHour;
  725. case TimeUnit::UTIMEUNIT_MINUTE:
  726. return gTimeUnitMinute;
  727. case TimeUnit::UTIMEUNIT_SECOND:
  728. return gTimeUnitSecond;
  729. default:
  730. status = U_ILLEGAL_ARGUMENT_ERROR;
  731. return nullptr;
  732. }
  733. }
  734. U_NAMESPACE_END
  735. #endif