collationruleparser.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. // © 2016 and later: Unicode, Inc. and others.
  2. // License & terms of use: http://www.unicode.org/copyright.html
  3. /*
  4. *******************************************************************************
  5. * Copyright (C) 2013-2015, International Business Machines
  6. * Corporation and others. All Rights Reserved.
  7. *******************************************************************************
  8. * collationruleparser.cpp
  9. *
  10. * (replaced the former ucol_tok.cpp)
  11. *
  12. * created on: 2013apr10
  13. * created by: Markus W. Scherer
  14. */
  15. #include "unicode/utypes.h"
  16. #if !UCONFIG_NO_COLLATION
  17. #include "unicode/normalizer2.h"
  18. #include "unicode/parseerr.h"
  19. #include "unicode/uchar.h"
  20. #include "unicode/ucol.h"
  21. #include "unicode/uloc.h"
  22. #include "unicode/unistr.h"
  23. #include "unicode/utf16.h"
  24. #include "charstr.h"
  25. #include "cmemory.h"
  26. #include "collation.h"
  27. #include "collationdata.h"
  28. #include "collationruleparser.h"
  29. #include "collationsettings.h"
  30. #include "collationtailoring.h"
  31. #include "cstring.h"
  32. #include "patternprops.h"
  33. #include "uassert.h"
  34. #include "uvectr32.h"
  35. U_NAMESPACE_BEGIN
  36. namespace {
  37. static const char16_t BEFORE[] = { 0x5b, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0 }; // "[before"
  38. const int32_t BEFORE_LENGTH = 7;
  39. } // namespace
  40. CollationRuleParser::Sink::~Sink() {}
  41. void
  42. CollationRuleParser::Sink::suppressContractions(const UnicodeSet &, const char *&, UErrorCode &) {}
  43. void
  44. CollationRuleParser::Sink::optimize(const UnicodeSet &, const char *&, UErrorCode &) {}
  45. CollationRuleParser::Importer::~Importer() {}
  46. CollationRuleParser::CollationRuleParser(const CollationData *base, UErrorCode &errorCode)
  47. : nfd(*Normalizer2::getNFDInstance(errorCode)),
  48. nfc(*Normalizer2::getNFCInstance(errorCode)),
  49. rules(nullptr), baseData(base), settings(nullptr),
  50. parseError(nullptr), errorReason(nullptr),
  51. sink(nullptr), importer(nullptr),
  52. ruleIndex(0) {
  53. }
  54. CollationRuleParser::~CollationRuleParser() {
  55. }
  56. void
  57. CollationRuleParser::parse(const UnicodeString &ruleString,
  58. CollationSettings &outSettings,
  59. UParseError *outParseError,
  60. UErrorCode &errorCode) {
  61. if(U_FAILURE(errorCode)) { return; }
  62. settings = &outSettings;
  63. parseError = outParseError;
  64. if(parseError != nullptr) {
  65. parseError->line = 0;
  66. parseError->offset = -1;
  67. parseError->preContext[0] = 0;
  68. parseError->postContext[0] = 0;
  69. }
  70. errorReason = nullptr;
  71. parse(ruleString, errorCode);
  72. }
  73. void
  74. CollationRuleParser::parse(const UnicodeString &ruleString, UErrorCode &errorCode) {
  75. if(U_FAILURE(errorCode)) { return; }
  76. rules = &ruleString;
  77. ruleIndex = 0;
  78. while(ruleIndex < rules->length()) {
  79. char16_t c = rules->charAt(ruleIndex);
  80. if(PatternProps::isWhiteSpace(c)) {
  81. ++ruleIndex;
  82. continue;
  83. }
  84. switch(c) {
  85. case 0x26: // '&'
  86. parseRuleChain(errorCode);
  87. break;
  88. case 0x5b: // '['
  89. parseSetting(errorCode);
  90. break;
  91. case 0x23: // '#' starts a comment, until the end of the line
  92. ruleIndex = skipComment(ruleIndex + 1);
  93. break;
  94. case 0x40: // '@' is equivalent to [backwards 2]
  95. settings->setFlag(CollationSettings::BACKWARD_SECONDARY,
  96. UCOL_ON, 0, errorCode);
  97. ++ruleIndex;
  98. break;
  99. case 0x21: // '!' used to turn on Thai/Lao character reversal
  100. // Accept but ignore. The root collator has contractions
  101. // that are equivalent to the character reversal, where appropriate.
  102. ++ruleIndex;
  103. break;
  104. default:
  105. setParseError("expected a reset or setting or comment", errorCode);
  106. break;
  107. }
  108. if(U_FAILURE(errorCode)) { return; }
  109. }
  110. }
  111. void
  112. CollationRuleParser::parseRuleChain(UErrorCode &errorCode) {
  113. int32_t resetStrength = parseResetAndPosition(errorCode);
  114. UBool isFirstRelation = true;
  115. for(;;) {
  116. int32_t result = parseRelationOperator(errorCode);
  117. if(U_FAILURE(errorCode)) { return; }
  118. if(result < 0) {
  119. if(ruleIndex < rules->length() && rules->charAt(ruleIndex) == 0x23) {
  120. // '#' starts a comment, until the end of the line
  121. ruleIndex = skipComment(ruleIndex + 1);
  122. continue;
  123. }
  124. if(isFirstRelation) {
  125. setParseError("reset not followed by a relation", errorCode);
  126. }
  127. return;
  128. }
  129. int32_t strength = result & STRENGTH_MASK;
  130. if(resetStrength < UCOL_IDENTICAL) {
  131. // reset-before rule chain
  132. if(isFirstRelation) {
  133. if(strength != resetStrength) {
  134. setParseError("reset-before strength differs from its first relation", errorCode);
  135. return;
  136. }
  137. } else {
  138. if(strength < resetStrength) {
  139. setParseError("reset-before strength followed by a stronger relation", errorCode);
  140. return;
  141. }
  142. }
  143. }
  144. int32_t i = ruleIndex + (result >> OFFSET_SHIFT); // skip over the relation operator
  145. if((result & STARRED_FLAG) == 0) {
  146. parseRelationStrings(strength, i, errorCode);
  147. } else {
  148. parseStarredCharacters(strength, i, errorCode);
  149. }
  150. if(U_FAILURE(errorCode)) { return; }
  151. isFirstRelation = false;
  152. }
  153. }
  154. int32_t
  155. CollationRuleParser::parseResetAndPosition(UErrorCode &errorCode) {
  156. if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; }
  157. int32_t i = skipWhiteSpace(ruleIndex + 1);
  158. int32_t j;
  159. char16_t c;
  160. int32_t resetStrength;
  161. if(rules->compare(i, BEFORE_LENGTH, BEFORE, 0, BEFORE_LENGTH) == 0 &&
  162. (j = i + BEFORE_LENGTH) < rules->length() &&
  163. PatternProps::isWhiteSpace(rules->charAt(j)) &&
  164. ((j = skipWhiteSpace(j + 1)) + 1) < rules->length() &&
  165. 0x31 <= (c = rules->charAt(j)) && c <= 0x33 &&
  166. rules->charAt(j + 1) == 0x5d) {
  167. // &[before n] with n=1 or 2 or 3
  168. resetStrength = UCOL_PRIMARY + (c - 0x31);
  169. i = skipWhiteSpace(j + 2);
  170. } else {
  171. resetStrength = UCOL_IDENTICAL;
  172. }
  173. if(i >= rules->length()) {
  174. setParseError("reset without position", errorCode);
  175. return UCOL_DEFAULT;
  176. }
  177. UnicodeString str;
  178. if(rules->charAt(i) == 0x5b) { // '['
  179. i = parseSpecialPosition(i, str, errorCode);
  180. } else {
  181. i = parseTailoringString(i, str, errorCode);
  182. }
  183. sink->addReset(resetStrength, str, errorReason, errorCode);
  184. if(U_FAILURE(errorCode)) { setErrorContext(); }
  185. ruleIndex = i;
  186. return resetStrength;
  187. }
  188. int32_t
  189. CollationRuleParser::parseRelationOperator(UErrorCode &errorCode) {
  190. if(U_FAILURE(errorCode)) { return UCOL_DEFAULT; }
  191. ruleIndex = skipWhiteSpace(ruleIndex);
  192. if(ruleIndex >= rules->length()) { return UCOL_DEFAULT; }
  193. int32_t strength;
  194. int32_t i = ruleIndex;
  195. char16_t c = rules->charAt(i++);
  196. switch(c) {
  197. case 0x3c: // '<'
  198. if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<
  199. ++i;
  200. if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<<
  201. ++i;
  202. if(i < rules->length() && rules->charAt(i) == 0x3c) { // <<<<
  203. ++i;
  204. strength = UCOL_QUATERNARY;
  205. } else {
  206. strength = UCOL_TERTIARY;
  207. }
  208. } else {
  209. strength = UCOL_SECONDARY;
  210. }
  211. } else {
  212. strength = UCOL_PRIMARY;
  213. }
  214. if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*'
  215. ++i;
  216. strength |= STARRED_FLAG;
  217. }
  218. break;
  219. case 0x3b: // ';' same as <<
  220. strength = UCOL_SECONDARY;
  221. break;
  222. case 0x2c: // ',' same as <<<
  223. strength = UCOL_TERTIARY;
  224. break;
  225. case 0x3d: // '='
  226. strength = UCOL_IDENTICAL;
  227. if(i < rules->length() && rules->charAt(i) == 0x2a) { // '*'
  228. ++i;
  229. strength |= STARRED_FLAG;
  230. }
  231. break;
  232. default:
  233. return UCOL_DEFAULT;
  234. }
  235. return ((i - ruleIndex) << OFFSET_SHIFT) | strength;
  236. }
  237. void
  238. CollationRuleParser::parseRelationStrings(int32_t strength, int32_t i, UErrorCode &errorCode) {
  239. // Parse
  240. // prefix | str / extension
  241. // where prefix and extension are optional.
  242. UnicodeString prefix, str, extension;
  243. i = parseTailoringString(i, str, errorCode);
  244. if(U_FAILURE(errorCode)) { return; }
  245. char16_t next = (i < rules->length()) ? rules->charAt(i) : 0;
  246. if(next == 0x7c) { // '|' separates the context prefix from the string.
  247. prefix = str;
  248. i = parseTailoringString(i + 1, str, errorCode);
  249. if(U_FAILURE(errorCode)) { return; }
  250. next = (i < rules->length()) ? rules->charAt(i) : 0;
  251. }
  252. if(next == 0x2f) { // '/' separates the string from the extension.
  253. i = parseTailoringString(i + 1, extension, errorCode);
  254. }
  255. if(!prefix.isEmpty()) {
  256. UChar32 prefix0 = prefix.char32At(0);
  257. UChar32 c = str.char32At(0);
  258. if(!nfc.hasBoundaryBefore(prefix0) || !nfc.hasBoundaryBefore(c)) {
  259. setParseError("in 'prefix|str', prefix and str must each start with an NFC boundary",
  260. errorCode);
  261. return;
  262. }
  263. }
  264. sink->addRelation(strength, prefix, str, extension, errorReason, errorCode);
  265. if(U_FAILURE(errorCode)) { setErrorContext(); }
  266. ruleIndex = i;
  267. }
  268. void
  269. CollationRuleParser::parseStarredCharacters(int32_t strength, int32_t i, UErrorCode &errorCode) {
  270. UnicodeString empty, raw;
  271. i = parseString(skipWhiteSpace(i), raw, errorCode);
  272. if(U_FAILURE(errorCode)) { return; }
  273. if(raw.isEmpty()) {
  274. setParseError("missing starred-relation string", errorCode);
  275. return;
  276. }
  277. UChar32 prev = -1;
  278. int32_t j = 0;
  279. for(;;) {
  280. while(j < raw.length()) {
  281. UChar32 c = raw.char32At(j);
  282. if(!nfd.isInert(c)) {
  283. setParseError("starred-relation string is not all NFD-inert", errorCode);
  284. return;
  285. }
  286. sink->addRelation(strength, empty, UnicodeString(c), empty, errorReason, errorCode);
  287. if(U_FAILURE(errorCode)) {
  288. setErrorContext();
  289. return;
  290. }
  291. j += U16_LENGTH(c);
  292. prev = c;
  293. }
  294. if(i >= rules->length() || rules->charAt(i) != 0x2d) { // '-'
  295. break;
  296. }
  297. if(prev < 0) {
  298. setParseError("range without start in starred-relation string", errorCode);
  299. return;
  300. }
  301. i = parseString(i + 1, raw, errorCode);
  302. if(U_FAILURE(errorCode)) { return; }
  303. if(raw.isEmpty()) {
  304. setParseError("range without end in starred-relation string", errorCode);
  305. return;
  306. }
  307. UChar32 c = raw.char32At(0);
  308. if(c < prev) {
  309. setParseError("range start greater than end in starred-relation string", errorCode);
  310. return;
  311. }
  312. // range prev-c
  313. UnicodeString s;
  314. while(++prev <= c) {
  315. if(!nfd.isInert(prev)) {
  316. setParseError("starred-relation string range is not all NFD-inert", errorCode);
  317. return;
  318. }
  319. if(U_IS_SURROGATE(prev)) {
  320. setParseError("starred-relation string range contains a surrogate", errorCode);
  321. return;
  322. }
  323. if(0xfffd <= prev && prev <= 0xffff) {
  324. setParseError("starred-relation string range contains U+FFFD, U+FFFE or U+FFFF", errorCode);
  325. return;
  326. }
  327. s.setTo(prev);
  328. sink->addRelation(strength, empty, s, empty, errorReason, errorCode);
  329. if(U_FAILURE(errorCode)) {
  330. setErrorContext();
  331. return;
  332. }
  333. }
  334. prev = -1;
  335. j = U16_LENGTH(c);
  336. }
  337. ruleIndex = skipWhiteSpace(i);
  338. }
  339. int32_t
  340. CollationRuleParser::parseTailoringString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) {
  341. i = parseString(skipWhiteSpace(i), raw, errorCode);
  342. if(U_SUCCESS(errorCode) && raw.isEmpty()) {
  343. setParseError("missing relation string", errorCode);
  344. }
  345. return skipWhiteSpace(i);
  346. }
  347. int32_t
  348. CollationRuleParser::parseString(int32_t i, UnicodeString &raw, UErrorCode &errorCode) {
  349. if(U_FAILURE(errorCode)) { return i; }
  350. raw.remove();
  351. while(i < rules->length()) {
  352. UChar32 c = rules->charAt(i++);
  353. if(isSyntaxChar(c)) {
  354. if(c == 0x27) { // apostrophe
  355. if(i < rules->length() && rules->charAt(i) == 0x27) {
  356. // Double apostrophe, encodes a single one.
  357. raw.append((char16_t)0x27);
  358. ++i;
  359. continue;
  360. }
  361. // Quote literal text until the next single apostrophe.
  362. for(;;) {
  363. if(i == rules->length()) {
  364. setParseError("quoted literal text missing terminating apostrophe", errorCode);
  365. return i;
  366. }
  367. c = rules->charAt(i++);
  368. if(c == 0x27) {
  369. if(i < rules->length() && rules->charAt(i) == 0x27) {
  370. // Double apostrophe inside quoted literal text,
  371. // still encodes a single apostrophe.
  372. ++i;
  373. } else {
  374. break;
  375. }
  376. }
  377. raw.append((char16_t)c);
  378. }
  379. } else if(c == 0x5c) { // backslash
  380. if(i == rules->length()) {
  381. setParseError("backslash escape at the end of the rule string", errorCode);
  382. return i;
  383. }
  384. c = rules->char32At(i);
  385. raw.append(c);
  386. i += U16_LENGTH(c);
  387. } else {
  388. // Any other syntax character terminates a string.
  389. --i;
  390. break;
  391. }
  392. } else if(PatternProps::isWhiteSpace(c)) {
  393. // Unquoted white space terminates a string.
  394. --i;
  395. break;
  396. } else {
  397. raw.append((char16_t)c);
  398. }
  399. }
  400. for(int32_t j = 0; j < raw.length();) {
  401. UChar32 c = raw.char32At(j);
  402. if(U_IS_SURROGATE(c)) {
  403. setParseError("string contains an unpaired surrogate", errorCode);
  404. return i;
  405. }
  406. if(0xfffd <= c && c <= 0xffff) {
  407. setParseError("string contains U+FFFD, U+FFFE or U+FFFF", errorCode);
  408. return i;
  409. }
  410. j += U16_LENGTH(c);
  411. }
  412. return i;
  413. }
  414. namespace {
  415. static const char *const positions[] = {
  416. "first tertiary ignorable",
  417. "last tertiary ignorable",
  418. "first secondary ignorable",
  419. "last secondary ignorable",
  420. "first primary ignorable",
  421. "last primary ignorable",
  422. "first variable",
  423. "last variable",
  424. "first regular",
  425. "last regular",
  426. "first implicit",
  427. "last implicit",
  428. "first trailing",
  429. "last trailing"
  430. };
  431. } // namespace
  432. int32_t
  433. CollationRuleParser::parseSpecialPosition(int32_t i, UnicodeString &str, UErrorCode &errorCode) {
  434. if(U_FAILURE(errorCode)) { return 0; }
  435. UnicodeString raw;
  436. int32_t j = readWords(i + 1, raw);
  437. if(j > i && rules->charAt(j) == 0x5d && !raw.isEmpty()) { // words end with ]
  438. ++j;
  439. for(int32_t pos = 0; pos < UPRV_LENGTHOF(positions); ++pos) {
  440. if(raw == UnicodeString(positions[pos], -1, US_INV)) {
  441. str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + pos));
  442. return j;
  443. }
  444. }
  445. if(raw == UNICODE_STRING_SIMPLE("top")) {
  446. str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + LAST_REGULAR));
  447. return j;
  448. }
  449. if(raw == UNICODE_STRING_SIMPLE("variable top")) {
  450. str.setTo((char16_t)POS_LEAD).append((char16_t)(POS_BASE + LAST_VARIABLE));
  451. return j;
  452. }
  453. }
  454. setParseError("not a valid special reset position", errorCode);
  455. return i;
  456. }
  457. void
  458. CollationRuleParser::parseSetting(UErrorCode &errorCode) {
  459. if(U_FAILURE(errorCode)) { return; }
  460. UnicodeString raw;
  461. int32_t i = ruleIndex + 1;
  462. int32_t j = readWords(i, raw);
  463. if(j <= i || raw.isEmpty()) {
  464. setParseError("expected a setting/option at '['", errorCode);
  465. }
  466. if(rules->charAt(j) == 0x5d) { // words end with ]
  467. ++j;
  468. if(raw.startsWith(UNICODE_STRING_SIMPLE("reorder")) &&
  469. (raw.length() == 7 || raw.charAt(7) == 0x20)) {
  470. parseReordering(raw, errorCode);
  471. ruleIndex = j;
  472. return;
  473. }
  474. if(raw == UNICODE_STRING_SIMPLE("backwards 2")) {
  475. settings->setFlag(CollationSettings::BACKWARD_SECONDARY,
  476. UCOL_ON, 0, errorCode);
  477. ruleIndex = j;
  478. return;
  479. }
  480. UnicodeString v;
  481. int32_t valueIndex = raw.lastIndexOf((char16_t)0x20);
  482. if(valueIndex >= 0) {
  483. v.setTo(raw, valueIndex + 1);
  484. raw.truncate(valueIndex);
  485. }
  486. if(raw == UNICODE_STRING_SIMPLE("strength") && v.length() == 1) {
  487. int32_t value = UCOL_DEFAULT;
  488. char16_t c = v.charAt(0);
  489. if(0x31 <= c && c <= 0x34) { // 1..4
  490. value = UCOL_PRIMARY + (c - 0x31);
  491. } else if(c == 0x49) { // 'I'
  492. value = UCOL_IDENTICAL;
  493. }
  494. if(value != UCOL_DEFAULT) {
  495. settings->setStrength(value, 0, errorCode);
  496. ruleIndex = j;
  497. return;
  498. }
  499. } else if(raw == UNICODE_STRING_SIMPLE("alternate")) {
  500. UColAttributeValue value = UCOL_DEFAULT;
  501. if(v == UNICODE_STRING_SIMPLE("non-ignorable")) {
  502. value = UCOL_NON_IGNORABLE;
  503. } else if(v == UNICODE_STRING_SIMPLE("shifted")) {
  504. value = UCOL_SHIFTED;
  505. }
  506. if(value != UCOL_DEFAULT) {
  507. settings->setAlternateHandling(value, 0, errorCode);
  508. ruleIndex = j;
  509. return;
  510. }
  511. } else if(raw == UNICODE_STRING_SIMPLE("maxVariable")) {
  512. int32_t value = UCOL_DEFAULT;
  513. if(v == UNICODE_STRING_SIMPLE("space")) {
  514. value = CollationSettings::MAX_VAR_SPACE;
  515. } else if(v == UNICODE_STRING_SIMPLE("punct")) {
  516. value = CollationSettings::MAX_VAR_PUNCT;
  517. } else if(v == UNICODE_STRING_SIMPLE("symbol")) {
  518. value = CollationSettings::MAX_VAR_SYMBOL;
  519. } else if(v == UNICODE_STRING_SIMPLE("currency")) {
  520. value = CollationSettings::MAX_VAR_CURRENCY;
  521. }
  522. if(value != UCOL_DEFAULT) {
  523. settings->setMaxVariable(value, 0, errorCode);
  524. settings->variableTop = baseData->getLastPrimaryForGroup(
  525. UCOL_REORDER_CODE_FIRST + value);
  526. U_ASSERT(settings->variableTop != 0);
  527. ruleIndex = j;
  528. return;
  529. }
  530. } else if(raw == UNICODE_STRING_SIMPLE("caseFirst")) {
  531. UColAttributeValue value = UCOL_DEFAULT;
  532. if(v == UNICODE_STRING_SIMPLE("off")) {
  533. value = UCOL_OFF;
  534. } else if(v == UNICODE_STRING_SIMPLE("lower")) {
  535. value = UCOL_LOWER_FIRST;
  536. } else if(v == UNICODE_STRING_SIMPLE("upper")) {
  537. value = UCOL_UPPER_FIRST;
  538. }
  539. if(value != UCOL_DEFAULT) {
  540. settings->setCaseFirst(value, 0, errorCode);
  541. ruleIndex = j;
  542. return;
  543. }
  544. } else if(raw == UNICODE_STRING_SIMPLE("caseLevel")) {
  545. UColAttributeValue value = getOnOffValue(v);
  546. if(value != UCOL_DEFAULT) {
  547. settings->setFlag(CollationSettings::CASE_LEVEL, value, 0, errorCode);
  548. ruleIndex = j;
  549. return;
  550. }
  551. } else if(raw == UNICODE_STRING_SIMPLE("normalization")) {
  552. UColAttributeValue value = getOnOffValue(v);
  553. if(value != UCOL_DEFAULT) {
  554. settings->setFlag(CollationSettings::CHECK_FCD, value, 0, errorCode);
  555. ruleIndex = j;
  556. return;
  557. }
  558. } else if(raw == UNICODE_STRING_SIMPLE("numericOrdering")) {
  559. UColAttributeValue value = getOnOffValue(v);
  560. if(value != UCOL_DEFAULT) {
  561. settings->setFlag(CollationSettings::NUMERIC, value, 0, errorCode);
  562. ruleIndex = j;
  563. return;
  564. }
  565. } else if(raw == UNICODE_STRING_SIMPLE("hiraganaQ")) {
  566. UColAttributeValue value = getOnOffValue(v);
  567. if(value != UCOL_DEFAULT) {
  568. if(value == UCOL_ON) {
  569. setParseError("[hiraganaQ on] is not supported", errorCode);
  570. }
  571. ruleIndex = j;
  572. return;
  573. }
  574. } else if(raw == UNICODE_STRING_SIMPLE("import")) {
  575. CharString lang;
  576. lang.appendInvariantChars(v, errorCode);
  577. if(errorCode == U_MEMORY_ALLOCATION_ERROR) { return; }
  578. // BCP 47 language tag -> ICU locale ID
  579. char localeID[ULOC_FULLNAME_CAPACITY];
  580. int32_t parsedLength;
  581. int32_t length = uloc_forLanguageTag(lang.data(), localeID, ULOC_FULLNAME_CAPACITY,
  582. &parsedLength, &errorCode);
  583. if(U_FAILURE(errorCode) ||
  584. parsedLength != lang.length() || length >= ULOC_FULLNAME_CAPACITY) {
  585. errorCode = U_ZERO_ERROR;
  586. setParseError("expected language tag in [import langTag]", errorCode);
  587. return;
  588. }
  589. // localeID minus all keywords
  590. char baseID[ULOC_FULLNAME_CAPACITY];
  591. length = uloc_getBaseName(localeID, baseID, ULOC_FULLNAME_CAPACITY, &errorCode);
  592. if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) {
  593. errorCode = U_ZERO_ERROR;
  594. setParseError("expected language tag in [import langTag]", errorCode);
  595. return;
  596. }
  597. if(length == 0) {
  598. uprv_strcpy(baseID, "root");
  599. } else if(*baseID == '_') {
  600. uprv_memmove(baseID + 3, baseID, length + 1);
  601. uprv_memcpy(baseID, "und", 3);
  602. }
  603. // @collation=type, or length=0 if not specified
  604. char collationType[ULOC_KEYWORDS_CAPACITY];
  605. length = uloc_getKeywordValue(localeID, "collation",
  606. collationType, ULOC_KEYWORDS_CAPACITY,
  607. &errorCode);
  608. if(U_FAILURE(errorCode) || length >= ULOC_KEYWORDS_CAPACITY) {
  609. errorCode = U_ZERO_ERROR;
  610. setParseError("expected language tag in [import langTag]", errorCode);
  611. return;
  612. }
  613. if(importer == nullptr) {
  614. setParseError("[import langTag] is not supported", errorCode);
  615. } else {
  616. UnicodeString importedRules;
  617. importer->getRules(baseID, length > 0 ? collationType : "standard",
  618. importedRules, errorReason, errorCode);
  619. if(U_FAILURE(errorCode)) {
  620. if(errorReason == nullptr) {
  621. errorReason = "[import langTag] failed";
  622. }
  623. setErrorContext();
  624. return;
  625. }
  626. const UnicodeString *outerRules = rules;
  627. int32_t outerRuleIndex = ruleIndex;
  628. parse(importedRules, errorCode);
  629. if(U_FAILURE(errorCode)) {
  630. if(parseError != nullptr) {
  631. parseError->offset = outerRuleIndex;
  632. }
  633. }
  634. rules = outerRules;
  635. ruleIndex = j;
  636. }
  637. return;
  638. }
  639. } else if(rules->charAt(j) == 0x5b) { // words end with [
  640. UnicodeSet set;
  641. j = parseUnicodeSet(j, set, errorCode);
  642. if(U_FAILURE(errorCode)) { return; }
  643. if(raw == UNICODE_STRING_SIMPLE("optimize")) {
  644. sink->optimize(set, errorReason, errorCode);
  645. if(U_FAILURE(errorCode)) { setErrorContext(); }
  646. ruleIndex = j;
  647. return;
  648. } else if(raw == UNICODE_STRING_SIMPLE("suppressContractions")) {
  649. sink->suppressContractions(set, errorReason, errorCode);
  650. if(U_FAILURE(errorCode)) { setErrorContext(); }
  651. ruleIndex = j;
  652. return;
  653. }
  654. }
  655. setParseError("not a valid setting/option", errorCode);
  656. }
  657. void
  658. CollationRuleParser::parseReordering(const UnicodeString &raw, UErrorCode &errorCode) {
  659. if(U_FAILURE(errorCode)) { return; }
  660. int32_t i = 7; // after "reorder"
  661. if(i == raw.length()) {
  662. // empty [reorder] with no codes
  663. settings->resetReordering();
  664. return;
  665. }
  666. // Parse the codes in [reorder aa bb cc].
  667. UVector32 reorderCodes(errorCode);
  668. if(U_FAILURE(errorCode)) { return; }
  669. CharString word;
  670. while(i < raw.length()) {
  671. ++i; // skip the word-separating space
  672. int32_t limit = raw.indexOf((char16_t)0x20, i);
  673. if(limit < 0) { limit = raw.length(); }
  674. word.clear().appendInvariantChars(raw.tempSubStringBetween(i, limit), errorCode);
  675. if(U_FAILURE(errorCode)) { return; }
  676. int32_t code = getReorderCode(word.data());
  677. if(code < 0) {
  678. setParseError("unknown script or reorder code", errorCode);
  679. return;
  680. }
  681. reorderCodes.addElement(code, errorCode);
  682. if(U_FAILURE(errorCode)) { return; }
  683. i = limit;
  684. }
  685. settings->setReordering(*baseData, reorderCodes.getBuffer(), reorderCodes.size(), errorCode);
  686. }
  687. static const char *const gSpecialReorderCodes[] = {
  688. "space", "punct", "symbol", "currency", "digit"
  689. };
  690. int32_t
  691. CollationRuleParser::getReorderCode(const char *word) {
  692. for(int32_t i = 0; i < UPRV_LENGTHOF(gSpecialReorderCodes); ++i) {
  693. if(uprv_stricmp(word, gSpecialReorderCodes[i]) == 0) {
  694. return UCOL_REORDER_CODE_FIRST + i;
  695. }
  696. }
  697. int32_t script = u_getPropertyValueEnum(UCHAR_SCRIPT, word);
  698. if(script >= 0) {
  699. return script;
  700. }
  701. if(uprv_stricmp(word, "others") == 0) {
  702. return UCOL_REORDER_CODE_OTHERS; // same as Zzzz = USCRIPT_UNKNOWN
  703. }
  704. return -1;
  705. }
  706. UColAttributeValue
  707. CollationRuleParser::getOnOffValue(const UnicodeString &s) {
  708. if(s == UNICODE_STRING_SIMPLE("on")) {
  709. return UCOL_ON;
  710. } else if(s == UNICODE_STRING_SIMPLE("off")) {
  711. return UCOL_OFF;
  712. } else {
  713. return UCOL_DEFAULT;
  714. }
  715. }
  716. int32_t
  717. CollationRuleParser::parseUnicodeSet(int32_t i, UnicodeSet &set, UErrorCode &errorCode) {
  718. // Collect a UnicodeSet pattern between a balanced pair of [brackets].
  719. int32_t level = 0;
  720. int32_t j = i;
  721. for(;;) {
  722. if(j == rules->length()) {
  723. setParseError("unbalanced UnicodeSet pattern brackets", errorCode);
  724. return j;
  725. }
  726. char16_t c = rules->charAt(j++);
  727. if(c == 0x5b) { // '['
  728. ++level;
  729. } else if(c == 0x5d) { // ']'
  730. if(--level == 0) { break; }
  731. }
  732. }
  733. set.applyPattern(rules->tempSubStringBetween(i, j), errorCode);
  734. if(U_FAILURE(errorCode)) {
  735. errorCode = U_ZERO_ERROR;
  736. setParseError("not a valid UnicodeSet pattern", errorCode);
  737. return j;
  738. }
  739. j = skipWhiteSpace(j);
  740. if(j == rules->length() || rules->charAt(j) != 0x5d) {
  741. setParseError("missing option-terminating ']' after UnicodeSet pattern", errorCode);
  742. return j;
  743. }
  744. return ++j;
  745. }
  746. int32_t
  747. CollationRuleParser::readWords(int32_t i, UnicodeString &raw) const {
  748. static const char16_t sp = 0x20;
  749. raw.remove();
  750. i = skipWhiteSpace(i);
  751. for(;;) {
  752. if(i >= rules->length()) { return 0; }
  753. char16_t c = rules->charAt(i);
  754. if(isSyntaxChar(c) && c != 0x2d && c != 0x5f) { // syntax except -_
  755. if(raw.isEmpty()) { return i; }
  756. if(raw.endsWith(&sp, 1)) { // remove trailing space
  757. raw.truncate(raw.length() - 1);
  758. }
  759. return i;
  760. }
  761. if(PatternProps::isWhiteSpace(c)) {
  762. raw.append(sp);
  763. i = skipWhiteSpace(i + 1);
  764. } else {
  765. raw.append(c);
  766. ++i;
  767. }
  768. }
  769. }
  770. int32_t
  771. CollationRuleParser::skipComment(int32_t i) const {
  772. // skip to past the newline
  773. while(i < rules->length()) {
  774. char16_t c = rules->charAt(i++);
  775. // LF or FF or CR or NEL or LS or PS
  776. if(c == 0xa || c == 0xc || c == 0xd || c == 0x85 || c == 0x2028 || c == 0x2029) {
  777. // Unicode Newline Guidelines: "A readline function should stop at NLF, LS, FF, or PS."
  778. // NLF (new line function) = CR or LF or CR+LF or NEL.
  779. // No need to collect all of CR+LF because a following LF will be ignored anyway.
  780. break;
  781. }
  782. }
  783. return i;
  784. }
  785. void
  786. CollationRuleParser::setParseError(const char *reason, UErrorCode &errorCode) {
  787. if(U_FAILURE(errorCode)) { return; }
  788. // Error code consistent with the old parser (from ca. 2001),
  789. // rather than U_PARSE_ERROR;
  790. errorCode = U_INVALID_FORMAT_ERROR;
  791. errorReason = reason;
  792. if(parseError != nullptr) { setErrorContext(); }
  793. }
  794. void
  795. CollationRuleParser::setErrorContext() {
  796. if(parseError == nullptr) { return; }
  797. // Note: This relies on the calling code maintaining the ruleIndex
  798. // at a position that is useful for debugging.
  799. // For example, at the beginning of a reset or relation etc.
  800. parseError->offset = ruleIndex;
  801. parseError->line = 0; // We are not counting line numbers.
  802. // before ruleIndex
  803. int32_t start = ruleIndex - (U_PARSE_CONTEXT_LEN - 1);
  804. if(start < 0) {
  805. start = 0;
  806. } else if(start > 0 && U16_IS_TRAIL(rules->charAt(start))) {
  807. ++start;
  808. }
  809. int32_t length = ruleIndex - start;
  810. rules->extract(start, length, parseError->preContext);
  811. parseError->preContext[length] = 0;
  812. // starting from ruleIndex
  813. length = rules->length() - ruleIndex;
  814. if(length >= U_PARSE_CONTEXT_LEN) {
  815. length = U_PARSE_CONTEXT_LEN - 1;
  816. if(U16_IS_LEAD(rules->charAt(ruleIndex + length - 1))) {
  817. --length;
  818. }
  819. }
  820. rules->extract(ruleIndex, length, parseError->postContext);
  821. parseError->postContext[length] = 0;
  822. }
  823. UBool
  824. CollationRuleParser::isSyntaxChar(UChar32 c) {
  825. return 0x21 <= c && c <= 0x7e &&
  826. (c <= 0x2f || (0x3a <= c && c <= 0x40) ||
  827. (0x5b <= c && c <= 0x60) || (0x7b <= c));
  828. }
  829. int32_t
  830. CollationRuleParser::skipWhiteSpace(int32_t i) const {
  831. while(i < rules->length() && PatternProps::isWhiteSpace(rules->charAt(i))) {
  832. ++i;
  833. }
  834. return i;
  835. }
  836. U_NAMESPACE_END
  837. #endif // !UCONFIG_NO_COLLATION