number_skeletons.cpp 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  1. // © 2018 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. #include "unicode/utypes.h"
  4. #if !UCONFIG_NO_FORMATTING
  5. // Allow implicit conversion from char16_t* to UnicodeString for this file:
  6. // Helpful in toString methods and elsewhere.
  7. #define UNISTR_FROM_STRING_EXPLICIT
  8. #include "number_decnum.h"
  9. #include "number_roundingutils.h"
  10. #include "number_skeletons.h"
  11. #include "umutex.h"
  12. #include "ucln_in.h"
  13. #include "patternprops.h"
  14. #include "unicode/ucharstriebuilder.h"
  15. #include "number_utils.h"
  16. #include "number_decimalquantity.h"
  17. #include "unicode/numberformatter.h"
  18. #include "uinvchar.h"
  19. #include "charstr.h"
  20. #include "string_segment.h"
  21. #include "unicode/errorcode.h"
  22. #include "util.h"
  23. #include "measunit_impl.h"
  24. using namespace icu;
  25. using namespace icu::number;
  26. using namespace icu::number::impl;
  27. using namespace icu::number::impl::skeleton;
  28. namespace {
  29. icu::UInitOnce gNumberSkeletonsInitOnce {};
  30. char16_t* kSerializedStemTrie = nullptr;
  31. UBool U_CALLCONV cleanupNumberSkeletons() {
  32. uprv_free(kSerializedStemTrie);
  33. kSerializedStemTrie = nullptr;
  34. gNumberSkeletonsInitOnce.reset();
  35. return true;
  36. }
  37. void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
  38. ucln_i18n_registerCleanup(UCLN_I18N_NUMBER_SKELETONS, cleanupNumberSkeletons);
  39. UCharsTrieBuilder b(status);
  40. if (U_FAILURE(status)) { return; }
  41. // Section 1:
  42. b.add(u"compact-short", STEM_COMPACT_SHORT, status);
  43. b.add(u"compact-long", STEM_COMPACT_LONG, status);
  44. b.add(u"scientific", STEM_SCIENTIFIC, status);
  45. b.add(u"engineering", STEM_ENGINEERING, status);
  46. b.add(u"notation-simple", STEM_NOTATION_SIMPLE, status);
  47. b.add(u"base-unit", STEM_BASE_UNIT, status);
  48. b.add(u"percent", STEM_PERCENT, status);
  49. b.add(u"permille", STEM_PERMILLE, status);
  50. b.add(u"precision-integer", STEM_PRECISION_INTEGER, status);
  51. b.add(u"precision-unlimited", STEM_PRECISION_UNLIMITED, status);
  52. b.add(u"precision-currency-standard", STEM_PRECISION_CURRENCY_STANDARD, status);
  53. b.add(u"precision-currency-cash", STEM_PRECISION_CURRENCY_CASH, status);
  54. b.add(u"rounding-mode-ceiling", STEM_ROUNDING_MODE_CEILING, status);
  55. b.add(u"rounding-mode-floor", STEM_ROUNDING_MODE_FLOOR, status);
  56. b.add(u"rounding-mode-down", STEM_ROUNDING_MODE_DOWN, status);
  57. b.add(u"rounding-mode-up", STEM_ROUNDING_MODE_UP, status);
  58. b.add(u"rounding-mode-half-even", STEM_ROUNDING_MODE_HALF_EVEN, status);
  59. b.add(u"rounding-mode-half-odd", STEM_ROUNDING_MODE_HALF_ODD, status);
  60. b.add(u"rounding-mode-half-ceiling", STEM_ROUNDING_MODE_HALF_CEILING, status);
  61. b.add(u"rounding-mode-half-floor", STEM_ROUNDING_MODE_HALF_FLOOR, status);
  62. b.add(u"rounding-mode-half-down", STEM_ROUNDING_MODE_HALF_DOWN, status);
  63. b.add(u"rounding-mode-half-up", STEM_ROUNDING_MODE_HALF_UP, status);
  64. b.add(u"rounding-mode-unnecessary", STEM_ROUNDING_MODE_UNNECESSARY, status);
  65. b.add(u"integer-width-trunc", STEM_INTEGER_WIDTH_TRUNC, status);
  66. b.add(u"group-off", STEM_GROUP_OFF, status);
  67. b.add(u"group-min2", STEM_GROUP_MIN2, status);
  68. b.add(u"group-auto", STEM_GROUP_AUTO, status);
  69. b.add(u"group-on-aligned", STEM_GROUP_ON_ALIGNED, status);
  70. b.add(u"group-thousands", STEM_GROUP_THOUSANDS, status);
  71. b.add(u"latin", STEM_LATIN, status);
  72. b.add(u"unit-width-narrow", STEM_UNIT_WIDTH_NARROW, status);
  73. b.add(u"unit-width-short", STEM_UNIT_WIDTH_SHORT, status);
  74. b.add(u"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME, status);
  75. b.add(u"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE, status);
  76. b.add(u"unit-width-formal", STEM_UNIT_WIDTH_FORMAL, status);
  77. b.add(u"unit-width-variant", STEM_UNIT_WIDTH_VARIANT, status);
  78. b.add(u"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN, status);
  79. b.add(u"sign-auto", STEM_SIGN_AUTO, status);
  80. b.add(u"sign-always", STEM_SIGN_ALWAYS, status);
  81. b.add(u"sign-never", STEM_SIGN_NEVER, status);
  82. b.add(u"sign-accounting", STEM_SIGN_ACCOUNTING, status);
  83. b.add(u"sign-accounting-always", STEM_SIGN_ACCOUNTING_ALWAYS, status);
  84. b.add(u"sign-except-zero", STEM_SIGN_EXCEPT_ZERO, status);
  85. b.add(u"sign-accounting-except-zero", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status);
  86. b.add(u"sign-negative", STEM_SIGN_NEGATIVE, status);
  87. b.add(u"sign-accounting-negative", STEM_SIGN_ACCOUNTING_NEGATIVE, status);
  88. b.add(u"decimal-auto", STEM_DECIMAL_AUTO, status);
  89. b.add(u"decimal-always", STEM_DECIMAL_ALWAYS, status);
  90. if (U_FAILURE(status)) { return; }
  91. // Section 2:
  92. b.add(u"precision-increment", STEM_PRECISION_INCREMENT, status);
  93. b.add(u"measure-unit", STEM_MEASURE_UNIT, status);
  94. b.add(u"per-measure-unit", STEM_PER_MEASURE_UNIT, status);
  95. b.add(u"unit", STEM_UNIT, status);
  96. b.add(u"usage", STEM_UNIT_USAGE, status);
  97. b.add(u"currency", STEM_CURRENCY, status);
  98. b.add(u"integer-width", STEM_INTEGER_WIDTH, status);
  99. b.add(u"numbering-system", STEM_NUMBERING_SYSTEM, status);
  100. b.add(u"scale", STEM_SCALE, status);
  101. if (U_FAILURE(status)) { return; }
  102. // Section 3 (concise tokens):
  103. b.add(u"K", STEM_COMPACT_SHORT, status);
  104. b.add(u"KK", STEM_COMPACT_LONG, status);
  105. b.add(u"%", STEM_PERCENT, status);
  106. b.add(u"%x100", STEM_PERCENT_100, status);
  107. b.add(u",_", STEM_GROUP_OFF, status);
  108. b.add(u",?", STEM_GROUP_MIN2, status);
  109. b.add(u",!", STEM_GROUP_ON_ALIGNED, status);
  110. b.add(u"+!", STEM_SIGN_ALWAYS, status);
  111. b.add(u"+_", STEM_SIGN_NEVER, status);
  112. b.add(u"()", STEM_SIGN_ACCOUNTING, status);
  113. b.add(u"()!", STEM_SIGN_ACCOUNTING_ALWAYS, status);
  114. b.add(u"+?", STEM_SIGN_EXCEPT_ZERO, status);
  115. b.add(u"()?", STEM_SIGN_ACCOUNTING_EXCEPT_ZERO, status);
  116. b.add(u"+-", STEM_SIGN_NEGATIVE, status);
  117. b.add(u"()-", STEM_SIGN_ACCOUNTING_NEGATIVE, status);
  118. if (U_FAILURE(status)) { return; }
  119. // Build the CharsTrie
  120. // TODO: Use SLOW or FAST here?
  121. UnicodeString result;
  122. b.buildUnicodeString(USTRINGTRIE_BUILD_FAST, result, status);
  123. if (U_FAILURE(status)) { return; }
  124. // Copy the result into the global constant pointer
  125. size_t numBytes = result.length() * sizeof(char16_t);
  126. kSerializedStemTrie = static_cast<char16_t*>(uprv_malloc(numBytes));
  127. uprv_memcpy(kSerializedStemTrie, result.getBuffer(), numBytes);
  128. }
  129. inline void appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) {
  130. for (int i = 0; i < count; i++) {
  131. sb.append(cp);
  132. }
  133. }
  134. #define CHECK_NULL(seen, field, status) (void)(seen); /* for auto-format line wrapping */ \
  135. UPRV_BLOCK_MACRO_BEGIN { \
  136. if ((seen).field) { \
  137. (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \
  138. return STATE_NULL; \
  139. } \
  140. (seen).field = true; \
  141. } UPRV_BLOCK_MACRO_END
  142. } // anonymous namespace
  143. Notation stem_to_object::notation(skeleton::StemEnum stem) {
  144. switch (stem) {
  145. case STEM_COMPACT_SHORT:
  146. return Notation::compactShort();
  147. case STEM_COMPACT_LONG:
  148. return Notation::compactLong();
  149. case STEM_SCIENTIFIC:
  150. return Notation::scientific();
  151. case STEM_ENGINEERING:
  152. return Notation::engineering();
  153. case STEM_NOTATION_SIMPLE:
  154. return Notation::simple();
  155. default:
  156. UPRV_UNREACHABLE_EXIT;
  157. }
  158. }
  159. MeasureUnit stem_to_object::unit(skeleton::StemEnum stem) {
  160. switch (stem) {
  161. case STEM_BASE_UNIT:
  162. return MeasureUnit();
  163. case STEM_PERCENT:
  164. return MeasureUnit::getPercent();
  165. case STEM_PERMILLE:
  166. return MeasureUnit::getPermille();
  167. default:
  168. UPRV_UNREACHABLE_EXIT;
  169. }
  170. }
  171. Precision stem_to_object::precision(skeleton::StemEnum stem) {
  172. switch (stem) {
  173. case STEM_PRECISION_INTEGER:
  174. return Precision::integer();
  175. case STEM_PRECISION_UNLIMITED:
  176. return Precision::unlimited();
  177. case STEM_PRECISION_CURRENCY_STANDARD:
  178. return Precision::currency(UCURR_USAGE_STANDARD);
  179. case STEM_PRECISION_CURRENCY_CASH:
  180. return Precision::currency(UCURR_USAGE_CASH);
  181. default:
  182. UPRV_UNREACHABLE_EXIT;
  183. }
  184. }
  185. UNumberFormatRoundingMode stem_to_object::roundingMode(skeleton::StemEnum stem) {
  186. switch (stem) {
  187. case STEM_ROUNDING_MODE_CEILING:
  188. return UNUM_ROUND_CEILING;
  189. case STEM_ROUNDING_MODE_FLOOR:
  190. return UNUM_ROUND_FLOOR;
  191. case STEM_ROUNDING_MODE_DOWN:
  192. return UNUM_ROUND_DOWN;
  193. case STEM_ROUNDING_MODE_UP:
  194. return UNUM_ROUND_UP;
  195. case STEM_ROUNDING_MODE_HALF_EVEN:
  196. return UNUM_ROUND_HALFEVEN;
  197. case STEM_ROUNDING_MODE_HALF_ODD:
  198. return UNUM_ROUND_HALF_ODD;
  199. case STEM_ROUNDING_MODE_HALF_CEILING:
  200. return UNUM_ROUND_HALF_CEILING;
  201. case STEM_ROUNDING_MODE_HALF_FLOOR:
  202. return UNUM_ROUND_HALF_FLOOR;
  203. case STEM_ROUNDING_MODE_HALF_DOWN:
  204. return UNUM_ROUND_HALFDOWN;
  205. case STEM_ROUNDING_MODE_HALF_UP:
  206. return UNUM_ROUND_HALFUP;
  207. case STEM_ROUNDING_MODE_UNNECESSARY:
  208. return UNUM_ROUND_UNNECESSARY;
  209. default:
  210. UPRV_UNREACHABLE_EXIT;
  211. }
  212. }
  213. UNumberGroupingStrategy stem_to_object::groupingStrategy(skeleton::StemEnum stem) {
  214. switch (stem) {
  215. case STEM_GROUP_OFF:
  216. return UNUM_GROUPING_OFF;
  217. case STEM_GROUP_MIN2:
  218. return UNUM_GROUPING_MIN2;
  219. case STEM_GROUP_AUTO:
  220. return UNUM_GROUPING_AUTO;
  221. case STEM_GROUP_ON_ALIGNED:
  222. return UNUM_GROUPING_ON_ALIGNED;
  223. case STEM_GROUP_THOUSANDS:
  224. return UNUM_GROUPING_THOUSANDS;
  225. default:
  226. return UNUM_GROUPING_COUNT; // for objects, throw; for enums, return COUNT
  227. }
  228. }
  229. UNumberUnitWidth stem_to_object::unitWidth(skeleton::StemEnum stem) {
  230. switch (stem) {
  231. case STEM_UNIT_WIDTH_NARROW:
  232. return UNUM_UNIT_WIDTH_NARROW;
  233. case STEM_UNIT_WIDTH_SHORT:
  234. return UNUM_UNIT_WIDTH_SHORT;
  235. case STEM_UNIT_WIDTH_FULL_NAME:
  236. return UNUM_UNIT_WIDTH_FULL_NAME;
  237. case STEM_UNIT_WIDTH_ISO_CODE:
  238. return UNUM_UNIT_WIDTH_ISO_CODE;
  239. case STEM_UNIT_WIDTH_FORMAL:
  240. return UNUM_UNIT_WIDTH_FORMAL;
  241. case STEM_UNIT_WIDTH_VARIANT:
  242. return UNUM_UNIT_WIDTH_VARIANT;
  243. case STEM_UNIT_WIDTH_HIDDEN:
  244. return UNUM_UNIT_WIDTH_HIDDEN;
  245. default:
  246. return UNUM_UNIT_WIDTH_COUNT; // for objects, throw; for enums, return COUNT
  247. }
  248. }
  249. UNumberSignDisplay stem_to_object::signDisplay(skeleton::StemEnum stem) {
  250. switch (stem) {
  251. case STEM_SIGN_AUTO:
  252. return UNUM_SIGN_AUTO;
  253. case STEM_SIGN_ALWAYS:
  254. return UNUM_SIGN_ALWAYS;
  255. case STEM_SIGN_NEVER:
  256. return UNUM_SIGN_NEVER;
  257. case STEM_SIGN_ACCOUNTING:
  258. return UNUM_SIGN_ACCOUNTING;
  259. case STEM_SIGN_ACCOUNTING_ALWAYS:
  260. return UNUM_SIGN_ACCOUNTING_ALWAYS;
  261. case STEM_SIGN_EXCEPT_ZERO:
  262. return UNUM_SIGN_EXCEPT_ZERO;
  263. case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
  264. return UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
  265. case STEM_SIGN_NEGATIVE:
  266. return UNUM_SIGN_NEGATIVE;
  267. case STEM_SIGN_ACCOUNTING_NEGATIVE:
  268. return UNUM_SIGN_ACCOUNTING_NEGATIVE;
  269. default:
  270. return UNUM_SIGN_COUNT; // for objects, throw; for enums, return COUNT
  271. }
  272. }
  273. UNumberDecimalSeparatorDisplay stem_to_object::decimalSeparatorDisplay(skeleton::StemEnum stem) {
  274. switch (stem) {
  275. case STEM_DECIMAL_AUTO:
  276. return UNUM_DECIMAL_SEPARATOR_AUTO;
  277. case STEM_DECIMAL_ALWAYS:
  278. return UNUM_DECIMAL_SEPARATOR_ALWAYS;
  279. default:
  280. return UNUM_DECIMAL_SEPARATOR_COUNT; // for objects, throw; for enums, return COUNT
  281. }
  282. }
  283. void enum_to_stem_string::roundingMode(UNumberFormatRoundingMode value, UnicodeString& sb) {
  284. switch (value) {
  285. case UNUM_ROUND_CEILING:
  286. sb.append(u"rounding-mode-ceiling", -1);
  287. break;
  288. case UNUM_ROUND_FLOOR:
  289. sb.append(u"rounding-mode-floor", -1);
  290. break;
  291. case UNUM_ROUND_DOWN:
  292. sb.append(u"rounding-mode-down", -1);
  293. break;
  294. case UNUM_ROUND_UP:
  295. sb.append(u"rounding-mode-up", -1);
  296. break;
  297. case UNUM_ROUND_HALFEVEN:
  298. sb.append(u"rounding-mode-half-even", -1);
  299. break;
  300. case UNUM_ROUND_HALF_ODD:
  301. sb.append(u"rounding-mode-half-odd", -1);
  302. break;
  303. case UNUM_ROUND_HALF_CEILING:
  304. sb.append(u"rounding-mode-half-ceiling", -1);
  305. break;
  306. case UNUM_ROUND_HALF_FLOOR:
  307. sb.append(u"rounding-mode-half-floor", -1);
  308. break;
  309. case UNUM_ROUND_HALFDOWN:
  310. sb.append(u"rounding-mode-half-down", -1);
  311. break;
  312. case UNUM_ROUND_HALFUP:
  313. sb.append(u"rounding-mode-half-up", -1);
  314. break;
  315. case UNUM_ROUND_UNNECESSARY:
  316. sb.append(u"rounding-mode-unnecessary", -1);
  317. break;
  318. default:
  319. UPRV_UNREACHABLE_EXIT;
  320. }
  321. }
  322. void enum_to_stem_string::groupingStrategy(UNumberGroupingStrategy value, UnicodeString& sb) {
  323. switch (value) {
  324. case UNUM_GROUPING_OFF:
  325. sb.append(u"group-off", -1);
  326. break;
  327. case UNUM_GROUPING_MIN2:
  328. sb.append(u"group-min2", -1);
  329. break;
  330. case UNUM_GROUPING_AUTO:
  331. sb.append(u"group-auto", -1);
  332. break;
  333. case UNUM_GROUPING_ON_ALIGNED:
  334. sb.append(u"group-on-aligned", -1);
  335. break;
  336. case UNUM_GROUPING_THOUSANDS:
  337. sb.append(u"group-thousands", -1);
  338. break;
  339. default:
  340. UPRV_UNREACHABLE_EXIT;
  341. }
  342. }
  343. void enum_to_stem_string::unitWidth(UNumberUnitWidth value, UnicodeString& sb) {
  344. switch (value) {
  345. case UNUM_UNIT_WIDTH_NARROW:
  346. sb.append(u"unit-width-narrow", -1);
  347. break;
  348. case UNUM_UNIT_WIDTH_SHORT:
  349. sb.append(u"unit-width-short", -1);
  350. break;
  351. case UNUM_UNIT_WIDTH_FULL_NAME:
  352. sb.append(u"unit-width-full-name", -1);
  353. break;
  354. case UNUM_UNIT_WIDTH_ISO_CODE:
  355. sb.append(u"unit-width-iso-code", -1);
  356. break;
  357. case UNUM_UNIT_WIDTH_FORMAL:
  358. sb.append(u"unit-width-formal", -1);
  359. break;
  360. case UNUM_UNIT_WIDTH_VARIANT:
  361. sb.append(u"unit-width-variant", -1);
  362. break;
  363. case UNUM_UNIT_WIDTH_HIDDEN:
  364. sb.append(u"unit-width-hidden", -1);
  365. break;
  366. default:
  367. UPRV_UNREACHABLE_EXIT;
  368. }
  369. }
  370. void enum_to_stem_string::signDisplay(UNumberSignDisplay value, UnicodeString& sb) {
  371. switch (value) {
  372. case UNUM_SIGN_AUTO:
  373. sb.append(u"sign-auto", -1);
  374. break;
  375. case UNUM_SIGN_ALWAYS:
  376. sb.append(u"sign-always", -1);
  377. break;
  378. case UNUM_SIGN_NEVER:
  379. sb.append(u"sign-never", -1);
  380. break;
  381. case UNUM_SIGN_ACCOUNTING:
  382. sb.append(u"sign-accounting", -1);
  383. break;
  384. case UNUM_SIGN_ACCOUNTING_ALWAYS:
  385. sb.append(u"sign-accounting-always", -1);
  386. break;
  387. case UNUM_SIGN_EXCEPT_ZERO:
  388. sb.append(u"sign-except-zero", -1);
  389. break;
  390. case UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO:
  391. sb.append(u"sign-accounting-except-zero", -1);
  392. break;
  393. case UNUM_SIGN_NEGATIVE:
  394. sb.append(u"sign-negative", -1);
  395. break;
  396. case UNUM_SIGN_ACCOUNTING_NEGATIVE:
  397. sb.append(u"sign-accounting-negative", -1);
  398. break;
  399. default:
  400. UPRV_UNREACHABLE_EXIT;
  401. }
  402. }
  403. void
  404. enum_to_stem_string::decimalSeparatorDisplay(UNumberDecimalSeparatorDisplay value, UnicodeString& sb) {
  405. switch (value) {
  406. case UNUM_DECIMAL_SEPARATOR_AUTO:
  407. sb.append(u"decimal-auto", -1);
  408. break;
  409. case UNUM_DECIMAL_SEPARATOR_ALWAYS:
  410. sb.append(u"decimal-always", -1);
  411. break;
  412. default:
  413. UPRV_UNREACHABLE_EXIT;
  414. }
  415. }
  416. UnlocalizedNumberFormatter skeleton::create(
  417. const UnicodeString& skeletonString, UParseError* perror, UErrorCode& status) {
  418. // Initialize perror
  419. if (perror != nullptr) {
  420. perror->line = 0;
  421. perror->offset = -1;
  422. perror->preContext[0] = 0;
  423. perror->postContext[0] = 0;
  424. }
  425. umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status);
  426. if (U_FAILURE(status)) {
  427. return {};
  428. }
  429. int32_t errOffset;
  430. MacroProps macros = parseSkeleton(skeletonString, errOffset, status);
  431. if (U_SUCCESS(status)) {
  432. return NumberFormatter::with().macros(macros);
  433. }
  434. if (perror == nullptr) {
  435. return {};
  436. }
  437. // Populate the UParseError with the error location
  438. perror->offset = errOffset;
  439. int32_t contextStart = uprv_max(0, errOffset - U_PARSE_CONTEXT_LEN + 1);
  440. int32_t contextEnd = uprv_min(skeletonString.length(), errOffset + U_PARSE_CONTEXT_LEN - 1);
  441. skeletonString.extract(contextStart, errOffset - contextStart, perror->preContext, 0);
  442. perror->preContext[errOffset - contextStart] = 0;
  443. skeletonString.extract(errOffset, contextEnd - errOffset, perror->postContext, 0);
  444. perror->postContext[contextEnd - errOffset] = 0;
  445. return {};
  446. }
  447. UnicodeString skeleton::generate(const MacroProps& macros, UErrorCode& status) {
  448. umtx_initOnce(gNumberSkeletonsInitOnce, &initNumberSkeletons, status);
  449. UnicodeString sb;
  450. GeneratorHelpers::generateSkeleton(macros, sb, status);
  451. return sb;
  452. }
  453. MacroProps skeleton::parseSkeleton(
  454. const UnicodeString& skeletonString, int32_t& errOffset, UErrorCode& status) {
  455. U_ASSERT(U_SUCCESS(status));
  456. U_ASSERT(kSerializedStemTrie != nullptr);
  457. // Add a trailing whitespace to the end of the skeleton string to make code cleaner.
  458. UnicodeString tempSkeletonString(skeletonString);
  459. tempSkeletonString.append(u' ');
  460. SeenMacroProps seen;
  461. MacroProps macros;
  462. StringSegment segment(tempSkeletonString, false);
  463. UCharsTrie stemTrie(kSerializedStemTrie);
  464. ParseState stem = STATE_NULL;
  465. int32_t offset = 0;
  466. // Primary skeleton parse loop:
  467. while (offset < segment.length()) {
  468. UChar32 cp = segment.codePointAt(offset);
  469. bool isTokenSeparator = PatternProps::isWhiteSpace(cp);
  470. bool isOptionSeparator = (cp == u'/');
  471. if (!isTokenSeparator && !isOptionSeparator) {
  472. // Non-separator token; consume it.
  473. offset += U16_LENGTH(cp);
  474. if (stem == STATE_NULL) {
  475. // We are currently consuming a stem.
  476. // Go to the next state in the stem trie.
  477. stemTrie.nextForCodePoint(cp);
  478. }
  479. continue;
  480. }
  481. // We are looking at a token or option separator.
  482. // If the segment is nonempty, parse it and reset the segment.
  483. // Otherwise, make sure it is a valid repeating separator.
  484. if (offset != 0) {
  485. segment.setLength(offset);
  486. if (stem == STATE_NULL) {
  487. // The first separator after the start of a token. Parse it as a stem.
  488. stem = parseStem(segment, stemTrie, seen, macros, status);
  489. stemTrie.reset();
  490. } else {
  491. // A separator after the first separator of a token. Parse it as an option.
  492. stem = parseOption(stem, segment, macros, status);
  493. }
  494. segment.resetLength();
  495. if (U_FAILURE(status)) {
  496. errOffset = segment.getOffset();
  497. return macros;
  498. }
  499. // Consume the segment:
  500. segment.adjustOffset(offset);
  501. offset = 0;
  502. } else if (stem != STATE_NULL) {
  503. // A separator ('/' or whitespace) following an option separator ('/')
  504. // segment.setLength(U16_LENGTH(cp)); // for error message
  505. // throw new SkeletonSyntaxException("Unexpected separator character", segment);
  506. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  507. errOffset = segment.getOffset();
  508. return macros;
  509. } else {
  510. // Two spaces in a row; this is OK.
  511. }
  512. // Does the current stem forbid options?
  513. if (isOptionSeparator && stem == STATE_NULL) {
  514. // segment.setLength(U16_LENGTH(cp)); // for error message
  515. // throw new SkeletonSyntaxException("Unexpected option separator", segment);
  516. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  517. errOffset = segment.getOffset();
  518. return macros;
  519. }
  520. // Does the current stem require an option?
  521. if (isTokenSeparator && stem != STATE_NULL) {
  522. switch (stem) {
  523. case STATE_INCREMENT_PRECISION:
  524. case STATE_MEASURE_UNIT:
  525. case STATE_PER_MEASURE_UNIT:
  526. case STATE_IDENTIFIER_UNIT:
  527. case STATE_UNIT_USAGE:
  528. case STATE_CURRENCY_UNIT:
  529. case STATE_INTEGER_WIDTH:
  530. case STATE_NUMBERING_SYSTEM:
  531. case STATE_SCALE:
  532. // segment.setLength(U16_LENGTH(cp)); // for error message
  533. // throw new SkeletonSyntaxException("Stem requires an option", segment);
  534. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  535. errOffset = segment.getOffset();
  536. return macros;
  537. default:
  538. break;
  539. }
  540. stem = STATE_NULL;
  541. }
  542. // Consume the separator:
  543. segment.adjustOffset(U16_LENGTH(cp));
  544. }
  545. U_ASSERT(stem == STATE_NULL);
  546. return macros;
  547. }
  548. ParseState
  549. skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, SeenMacroProps& seen,
  550. MacroProps& macros, UErrorCode& status) {
  551. U_ASSERT(U_SUCCESS(status));
  552. // First check for "blueprint" stems, which start with a "signal char"
  553. switch (segment.charAt(0)) {
  554. case u'.':
  555. CHECK_NULL(seen, precision, status);
  556. blueprint_helpers::parseFractionStem(segment, macros, status);
  557. return STATE_FRACTION_PRECISION;
  558. case u'@':
  559. CHECK_NULL(seen, precision, status);
  560. blueprint_helpers::parseDigitsStem(segment, macros, status);
  561. return STATE_PRECISION;
  562. case u'E':
  563. CHECK_NULL(seen, notation, status);
  564. blueprint_helpers::parseScientificStem(segment, macros, status);
  565. return STATE_NULL;
  566. case u'0':
  567. CHECK_NULL(seen, integerWidth, status);
  568. blueprint_helpers::parseIntegerStem(segment, macros, status);
  569. return STATE_NULL;
  570. default:
  571. break;
  572. }
  573. // Now look at the stemsTrie, which is already be pointing at our stem.
  574. UStringTrieResult stemResult = stemTrie.current();
  575. if (stemResult != USTRINGTRIE_INTERMEDIATE_VALUE && stemResult != USTRINGTRIE_FINAL_VALUE) {
  576. // throw new SkeletonSyntaxException("Unknown stem", segment);
  577. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  578. return STATE_NULL;
  579. }
  580. auto stem = static_cast<StemEnum>(stemTrie.getValue());
  581. switch (stem) {
  582. // Stems with meaning on their own, not requiring an option:
  583. case STEM_COMPACT_SHORT:
  584. case STEM_COMPACT_LONG:
  585. case STEM_SCIENTIFIC:
  586. case STEM_ENGINEERING:
  587. case STEM_NOTATION_SIMPLE:
  588. CHECK_NULL(seen, notation, status);
  589. macros.notation = stem_to_object::notation(stem);
  590. switch (stem) {
  591. case STEM_SCIENTIFIC:
  592. case STEM_ENGINEERING:
  593. return STATE_SCIENTIFIC; // allows for scientific options
  594. default:
  595. return STATE_NULL;
  596. }
  597. case STEM_BASE_UNIT:
  598. case STEM_PERCENT:
  599. case STEM_PERMILLE:
  600. CHECK_NULL(seen, unit, status);
  601. macros.unit = stem_to_object::unit(stem);
  602. return STATE_NULL;
  603. case STEM_PERCENT_100:
  604. CHECK_NULL(seen, scale, status);
  605. CHECK_NULL(seen, unit, status);
  606. macros.scale = Scale::powerOfTen(2);
  607. macros.unit = NoUnit::percent();
  608. return STATE_NULL;
  609. case STEM_PRECISION_INTEGER:
  610. case STEM_PRECISION_UNLIMITED:
  611. case STEM_PRECISION_CURRENCY_STANDARD:
  612. case STEM_PRECISION_CURRENCY_CASH:
  613. CHECK_NULL(seen, precision, status);
  614. macros.precision = stem_to_object::precision(stem);
  615. switch (stem) {
  616. case STEM_PRECISION_INTEGER:
  617. return STATE_FRACTION_PRECISION; // allows for "precision-integer/@##"
  618. default:
  619. return STATE_PRECISION;
  620. }
  621. case STEM_ROUNDING_MODE_CEILING:
  622. case STEM_ROUNDING_MODE_FLOOR:
  623. case STEM_ROUNDING_MODE_DOWN:
  624. case STEM_ROUNDING_MODE_UP:
  625. case STEM_ROUNDING_MODE_HALF_EVEN:
  626. case STEM_ROUNDING_MODE_HALF_ODD:
  627. case STEM_ROUNDING_MODE_HALF_CEILING:
  628. case STEM_ROUNDING_MODE_HALF_FLOOR:
  629. case STEM_ROUNDING_MODE_HALF_DOWN:
  630. case STEM_ROUNDING_MODE_HALF_UP:
  631. case STEM_ROUNDING_MODE_UNNECESSARY:
  632. CHECK_NULL(seen, roundingMode, status);
  633. macros.roundingMode = stem_to_object::roundingMode(stem);
  634. return STATE_NULL;
  635. case STEM_INTEGER_WIDTH_TRUNC:
  636. CHECK_NULL(seen, integerWidth, status);
  637. macros.integerWidth = IntegerWidth::zeroFillTo(0).truncateAt(0);
  638. return STATE_NULL;
  639. case STEM_GROUP_OFF:
  640. case STEM_GROUP_MIN2:
  641. case STEM_GROUP_AUTO:
  642. case STEM_GROUP_ON_ALIGNED:
  643. case STEM_GROUP_THOUSANDS:
  644. CHECK_NULL(seen, grouper, status);
  645. macros.grouper = Grouper::forStrategy(stem_to_object::groupingStrategy(stem));
  646. return STATE_NULL;
  647. case STEM_LATIN:
  648. CHECK_NULL(seen, symbols, status);
  649. macros.symbols.setTo(NumberingSystem::createInstanceByName("latn", status));
  650. return STATE_NULL;
  651. case STEM_UNIT_WIDTH_NARROW:
  652. case STEM_UNIT_WIDTH_SHORT:
  653. case STEM_UNIT_WIDTH_FULL_NAME:
  654. case STEM_UNIT_WIDTH_ISO_CODE:
  655. case STEM_UNIT_WIDTH_FORMAL:
  656. case STEM_UNIT_WIDTH_VARIANT:
  657. case STEM_UNIT_WIDTH_HIDDEN:
  658. CHECK_NULL(seen, unitWidth, status);
  659. macros.unitWidth = stem_to_object::unitWidth(stem);
  660. return STATE_NULL;
  661. case STEM_SIGN_AUTO:
  662. case STEM_SIGN_ALWAYS:
  663. case STEM_SIGN_NEVER:
  664. case STEM_SIGN_ACCOUNTING:
  665. case STEM_SIGN_ACCOUNTING_ALWAYS:
  666. case STEM_SIGN_EXCEPT_ZERO:
  667. case STEM_SIGN_ACCOUNTING_EXCEPT_ZERO:
  668. case STEM_SIGN_NEGATIVE:
  669. case STEM_SIGN_ACCOUNTING_NEGATIVE:
  670. CHECK_NULL(seen, sign, status);
  671. macros.sign = stem_to_object::signDisplay(stem);
  672. return STATE_NULL;
  673. case STEM_DECIMAL_AUTO:
  674. case STEM_DECIMAL_ALWAYS:
  675. CHECK_NULL(seen, decimal, status);
  676. macros.decimal = stem_to_object::decimalSeparatorDisplay(stem);
  677. return STATE_NULL;
  678. // Stems requiring an option:
  679. case STEM_PRECISION_INCREMENT:
  680. CHECK_NULL(seen, precision, status);
  681. return STATE_INCREMENT_PRECISION;
  682. case STEM_MEASURE_UNIT:
  683. CHECK_NULL(seen, unit, status);
  684. return STATE_MEASURE_UNIT;
  685. case STEM_PER_MEASURE_UNIT:
  686. CHECK_NULL(seen, perUnit, status);
  687. return STATE_PER_MEASURE_UNIT;
  688. case STEM_UNIT:
  689. CHECK_NULL(seen, unit, status);
  690. CHECK_NULL(seen, perUnit, status);
  691. return STATE_IDENTIFIER_UNIT;
  692. case STEM_UNIT_USAGE:
  693. CHECK_NULL(seen, usage, status);
  694. return STATE_UNIT_USAGE;
  695. case STEM_CURRENCY:
  696. CHECK_NULL(seen, unit, status);
  697. CHECK_NULL(seen, perUnit, status);
  698. return STATE_CURRENCY_UNIT;
  699. case STEM_INTEGER_WIDTH:
  700. CHECK_NULL(seen, integerWidth, status);
  701. return STATE_INTEGER_WIDTH;
  702. case STEM_NUMBERING_SYSTEM:
  703. CHECK_NULL(seen, symbols, status);
  704. return STATE_NUMBERING_SYSTEM;
  705. case STEM_SCALE:
  706. CHECK_NULL(seen, scale, status);
  707. return STATE_SCALE;
  708. default:
  709. UPRV_UNREACHABLE_EXIT;
  710. }
  711. }
  712. ParseState skeleton::parseOption(ParseState stem, const StringSegment& segment, MacroProps& macros,
  713. UErrorCode& status) {
  714. U_ASSERT(U_SUCCESS(status));
  715. ///// Required options: /////
  716. switch (stem) {
  717. case STATE_CURRENCY_UNIT:
  718. blueprint_helpers::parseCurrencyOption(segment, macros, status);
  719. return STATE_NULL;
  720. case STATE_MEASURE_UNIT:
  721. blueprint_helpers::parseMeasureUnitOption(segment, macros, status);
  722. return STATE_NULL;
  723. case STATE_PER_MEASURE_UNIT:
  724. blueprint_helpers::parseMeasurePerUnitOption(segment, macros, status);
  725. return STATE_NULL;
  726. case STATE_IDENTIFIER_UNIT:
  727. blueprint_helpers::parseIdentifierUnitOption(segment, macros, status);
  728. return STATE_NULL;
  729. case STATE_UNIT_USAGE:
  730. blueprint_helpers::parseUnitUsageOption(segment, macros, status);
  731. return STATE_NULL;
  732. case STATE_INCREMENT_PRECISION:
  733. blueprint_helpers::parseIncrementOption(segment, macros, status);
  734. return STATE_PRECISION;
  735. case STATE_INTEGER_WIDTH:
  736. blueprint_helpers::parseIntegerWidthOption(segment, macros, status);
  737. return STATE_NULL;
  738. case STATE_NUMBERING_SYSTEM:
  739. blueprint_helpers::parseNumberingSystemOption(segment, macros, status);
  740. return STATE_NULL;
  741. case STATE_SCALE:
  742. blueprint_helpers::parseScaleOption(segment, macros, status);
  743. return STATE_NULL;
  744. default:
  745. break;
  746. }
  747. ///// Non-required options: /////
  748. // Scientific options
  749. switch (stem) {
  750. case STATE_SCIENTIFIC:
  751. if (blueprint_helpers::parseExponentWidthOption(segment, macros, status)) {
  752. return STATE_SCIENTIFIC;
  753. }
  754. if (U_FAILURE(status)) {
  755. return {};
  756. }
  757. if (blueprint_helpers::parseExponentSignOption(segment, macros, status)) {
  758. return STATE_SCIENTIFIC;
  759. }
  760. if (U_FAILURE(status)) {
  761. return {};
  762. }
  763. break;
  764. default:
  765. break;
  766. }
  767. // Frac-sig option
  768. switch (stem) {
  769. case STATE_FRACTION_PRECISION:
  770. if (blueprint_helpers::parseFracSigOption(segment, macros, status)) {
  771. return STATE_PRECISION;
  772. }
  773. if (U_FAILURE(status)) {
  774. return {};
  775. }
  776. // If the fracSig option was not found, try normal precision options.
  777. stem = STATE_PRECISION;
  778. break;
  779. default:
  780. break;
  781. }
  782. // Trailing zeros option
  783. switch (stem) {
  784. case STATE_PRECISION:
  785. if (blueprint_helpers::parseTrailingZeroOption(segment, macros, status)) {
  786. return STATE_NULL;
  787. }
  788. if (U_FAILURE(status)) {
  789. return {};
  790. }
  791. break;
  792. default:
  793. break;
  794. }
  795. // Unknown option
  796. // throw new SkeletonSyntaxException("Invalid option", segment);
  797. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  798. return STATE_NULL;
  799. }
  800. void GeneratorHelpers::generateSkeleton(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  801. if (U_FAILURE(status)) { return; }
  802. // Supported options
  803. if (GeneratorHelpers::notation(macros, sb, status)) {
  804. sb.append(u' ');
  805. }
  806. if (U_FAILURE(status)) { return; }
  807. if (GeneratorHelpers::unit(macros, sb, status)) {
  808. sb.append(u' ');
  809. }
  810. if (U_FAILURE(status)) { return; }
  811. if (GeneratorHelpers::usage(macros, sb, status)) {
  812. sb.append(u' ');
  813. }
  814. if (U_FAILURE(status)) { return; }
  815. if (GeneratorHelpers::precision(macros, sb, status)) {
  816. sb.append(u' ');
  817. }
  818. if (U_FAILURE(status)) { return; }
  819. if (GeneratorHelpers::roundingMode(macros, sb, status)) {
  820. sb.append(u' ');
  821. }
  822. if (U_FAILURE(status)) { return; }
  823. if (GeneratorHelpers::grouping(macros, sb, status)) {
  824. sb.append(u' ');
  825. }
  826. if (U_FAILURE(status)) { return; }
  827. if (GeneratorHelpers::integerWidth(macros, sb, status)) {
  828. sb.append(u' ');
  829. }
  830. if (U_FAILURE(status)) { return; }
  831. if (GeneratorHelpers::symbols(macros, sb, status)) {
  832. sb.append(u' ');
  833. }
  834. if (U_FAILURE(status)) { return; }
  835. if (GeneratorHelpers::unitWidth(macros, sb, status)) {
  836. sb.append(u' ');
  837. }
  838. if (U_FAILURE(status)) { return; }
  839. if (GeneratorHelpers::sign(macros, sb, status)) {
  840. sb.append(u' ');
  841. }
  842. if (U_FAILURE(status)) { return; }
  843. if (GeneratorHelpers::decimal(macros, sb, status)) {
  844. sb.append(u' ');
  845. }
  846. if (U_FAILURE(status)) { return; }
  847. if (GeneratorHelpers::scale(macros, sb, status)) {
  848. sb.append(u' ');
  849. }
  850. if (U_FAILURE(status)) { return; }
  851. // Unsupported options
  852. if (!macros.padder.isBogus()) {
  853. status = U_UNSUPPORTED_ERROR;
  854. return;
  855. }
  856. if (macros.unitDisplayCase.isSet()) {
  857. status = U_UNSUPPORTED_ERROR;
  858. return;
  859. }
  860. if (macros.affixProvider != nullptr) {
  861. status = U_UNSUPPORTED_ERROR;
  862. return;
  863. }
  864. if (macros.rules != nullptr) {
  865. status = U_UNSUPPORTED_ERROR;
  866. return;
  867. }
  868. // Remove the trailing space
  869. if (sb.length() > 0) {
  870. sb.truncate(sb.length() - 1);
  871. }
  872. }
  873. bool blueprint_helpers::parseExponentWidthOption(const StringSegment& segment, MacroProps& macros,
  874. UErrorCode&) {
  875. if (!isWildcardChar(segment.charAt(0))) {
  876. return false;
  877. }
  878. int32_t offset = 1;
  879. int32_t minExp = 0;
  880. for (; offset < segment.length(); offset++) {
  881. if (segment.charAt(offset) == u'e') {
  882. minExp++;
  883. } else {
  884. break;
  885. }
  886. }
  887. if (offset < segment.length()) {
  888. return false;
  889. }
  890. // Use the public APIs to enforce bounds checking
  891. macros.notation = static_cast<ScientificNotation&>(macros.notation).withMinExponentDigits(minExp);
  892. return true;
  893. }
  894. void
  895. blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode&) {
  896. sb.append(kWildcardChar);
  897. appendMultiple(sb, u'e', minExponentDigits);
  898. }
  899. bool
  900. blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) {
  901. // Get the sign display type out of the CharsTrie data structure.
  902. UCharsTrie tempStemTrie(kSerializedStemTrie);
  903. UStringTrieResult result = tempStemTrie.next(
  904. segment.toTempUnicodeString().getBuffer(),
  905. segment.length());
  906. if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) {
  907. return false;
  908. }
  909. auto sign = stem_to_object::signDisplay(static_cast<StemEnum>(tempStemTrie.getValue()));
  910. if (sign == UNUM_SIGN_COUNT) {
  911. return false;
  912. }
  913. macros.notation = static_cast<ScientificNotation&>(macros.notation).withExponentSignDisplay(sign);
  914. return true;
  915. }
  916. void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros,
  917. UErrorCode& status) {
  918. // Unlike ICU4J, have to check length manually because ICU4C CurrencyUnit does not check it for us
  919. if (segment.length() != 3) {
  920. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  921. return;
  922. }
  923. const char16_t* currencyCode = segment.toTempUnicodeString().getBuffer();
  924. UErrorCode localStatus = U_ZERO_ERROR;
  925. CurrencyUnit currency(currencyCode, localStatus);
  926. if (U_FAILURE(localStatus)) {
  927. // Not 3 ascii chars
  928. // throw new SkeletonSyntaxException("Invalid currency", segment);
  929. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  930. return;
  931. }
  932. // Slicing is OK
  933. macros.unit = currency; // NOLINT
  934. }
  935. void
  936. blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode&) {
  937. sb.append(currency.getISOCurrency(), -1);
  938. }
  939. void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros,
  940. UErrorCode& status) {
  941. U_ASSERT(U_SUCCESS(status));
  942. const UnicodeString stemString = segment.toTempUnicodeString();
  943. // NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric)
  944. // http://unicode.org/reports/tr35/#Validity_Data
  945. int firstHyphen = 0;
  946. while (firstHyphen < stemString.length() && stemString.charAt(firstHyphen) != '-') {
  947. firstHyphen++;
  948. }
  949. if (firstHyphen == stemString.length()) {
  950. // throw new SkeletonSyntaxException("Invalid measure unit option", segment);
  951. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  952. return;
  953. }
  954. // Need to do char <-> char16_t conversion...
  955. CharString type;
  956. SKELETON_UCHAR_TO_CHAR(type, stemString, 0, firstHyphen, status);
  957. CharString subType;
  958. SKELETON_UCHAR_TO_CHAR(subType, stemString, firstHyphen + 1, stemString.length(), status);
  959. // Note: the largest type as of this writing (Aug 2020) is "volume", which has 33 units.
  960. static constexpr int32_t CAPACITY = 40;
  961. MeasureUnit units[CAPACITY];
  962. UErrorCode localStatus = U_ZERO_ERROR;
  963. int32_t numUnits = MeasureUnit::getAvailable(type.data(), units, CAPACITY, localStatus);
  964. if (U_FAILURE(localStatus)) {
  965. // More than 30 units in this type?
  966. status = U_INTERNAL_PROGRAM_ERROR;
  967. return;
  968. }
  969. for (int32_t i = 0; i < numUnits; i++) {
  970. auto& unit = units[i];
  971. if (uprv_strcmp(subType.data(), unit.getSubtype()) == 0) {
  972. macros.unit = unit;
  973. return;
  974. }
  975. }
  976. // throw new SkeletonSyntaxException("Unknown measure unit", segment);
  977. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  978. }
  979. void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros,
  980. UErrorCode& status) {
  981. // A little bit of a hack: save the current unit (numerator), call the main measure unit
  982. // parsing code, put back the numerator unit, and put the new unit into per-unit.
  983. MeasureUnit numerator = macros.unit;
  984. parseMeasureUnitOption(segment, macros, status);
  985. if (U_FAILURE(status)) { return; }
  986. macros.perUnit = macros.unit;
  987. macros.unit = numerator;
  988. }
  989. void blueprint_helpers::parseIdentifierUnitOption(const StringSegment& segment, MacroProps& macros,
  990. UErrorCode& status) {
  991. // Need to do char <-> char16_t conversion...
  992. U_ASSERT(U_SUCCESS(status));
  993. CharString buffer;
  994. SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
  995. ErrorCode internalStatus;
  996. macros.unit = MeasureUnit::forIdentifier(buffer.toStringPiece(), internalStatus);
  997. if (internalStatus.isFailure()) {
  998. // throw new SkeletonSyntaxException("Invalid core unit identifier", segment, e);
  999. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1000. return;
  1001. }
  1002. }
  1003. void blueprint_helpers::parseUnitUsageOption(const StringSegment &segment, MacroProps &macros,
  1004. UErrorCode &status) {
  1005. // Need to do char <-> char16_t conversion...
  1006. U_ASSERT(U_SUCCESS(status));
  1007. CharString buffer;
  1008. SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
  1009. macros.usage.set(buffer.toStringPiece());
  1010. // We do not do any validation of the usage string: it depends on the
  1011. // unitPreferenceData in the units resources.
  1012. }
  1013. void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros,
  1014. UErrorCode& status) {
  1015. U_ASSERT(segment.charAt(0) == u'.');
  1016. int32_t offset = 1;
  1017. int32_t minFrac = 0;
  1018. int32_t maxFrac;
  1019. for (; offset < segment.length(); offset++) {
  1020. if (segment.charAt(offset) == u'0') {
  1021. minFrac++;
  1022. } else {
  1023. break;
  1024. }
  1025. }
  1026. if (offset < segment.length()) {
  1027. if (isWildcardChar(segment.charAt(offset))) {
  1028. maxFrac = -1;
  1029. offset++;
  1030. } else {
  1031. maxFrac = minFrac;
  1032. for (; offset < segment.length(); offset++) {
  1033. if (segment.charAt(offset) == u'#') {
  1034. maxFrac++;
  1035. } else {
  1036. break;
  1037. }
  1038. }
  1039. }
  1040. } else {
  1041. maxFrac = minFrac;
  1042. }
  1043. if (offset < segment.length()) {
  1044. // throw new SkeletonSyntaxException("Invalid fraction stem", segment);
  1045. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1046. return;
  1047. }
  1048. // Use the public APIs to enforce bounds checking
  1049. if (maxFrac == -1) {
  1050. if (minFrac == 0) {
  1051. macros.precision = Precision::unlimited();
  1052. } else {
  1053. macros.precision = Precision::minFraction(minFrac);
  1054. }
  1055. } else {
  1056. macros.precision = Precision::minMaxFraction(minFrac, maxFrac);
  1057. }
  1058. }
  1059. void
  1060. blueprint_helpers::generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode&) {
  1061. if (minFrac == 0 && maxFrac == 0) {
  1062. sb.append(u"precision-integer", -1);
  1063. return;
  1064. }
  1065. sb.append(u'.');
  1066. appendMultiple(sb, u'0', minFrac);
  1067. if (maxFrac == -1) {
  1068. sb.append(kWildcardChar);
  1069. } else {
  1070. appendMultiple(sb, u'#', maxFrac - minFrac);
  1071. }
  1072. }
  1073. void
  1074. blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) {
  1075. U_ASSERT(segment.charAt(0) == u'@');
  1076. int32_t offset = 0;
  1077. int32_t minSig = 0;
  1078. int32_t maxSig;
  1079. for (; offset < segment.length(); offset++) {
  1080. if (segment.charAt(offset) == u'@') {
  1081. minSig++;
  1082. } else {
  1083. break;
  1084. }
  1085. }
  1086. if (offset < segment.length()) {
  1087. if (isWildcardChar(segment.charAt(offset))) {
  1088. maxSig = -1;
  1089. offset++;
  1090. } else {
  1091. maxSig = minSig;
  1092. for (; offset < segment.length(); offset++) {
  1093. if (segment.charAt(offset) == u'#') {
  1094. maxSig++;
  1095. } else {
  1096. break;
  1097. }
  1098. }
  1099. }
  1100. } else {
  1101. maxSig = minSig;
  1102. }
  1103. if (offset < segment.length()) {
  1104. // throw new SkeletonSyntaxException("Invalid significant digits stem", segment);
  1105. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1106. return;
  1107. }
  1108. // Use the public APIs to enforce bounds checking
  1109. if (maxSig == -1) {
  1110. macros.precision = Precision::minSignificantDigits(minSig);
  1111. } else {
  1112. macros.precision = Precision::minMaxSignificantDigits(minSig, maxSig);
  1113. }
  1114. }
  1115. void
  1116. blueprint_helpers::generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode&) {
  1117. appendMultiple(sb, u'@', minSig);
  1118. if (maxSig == -1) {
  1119. sb.append(kWildcardChar);
  1120. } else {
  1121. appendMultiple(sb, u'#', maxSig - minSig);
  1122. }
  1123. }
  1124. void blueprint_helpers::parseScientificStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) {
  1125. U_ASSERT(segment.charAt(0) == u'E');
  1126. {
  1127. int32_t offset = 1;
  1128. if (segment.length() == offset) {
  1129. goto fail;
  1130. }
  1131. bool isEngineering = false;
  1132. if (segment.charAt(offset) == u'E') {
  1133. isEngineering = true;
  1134. offset++;
  1135. if (segment.length() == offset) {
  1136. goto fail;
  1137. }
  1138. }
  1139. UNumberSignDisplay signDisplay = UNUM_SIGN_AUTO;
  1140. if (segment.charAt(offset) == u'+') {
  1141. offset++;
  1142. if (segment.length() == offset) {
  1143. goto fail;
  1144. }
  1145. if (segment.charAt(offset) == u'!') {
  1146. signDisplay = UNUM_SIGN_ALWAYS;
  1147. } else if (segment.charAt(offset) == u'?') {
  1148. signDisplay = UNUM_SIGN_EXCEPT_ZERO;
  1149. } else {
  1150. // NOTE: Other sign displays are not included because they aren't useful in this context
  1151. goto fail;
  1152. }
  1153. offset++;
  1154. if (segment.length() == offset) {
  1155. goto fail;
  1156. }
  1157. }
  1158. int32_t minDigits = 0;
  1159. for (; offset < segment.length(); offset++) {
  1160. if (segment.charAt(offset) != u'0') {
  1161. goto fail;
  1162. }
  1163. minDigits++;
  1164. }
  1165. macros.notation = (isEngineering ? Notation::engineering() : Notation::scientific())
  1166. .withExponentSignDisplay(signDisplay)
  1167. .withMinExponentDigits(minDigits);
  1168. return;
  1169. }
  1170. fail: void();
  1171. // throw new SkeletonSyntaxException("Invalid scientific stem", segment);
  1172. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1173. return;
  1174. }
  1175. void blueprint_helpers::parseIntegerStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) {
  1176. U_ASSERT(segment.charAt(0) == u'0');
  1177. int32_t offset = 1;
  1178. for (; offset < segment.length(); offset++) {
  1179. if (segment.charAt(offset) != u'0') {
  1180. offset--;
  1181. break;
  1182. }
  1183. }
  1184. if (offset < segment.length()) {
  1185. // throw new SkeletonSyntaxException("Invalid integer stem", segment);
  1186. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1187. return;
  1188. }
  1189. macros.integerWidth = IntegerWidth::zeroFillTo(offset);
  1190. return;
  1191. }
  1192. bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroProps& macros,
  1193. UErrorCode& status) {
  1194. if (segment.charAt(0) != u'@') {
  1195. return false;
  1196. }
  1197. int offset = 0;
  1198. int minSig = 0;
  1199. int maxSig;
  1200. for (; offset < segment.length(); offset++) {
  1201. if (segment.charAt(offset) == u'@') {
  1202. minSig++;
  1203. } else {
  1204. break;
  1205. }
  1206. }
  1207. if (offset < segment.length()) {
  1208. if (isWildcardChar(segment.charAt(offset))) {
  1209. // @+, @@+, @@@+
  1210. maxSig = -1;
  1211. offset++;
  1212. } else {
  1213. // @#, @##, @###
  1214. // @@#, @@##, @@@#
  1215. maxSig = minSig;
  1216. for (; offset < segment.length(); offset++) {
  1217. if (segment.charAt(offset) == u'#') {
  1218. maxSig++;
  1219. } else {
  1220. break;
  1221. }
  1222. }
  1223. }
  1224. } else {
  1225. // @, @@, @@@
  1226. maxSig = minSig;
  1227. }
  1228. auto& oldPrecision = static_cast<const FractionPrecision&>(macros.precision);
  1229. if (offset < segment.length()) {
  1230. UNumberRoundingPriority priority;
  1231. if (maxSig == -1) {
  1232. // The wildcard character is not allowed with the priority annotation
  1233. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1234. return false;
  1235. }
  1236. if (segment.codePointAt(offset) == u'r') {
  1237. priority = UNUM_ROUNDING_PRIORITY_RELAXED;
  1238. offset++;
  1239. } else if (segment.codePointAt(offset) == u's') {
  1240. priority = UNUM_ROUNDING_PRIORITY_STRICT;
  1241. offset++;
  1242. } else {
  1243. // Invalid digits option for fraction rounder
  1244. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1245. return false;
  1246. }
  1247. if (offset < segment.length()) {
  1248. // Invalid digits option for fraction rounder
  1249. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1250. return false;
  1251. }
  1252. macros.precision = oldPrecision.withSignificantDigits(minSig, maxSig, priority);
  1253. } else if (maxSig == -1) {
  1254. // withMinDigits
  1255. macros.precision = oldPrecision.withMinDigits(minSig);
  1256. } else if (minSig == 1) {
  1257. // withMaxDigits
  1258. macros.precision = oldPrecision.withMaxDigits(maxSig);
  1259. } else {
  1260. // Digits options with both min and max sig require the priority option
  1261. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1262. return false;
  1263. }
  1264. return true;
  1265. }
  1266. bool blueprint_helpers::parseTrailingZeroOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) {
  1267. if (segment == u"w") {
  1268. macros.precision = macros.precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE);
  1269. return true;
  1270. }
  1271. return false;
  1272. }
  1273. void blueprint_helpers::parseIncrementOption(const StringSegment &segment, MacroProps &macros,
  1274. UErrorCode &status) {
  1275. number::impl::parseIncrementOption(segment, macros.precision, status);
  1276. }
  1277. void blueprint_helpers::generateIncrementOption(
  1278. uint32_t increment,
  1279. digits_t incrementMagnitude,
  1280. int32_t minFrac,
  1281. UnicodeString& sb,
  1282. UErrorCode&) {
  1283. // Utilize DecimalQuantity/double_conversion to format this for us.
  1284. DecimalQuantity dq;
  1285. dq.setToLong(increment);
  1286. dq.adjustMagnitude(incrementMagnitude);
  1287. dq.setMinFraction(minFrac);
  1288. sb.append(dq.toPlainString());
  1289. }
  1290. void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros,
  1291. UErrorCode& status) {
  1292. int32_t offset = 0;
  1293. int32_t minInt = 0;
  1294. int32_t maxInt;
  1295. if (isWildcardChar(segment.charAt(0))) {
  1296. maxInt = -1;
  1297. offset++;
  1298. } else {
  1299. maxInt = 0;
  1300. }
  1301. for (; offset < segment.length(); offset++) {
  1302. if (maxInt != -1 && segment.charAt(offset) == u'#') {
  1303. maxInt++;
  1304. } else {
  1305. break;
  1306. }
  1307. }
  1308. if (offset < segment.length()) {
  1309. for (; offset < segment.length(); offset++) {
  1310. if (segment.charAt(offset) == u'0') {
  1311. minInt++;
  1312. } else {
  1313. break;
  1314. }
  1315. }
  1316. }
  1317. if (maxInt != -1) {
  1318. maxInt += minInt;
  1319. }
  1320. if (offset < segment.length()) {
  1321. // throw new SkeletonSyntaxException("Invalid integer width stem", segment);
  1322. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1323. return;
  1324. }
  1325. // Use the public APIs to enforce bounds checking
  1326. if (maxInt == -1) {
  1327. macros.integerWidth = IntegerWidth::zeroFillTo(minInt);
  1328. } else {
  1329. macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt);
  1330. }
  1331. }
  1332. void blueprint_helpers::generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb,
  1333. UErrorCode&) {
  1334. if (maxInt == -1) {
  1335. sb.append(kWildcardChar);
  1336. } else {
  1337. appendMultiple(sb, u'#', maxInt - minInt);
  1338. }
  1339. appendMultiple(sb, u'0', minInt);
  1340. }
  1341. void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros,
  1342. UErrorCode& status) {
  1343. // Need to do char <-> char16_t conversion...
  1344. U_ASSERT(U_SUCCESS(status));
  1345. CharString buffer;
  1346. SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
  1347. NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer.data(), status);
  1348. if (ns == nullptr || U_FAILURE(status)) {
  1349. // This is a skeleton syntax error; don't bubble up the low-level NumberingSystem error
  1350. // throw new SkeletonSyntaxException("Unknown numbering system", segment);
  1351. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1352. return;
  1353. }
  1354. macros.symbols.setTo(ns);
  1355. }
  1356. void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb,
  1357. UErrorCode&) {
  1358. // Need to do char <-> char16_t conversion...
  1359. sb.append(UnicodeString(ns.getName(), -1, US_INV));
  1360. }
  1361. void blueprint_helpers::parseScaleOption(const StringSegment& segment, MacroProps& macros,
  1362. UErrorCode& status) {
  1363. // Need to do char <-> char16_t conversion...
  1364. U_ASSERT(U_SUCCESS(status));
  1365. CharString buffer;
  1366. SKELETON_UCHAR_TO_CHAR(buffer, segment.toTempUnicodeString(), 0, segment.length(), status);
  1367. LocalPointer<DecNum> decnum(new DecNum(), status);
  1368. if (U_FAILURE(status)) { return; }
  1369. decnum->setTo({buffer.data(), buffer.length()}, status);
  1370. if (U_FAILURE(status) || decnum->isSpecial()) {
  1371. // This is a skeleton syntax error; don't let the low-level decnum error bubble up
  1372. status = U_NUMBER_SKELETON_SYNTAX_ERROR;
  1373. return;
  1374. }
  1375. // NOTE: The constructor will optimize the decnum for us if possible.
  1376. macros.scale = {0, decnum.orphan()};
  1377. }
  1378. void blueprint_helpers::generateScaleOption(int32_t magnitude, const DecNum* arbitrary, UnicodeString& sb,
  1379. UErrorCode& status) {
  1380. // Utilize DecimalQuantity/double_conversion to format this for us.
  1381. DecimalQuantity dq;
  1382. if (arbitrary != nullptr) {
  1383. dq.setToDecNum(*arbitrary, status);
  1384. if (U_FAILURE(status)) { return; }
  1385. } else {
  1386. dq.setToInt(1);
  1387. }
  1388. dq.adjustMagnitude(magnitude);
  1389. dq.roundToInfinity();
  1390. sb.append(dq.toPlainString());
  1391. }
  1392. bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1393. if (macros.notation.fType == Notation::NTN_COMPACT) {
  1394. UNumberCompactStyle style = macros.notation.fUnion.compactStyle;
  1395. if (style == UNumberCompactStyle::UNUM_LONG) {
  1396. sb.append(u"compact-long", -1);
  1397. return true;
  1398. } else if (style == UNumberCompactStyle::UNUM_SHORT) {
  1399. sb.append(u"compact-short", -1);
  1400. return true;
  1401. } else {
  1402. // Compact notation generated from custom data (not supported in skeleton)
  1403. // The other compact notations are literals
  1404. status = U_UNSUPPORTED_ERROR;
  1405. return false;
  1406. }
  1407. } else if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
  1408. const Notation::ScientificSettings& impl = macros.notation.fUnion.scientific;
  1409. if (impl.fEngineeringInterval == 3) {
  1410. sb.append(u"engineering", -1);
  1411. } else {
  1412. sb.append(u"scientific", -1);
  1413. }
  1414. if (impl.fMinExponentDigits > 1) {
  1415. sb.append(u'/');
  1416. blueprint_helpers::generateExponentWidthOption(impl.fMinExponentDigits, sb, status);
  1417. if (U_FAILURE(status)) {
  1418. return false;
  1419. }
  1420. }
  1421. if (impl.fExponentSignDisplay != UNUM_SIGN_AUTO) {
  1422. sb.append(u'/');
  1423. enum_to_stem_string::signDisplay(impl.fExponentSignDisplay, sb);
  1424. }
  1425. return true;
  1426. } else {
  1427. // Default value is not shown in normalized form
  1428. return false;
  1429. }
  1430. }
  1431. bool GeneratorHelpers::unit(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1432. MeasureUnit unit = macros.unit;
  1433. if (!utils::unitIsBaseUnit(macros.perUnit)) {
  1434. if (utils::unitIsCurrency(macros.unit) || utils::unitIsCurrency(macros.perUnit)) {
  1435. status = U_UNSUPPORTED_ERROR;
  1436. return false;
  1437. }
  1438. unit = unit.product(macros.perUnit.reciprocal(status), status);
  1439. }
  1440. if (utils::unitIsCurrency(unit)) {
  1441. sb.append(u"currency/", -1);
  1442. CurrencyUnit currency(unit, status);
  1443. if (U_FAILURE(status)) {
  1444. return false;
  1445. }
  1446. blueprint_helpers::generateCurrencyOption(currency, sb, status);
  1447. return true;
  1448. } else if (utils::unitIsBaseUnit(unit)) {
  1449. // Default value is not shown in normalized form
  1450. return false;
  1451. } else if (utils::unitIsPercent(unit)) {
  1452. sb.append(u"percent", -1);
  1453. return true;
  1454. } else if (utils::unitIsPermille(unit)) {
  1455. sb.append(u"permille", -1);
  1456. return true;
  1457. } else {
  1458. sb.append(u"unit/", -1);
  1459. sb.append(unit.getIdentifier());
  1460. return true;
  1461. }
  1462. }
  1463. bool GeneratorHelpers::usage(const MacroProps& macros, UnicodeString& sb, UErrorCode& /* status */) {
  1464. if (macros.usage.isSet()) {
  1465. sb.append(u"usage/", -1);
  1466. sb.append(UnicodeString(macros.usage.fValue, -1, US_INV));
  1467. return true;
  1468. }
  1469. return false;
  1470. }
  1471. bool GeneratorHelpers::precision(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1472. if (macros.precision.fType == Precision::RND_NONE) {
  1473. sb.append(u"precision-unlimited", -1);
  1474. } else if (macros.precision.fType == Precision::RND_FRACTION) {
  1475. const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
  1476. blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status);
  1477. } else if (macros.precision.fType == Precision::RND_SIGNIFICANT) {
  1478. const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
  1479. blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status);
  1480. } else if (macros.precision.fType == Precision::RND_FRACTION_SIGNIFICANT) {
  1481. const Precision::FractionSignificantSettings& impl = macros.precision.fUnion.fracSig;
  1482. blueprint_helpers::generateFractionStem(impl.fMinFrac, impl.fMaxFrac, sb, status);
  1483. sb.append(u'/');
  1484. if (impl.fRetain) {
  1485. if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
  1486. // withMinDigits
  1487. blueprint_helpers::generateDigitsStem(impl.fMaxSig, -1, sb, status);
  1488. } else {
  1489. // withMaxDigits
  1490. blueprint_helpers::generateDigitsStem(1, impl.fMaxSig, sb, status);
  1491. }
  1492. } else {
  1493. blueprint_helpers::generateDigitsStem(impl.fMinSig, impl.fMaxSig, sb, status);
  1494. if (impl.fPriority == UNUM_ROUNDING_PRIORITY_RELAXED) {
  1495. sb.append(u'r');
  1496. } else {
  1497. sb.append(u's');
  1498. }
  1499. }
  1500. } else if (macros.precision.fType == Precision::RND_INCREMENT
  1501. || macros.precision.fType == Precision::RND_INCREMENT_ONE
  1502. || macros.precision.fType == Precision::RND_INCREMENT_FIVE) {
  1503. const Precision::IncrementSettings& impl = macros.precision.fUnion.increment;
  1504. sb.append(u"precision-increment/", -1);
  1505. blueprint_helpers::generateIncrementOption(
  1506. impl.fIncrement,
  1507. impl.fIncrementMagnitude,
  1508. impl.fMinFrac,
  1509. sb,
  1510. status);
  1511. } else if (macros.precision.fType == Precision::RND_CURRENCY) {
  1512. UCurrencyUsage usage = macros.precision.fUnion.currencyUsage;
  1513. if (usage == UCURR_USAGE_STANDARD) {
  1514. sb.append(u"precision-currency-standard", -1);
  1515. } else {
  1516. sb.append(u"precision-currency-cash", -1);
  1517. }
  1518. } else {
  1519. // Bogus or Error
  1520. return false;
  1521. }
  1522. if (macros.precision.fTrailingZeroDisplay == UNUM_TRAILING_ZERO_HIDE_IF_WHOLE) {
  1523. sb.append(u"/w", -1);
  1524. }
  1525. // NOTE: Always return true for rounding because the default value depends on other options.
  1526. return true;
  1527. }
  1528. bool GeneratorHelpers::roundingMode(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
  1529. if (macros.roundingMode == kDefaultMode) {
  1530. return false; // Default
  1531. }
  1532. enum_to_stem_string::roundingMode(macros.roundingMode, sb);
  1533. return true;
  1534. }
  1535. bool GeneratorHelpers::grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1536. if (macros.grouper.isBogus()) {
  1537. return false; // No value
  1538. } else if (macros.grouper.fStrategy == UNUM_GROUPING_COUNT) {
  1539. status = U_UNSUPPORTED_ERROR;
  1540. return false;
  1541. } else if (macros.grouper.fStrategy == UNUM_GROUPING_AUTO) {
  1542. return false; // Default value
  1543. } else {
  1544. enum_to_stem_string::groupingStrategy(macros.grouper.fStrategy, sb);
  1545. return true;
  1546. }
  1547. }
  1548. bool GeneratorHelpers::integerWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1549. if (macros.integerWidth.fHasError || macros.integerWidth.isBogus() ||
  1550. macros.integerWidth == IntegerWidth::standard()) {
  1551. // Error or Default
  1552. return false;
  1553. }
  1554. const auto& minMaxInt = macros.integerWidth.fUnion.minMaxInt;
  1555. if (minMaxInt.fMinInt == 0 && minMaxInt.fMaxInt == 0) {
  1556. sb.append(u"integer-width-trunc", -1);
  1557. return true;
  1558. }
  1559. sb.append(u"integer-width/", -1);
  1560. blueprint_helpers::generateIntegerWidthOption(
  1561. minMaxInt.fMinInt,
  1562. minMaxInt.fMaxInt,
  1563. sb,
  1564. status);
  1565. return true;
  1566. }
  1567. bool GeneratorHelpers::symbols(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1568. if (macros.symbols.isNumberingSystem()) {
  1569. const NumberingSystem& ns = *macros.symbols.getNumberingSystem();
  1570. if (uprv_strcmp(ns.getName(), "latn") == 0) {
  1571. sb.append(u"latin", -1);
  1572. } else {
  1573. sb.append(u"numbering-system/", -1);
  1574. blueprint_helpers::generateNumberingSystemOption(ns, sb, status);
  1575. }
  1576. return true;
  1577. } else if (macros.symbols.isDecimalFormatSymbols()) {
  1578. status = U_UNSUPPORTED_ERROR;
  1579. return false;
  1580. } else {
  1581. // No custom symbols
  1582. return false;
  1583. }
  1584. }
  1585. bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
  1586. if (macros.unitWidth == UNUM_UNIT_WIDTH_SHORT || macros.unitWidth == UNUM_UNIT_WIDTH_COUNT) {
  1587. return false; // Default or Bogus
  1588. }
  1589. enum_to_stem_string::unitWidth(macros.unitWidth, sb);
  1590. return true;
  1591. }
  1592. bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
  1593. if (macros.sign == UNUM_SIGN_AUTO || macros.sign == UNUM_SIGN_COUNT) {
  1594. return false; // Default or Bogus
  1595. }
  1596. enum_to_stem_string::signDisplay(macros.sign, sb);
  1597. return true;
  1598. }
  1599. bool GeneratorHelpers::decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode&) {
  1600. if (macros.decimal == UNUM_DECIMAL_SEPARATOR_AUTO || macros.decimal == UNUM_DECIMAL_SEPARATOR_COUNT) {
  1601. return false; // Default or Bogus
  1602. }
  1603. enum_to_stem_string::decimalSeparatorDisplay(macros.decimal, sb);
  1604. return true;
  1605. }
  1606. bool GeneratorHelpers::scale(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) {
  1607. if (!macros.scale.isValid()) {
  1608. return false; // Default or Bogus
  1609. }
  1610. sb.append(u"scale/", -1);
  1611. blueprint_helpers::generateScaleOption(
  1612. macros.scale.fMagnitude,
  1613. macros.scale.fArbitrary,
  1614. sb,
  1615. status);
  1616. return true;
  1617. }
  1618. // Definitions of public API methods (put here for dependency disentanglement)
  1619. #if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER)
  1620. // Ignore MSVC warning 4661. This is generated for NumberFormatterSettings<>::toSkeleton() as this method
  1621. // is defined elsewhere (in number_skeletons.cpp). The compiler is warning that the explicit template instantiation
  1622. // inside this single translation unit (CPP file) is incomplete, and thus it isn't sure if the template class is
  1623. // fully defined. However, since each translation unit explicitly instantiates all the necessary template classes,
  1624. // they will all be passed to the linker, and the linker will still find and export all the class members.
  1625. #pragma warning(push)
  1626. #pragma warning(disable: 4661)
  1627. #endif
  1628. template<typename Derived>
  1629. UnicodeString NumberFormatterSettings<Derived>::toSkeleton(UErrorCode& status) const {
  1630. if (U_FAILURE(status)) {
  1631. return ICU_Utility::makeBogusString();
  1632. }
  1633. if (fMacros.copyErrorTo(status)) {
  1634. return ICU_Utility::makeBogusString();
  1635. }
  1636. return skeleton::generate(fMacros, status);
  1637. }
  1638. // Declare all classes that implement NumberFormatterSettings
  1639. // See https://stackoverflow.com/a/495056/1407170
  1640. template
  1641. class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>;
  1642. template
  1643. class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
  1644. UnlocalizedNumberFormatter
  1645. NumberFormatter::forSkeleton(const UnicodeString& skeleton, UErrorCode& status) {
  1646. return skeleton::create(skeleton, nullptr, status);
  1647. }
  1648. UnlocalizedNumberFormatter
  1649. NumberFormatter::forSkeleton(const UnicodeString& skeleton, UParseError& perror, UErrorCode& status) {
  1650. return skeleton::create(skeleton, &perror, status);
  1651. }
  1652. #if (U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN) && defined(_MSC_VER)
  1653. // Warning 4661.
  1654. #pragma warning(pop)
  1655. #endif
  1656. #endif /* #if !UCONFIG_NO_FORMATTING */