1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #include "codegen/emoji/data.h"
8
9 #include "codegen/emoji/data_old.h"
10 #include "codegen/emoji/data_read.h"
11
12 namespace codegen {
13 namespace emoji {
14 namespace {
15
16 using std::vector;
17 using std::map;
18 using std::set;
19 using std::find;
20 using std::make_pair;
21 using std::move;
22 using std::begin;
23 using std::end;
24
25 // copied from emoji_box.cpp
26 struct Replace {
27 InputId code;
28 const char *replace;
29 };
30
31 Replace Replaces[] = {
32 { { 0xD83DDE0AU }, ":-)" },
33 { { 0xD83DDE0DU }, "8-)" },
34 { { 0x2764U }, "<3" },
35 // { { 0xD83DDC8BU }, ":kiss:" },
36 // { { 0xD83DDE01U }, ":grin:" },
37 // { { 0xD83DDE02U }, ":joy:" },
38 { { 0xD83DDE1AU }, ":-*" },
39 // { { 0xD83DDE06U }, "xD" }, // Conflicts with typing xDDD...
40 // { { 0xD83DDC4DU }, ":like:" },
41 // { { 0xD83DDC4EU }, ":dislike:" },
42 // { { 0x261DU }, ":up:" },
43 // { { 0x270CU }, ":v:" },
44 // { { 0xD83DDC4CU }, ":ok:" },
45 { { 0xD83DDE0EU }, "B-)" },
46 { { 0xD83DDE03U }, ":-D" },
47 { { 0xD83DDE09U }, ";-)" },
48 { { 0xD83DDE1CU }, ";-P" },
49 { { 0xD83DDE0BU }, ":-p" },
50 { { 0xD83DDE14U }, "3(" },
51 { { 0xD83DDE1EU }, ":-(" },
52 { { 0xD83DDE0FU }, ":]" },
53 { { 0xD83DDE22U }, ":'(" },
54 { { 0xD83DDE2DU }, ":_(" },
55 { { 0xD83DDE29U }, ":((" },
56 // { { 0xD83DDE28U }, ":o" }, // Conflicts with typing :ok...
57 { { 0xD83DDE10U }, ":|" },
58 { { 0xD83DDE0CU }, "3-)" },
59 { { 0xD83DDE20U }, ">(" },
60 { { 0xD83DDE21U }, ">((" },
61 { { 0xD83DDE07U }, "O:)" },
62 { { 0xD83DDE30U }, ";o" },
63 { { 0xD83DDE33U }, "8|" },
64 { { 0xD83DDE32U }, "8o" },
65 { { 0xD83DDE37U }, ":X" },
66 { { 0xD83DDE08U }, "}:)" },
67 };
68
69 InputCategory PostfixRequired = {
70 { 0x2122U, 0xFE0FU, },
71 { 0xA9U, 0xFE0FU, },
72 { 0xAEU, 0xFE0FU, },
73 };
74
75 using ColorId = uint32;
76 ColorId Colors[] = {
77 0xD83CDFFBU,
78 0xD83CDFFCU,
79 0xD83CDFFDU,
80 0xD83CDFFEU,
81 0xD83CDFFFU,
82 };
83
84 constexpr auto ColorMask = 0xD83CDFFBU;
85
86 // Original data has those emoji only with gender symbols.
87 // But they should be displayed as emoji even without gender symbols.
88 // So we map which gender symbol to use for an emoji without one.
89 std::map<InputId, uint32> WithoutGenderAliases = {
90 // { { 0xD83EDD26U, }, 0x2642U },
91 // { { 0xD83EDD37U, }, 0x2640U },
92 // { { 0xD83EDD38U, }, 0x2642U },
93 // { { 0xD83EDD3CU, }, 0x2640U },
94 // { { 0xD83EDD3DU, }, 0x2642U },
95 // { { 0xD83EDD3EU, }, 0x2640U },
96 // { { 0xD83EDD39U, }, 0x2642U },
97 // { { 0xD83EDDB8U, }, 0x2640U },
98 // { { 0xD83EDDB9U, }, 0x2640U },
99 // { { 0xD83EDDD6U, }, 0x2642U },
100 // { { 0xD83EDDD7U, }, 0x2640U },
101 // { { 0xD83EDDD8U, }, 0x2640U },
102 // { { 0xD83EDDD9U, }, 0x2640U },
103 // { { 0xD83EDDDAU, }, 0x2640U },
104 // { { 0xD83EDDDBU, }, 0x2640U },
105 // { { 0xD83EDDDCU, }, 0x2642U },
106 // { { 0xD83EDDDDU, }, 0x2642U },
107 // { { 0xD83EDDDEU, }, 0x2642U },
108 // { { 0xD83EDDDFU, }, 0x2642U },
109 };
110
111 // Some flags are sent as one string, but are rendered as a different too.
112 std::map<InputId, InputId> FlagAliases = {
113 // { { 0xD83CDDE8U, 0xD83CDDF5U, }, { 0xD83CDDEBU, 0xD83CDDF7U, } },
114 // { { 0xD83CDDE7U, 0xD83CDDFBU, }, { 0xD83CDDF3U, 0xD83CDDF4U, } },
115 // { { 0xD83CDDE6U, 0xD83CDDE8U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } },
116 //
117 // // This is different flag, but macOS shows that glyph :(
118 // { { 0xD83CDDE9U, 0xD83CDDECU, }, { 0xD83CDDEEU, 0xD83CDDF4U, } },
119 //
120 // { { 0xD83CDDF9U, 0xD83CDDE6U, }, { 0xD83CDDF8U, 0xD83CDDEDU, } },
121 // { { 0xD83CDDF2U, 0xD83CDDEBU, }, { 0xD83CDDEBU, 0xD83CDDF7U, } },
122 // { { 0xD83CDDEAU, 0xD83CDDE6U, }, { 0xD83CDDEAU, 0xD83CDDF8U, } },
123 };
124
125 std::map<Id, std::vector<Id>> Aliases; // original -> list of aliased
126 std::set<Id> AliasesAdded;
127
AddAlias(const Id & original,const Id & aliased)128 void AddAlias(const Id &original, const Id &aliased) {
129 auto &aliases = Aliases[original];
130 if (std::find(aliases.begin(), aliases.end(), aliased) == aliases.end()) {
131 aliases.push_back(aliased);
132 }
133 }
134
135 constexpr auto kErrorBadData = 401;
136
append(Id & id,uint32 code)137 void append(Id &id, uint32 code) {
138 if (auto first = static_cast<uint16>((code >> 16) & 0xFFFFU)) {
139 id.append(QChar(first));
140 }
141 id.append(QChar(static_cast<uint16>(code & 0xFFFFU)));
142 }
143
BareIdFromInput(const InputId & id)144 [[nodiscard]] Id BareIdFromInput(const InputId &id) {
145 auto result = Id();
146 for (const auto unicode : id) {
147 if (unicode != kPostfix) {
148 append(result, unicode);
149 }
150 }
151 return result;
152 }
153
FillVariatedIds(const InputData & data)154 [[nodiscard]] set<Id> FillVariatedIds(const InputData &data) {
155 auto result = set<Id>();
156 for (const auto &row : data.colored) {
157 auto variatedId = Id();
158 if (row.size() < 2) {
159 logDataError() << "colored string should have at least two characters.";
160 return {};
161 }
162 for (auto i = size_t(0), size = row.size(); i != size; ++i) {
163 auto code = row[i];
164 if (i == 1) {
165 if (code != ColorMask) {
166 logDataError() << "color code should appear at index 1.";
167 return {};
168 }
169 } else if (code == ColorMask) {
170 logDataError() << "color code should appear only at index 1.";
171 return {};
172 } else if (code != kPostfix) {
173 append(variatedId, code);
174 }
175 }
176 result.emplace(variatedId);
177 }
178 return result;
179 }
180
FillDoubleVariatedIds(const InputData & data)181 [[nodiscard]] map<Id, InputId> FillDoubleVariatedIds(const InputData &data) {
182 auto result = map<Id, InputId>();
183 for (const auto &[original, same, different] : data.doubleColored) {
184 auto variatedId = Id();
185 if (original.size() < 1) {
186 logDataError() << "original string should have at least one character.";
187 return {};
188 } else if (same.size() < 2) {
189 logDataError() << "colored string should have at least two characters.";
190 return {};
191 }
192 if (same.size() == 2) {
193 // original: 1
194 // same: original + color
195 // different: some1 + color1 + sep + some2 + sep + some3 + color2
196 if (same[1] != ColorMask) {
197 logDataError() << "color code should appear at index 1.";
198 return {};
199 } else if (same[0] == ColorMask) {
200 logDataError() << "color code should appear only at index 1.";
201 return {};
202 } else if (same[0] == kPostfix) {
203 logDataError() << "postfix in double colored is not supported.";
204 return {};
205 } else {
206 append(variatedId, same[0]);
207 }
208 // add an alias to 'same' in the form ..
209 // .. of 'some1 + color + sep + some2 + sep + some3 + color'
210 if (std::count(different.begin(), different.end(), kJoiner) != 2) {
211 logDataError() << "complex double colored bad different.";
212 return {};
213 }
214 for (const auto color : Colors) {
215 auto copy = same;
216 copy[1] = color;
217 auto sameWithColor = BareIdFromInput(copy);
218 copy = different;
219 for (auto &entry : copy) {
220 if (entry == Colors[0] || entry == Colors[1]) {
221 entry = color;
222 }
223 }
224 auto differentWithColor = BareIdFromInput(copy);
225 AddAlias(sameWithColor, differentWithColor);
226 }
227 } else {
228 // same: som1 + color + sep + some2 + sep + some3 + color
229 // different: some1 + color1 + sep + some2 + sep + some3 + color2
230 auto copy = different;
231 if (copy.size() < 7 || copy[1] != Colors[0] || copy[copy.size() - 1] != Colors[1]) {
232 logDataError() << "complex double colored bad different.";
233 return {};
234 }
235 copy[copy.size() - 1] = Colors[0];
236 if (copy != same) {
237 logDataError() << "complex double colored should colorize all the same.";
238 return {};
239 }
240 if (original.size() == 1) {
241 // original: 1
242 // add an alias to 'same' in the form of 'original + color'
243 for (const auto color : Colors) {
244 auto copy = original;
245 copy.push_back(color);
246 auto originalWithColor = BareIdFromInput(copy);
247 copy = same;
248 for (auto &entry : copy) {
249 if (entry == ColorMask) {
250 entry = color;
251 }
252 }
253 auto sameWithColor = BareIdFromInput(copy);
254 AddAlias(originalWithColor, sameWithColor);
255 }
256 }
257 variatedId = BareIdFromInput(original);
258 }
259 result.emplace(variatedId, different);
260 }
261 return result;
262 }
263
FillPostfixRequiredIds()264 [[nodiscard]] set<Id> FillPostfixRequiredIds() {
265 auto result = set<Id>();
266 for (const auto &row : PostfixRequired) {
267 result.emplace(BareIdFromInput(row));
268 }
269 return result;
270 }
271
appendCategory(Data & result,const InputCategory & category,const set<Id> & variatedIds,const map<Id,InputId> & doubleVariatedIds,const set<Id> & postfixRequiredIds)272 void appendCategory(
273 Data &result,
274 const InputCategory &category,
275 const set<Id> &variatedIds,
276 const map<Id, InputId> &doubleVariatedIds,
277 const set<Id> &postfixRequiredIds) {
278 result.categories.emplace_back();
279 for (auto &id : category) {
280 auto emoji = Emoji();
281 auto bareId = BareIdFromInput(id);
282 auto from = id.cbegin(), to = id.cend();
283 if (to - from == 2 && *(to - 1) == kPostfix) {
284 emoji.postfixed = true;
285 --to;
286 }
287 for (auto i = from; i != to; ++i) {
288 auto code = *i;
289 if (find(begin(Colors), end(Colors), code) != end(Colors)) {
290 logDataError() << "color code found in a category emoji.";
291 result = Data();
292 return;
293 }
294 append(emoji.id, code);
295 }
296 if (bareId.isEmpty()) {
297 logDataError() << "empty emoji id found.";
298 result = Data();
299 return;
300 }
301
302 const auto addOne = [&](const Id &bareId, Emoji &&emoji) {
303 auto it = result.map.find(bareId);
304 if (it == result.map.cend()) {
305 const auto index = result.list.size();
306 it = result.map.emplace(bareId, index).first;
307 result.list.push_back(move(emoji));
308 if (const auto a = Aliases.find(bareId); a != end(Aliases)) {
309 AliasesAdded.emplace(bareId);
310 for (const auto &alias : a->second) {
311 const auto ok = result.map.emplace(alias, index).second;
312 if (!ok) {
313 logDataError() << "some emoji alias already in the map.";
314 result = Data();
315 return result.map.end();
316 }
317 }
318 }
319 if (postfixRequiredIds.find(bareId) != end(postfixRequiredIds)) {
320 result.postfixRequired.emplace(index);
321 }
322 } else if (result.list[it->second].postfixed != emoji.postfixed) {
323 logDataError() << "same emoji found with different postfixed property.";
324 result = Data();
325 return result.map.end();
326 } else if (result.list[it->second].id != emoji.id) {
327 logDataError() << "same emoji found with different id.";
328 result = Data();
329 return result.map.end();
330 }
331 return it;
332 };
333
334 auto it = addOne(bareId, move(emoji));
335 if (it == result.map.end()) {
336 return;
337 }
338 if (variatedIds.find(bareId) != end(variatedIds)) {
339 result.list[it->second].variated = true;
340
341 auto baseId = Id();
342 if (*from == kPostfix) {
343 logDataError() << "bad first symbol in emoji.";
344 result = Data();
345 return;
346 }
347 append(baseId, *from++);
348 // A few uncolored emoji contain two kPostfix.
349 // (:(wo)man_lifting_weights:, :(wo)man_golfing:
350 // :(wo)man_bouncing_ball:, :(wo)man_detective:)
351 // But colored emoji should have only one kPostfix.
352 if (std::count(from, to, kPostfix) == 2 && *from == kPostfix) {
353 from++;
354 }
355 for (auto color : Colors) {
356 auto colored = Emoji();
357 colored.colored = true;
358 colored.id = baseId;
359 append(colored.id, color);
360 auto bareColoredId = colored.id;
361 for (auto i = from; i != to; ++i) {
362 append(colored.id, *i);
363 if (*i != kPostfix) {
364 append(bareColoredId, *i);
365 }
366 }
367 if (addOne(bareColoredId, std::move(colored)) == result.map.end()) {
368 return;
369 }
370 }
371 } else if (const auto d = doubleVariatedIds.find(bareId); d != end(doubleVariatedIds)) {
372 //result.list[it->second].doubleVariated = true;
373
374 const auto baseId = bareId;
375 const auto &different = d->second;
376 if (different.size() < 4
377 || different[1] != Colors[0]
378 || different[different.size() - 1] != Colors[1]) {
379 logDataError() << "bad data in double-colored emoji.";
380 result = Data();
381 return;
382 }
383 for (auto color1 : Colors) {
384 for (auto color2 : Colors) {
385 auto colored = Emoji();
386 //colored.colored = true;
387 if (color1 == color2 && baseId.size() == 2) {
388 colored.id = baseId;
389 append(colored.id, color1);
390 } else {
391 auto copy = different;
392 copy[1] = color1;
393 copy[copy.size() - 1] = color2;
394 for (const auto code : copy) {
395 append(colored.id, code);
396 }
397 }
398 auto bareColoredId = colored.id.replace(QChar(kPostfix), QString());
399 if (addOne(bareColoredId, move(colored)) == result.map.end()) {
400 return;
401 }
402 }
403 }
404
405 }
406 result.categories.back().push_back(it->second);
407 }
408 }
409
fillReplaces(Data & result)410 void fillReplaces(Data &result) {
411 for (auto &replace : Replaces) {
412 auto id = Id();
413 for (auto code : replace.code) {
414 append(id, code);
415 }
416 auto it = result.map.find(id);
417 if (it == result.map.cend()) {
418 logDataError() << "emoji from replaces not found in the map.";
419 result = Data();
420 return;
421 }
422 result.replaces.insert(make_pair(QString::fromUtf8(replace.replace), it->second));
423 }
424 }
425
CheckOldInCurrent(const InputData & data,const InputData & old,const std::set<Id> & variatedIds)426 bool CheckOldInCurrent(
427 const InputData &data,
428 const InputData &old,
429 const std::set<Id> &variatedIds) {
430 const auto genders = { 0x2640U, 0x2642U };
431 const auto addGender = [](const InputId &was, uint32 gender) {
432 auto result = was;
433 result.push_back(0x200DU);
434 result.push_back(gender);
435 result.push_back(kPostfix);
436 return result;
437 };
438 const auto addGenderByIndex = [&](const InputId &was, int index) {
439 return addGender(was, *(begin(genders) + index));
440 };
441 static const auto find = [](
442 const InputCategory &list,
443 const InputId &id) {
444 return (std::find(begin(list), end(list), id) != end(list));
445 };
446 static const auto findInMany = [](
447 const InputData &data,
448 const InputId &id) {
449 for (const auto ¤t : data.categories) {
450 if (find(current, id)) {
451 return true;
452 }
453 }
454 return find(data.other, id);
455 };
456 const auto emplaceColoredAlias = [](const InputId &real, const InputId &alias, uint32_t color) {
457 if (real.size() < 2 || alias.size() < 2 || real[1] != Colors[0] || alias[1] != Colors[0]) {
458 return false;
459 }
460 auto key = real;
461 key[1] = color;
462 auto value = alias;
463 value[1] = color;
464 AddAlias(BareIdFromInput(key), BareIdFromInput(value));
465 return true;
466 };
467 auto result = true;
468 for (auto c = begin(old.categories); c != end(old.categories); ++c) {
469 const auto &category = *c;
470 for (auto i = begin(category); i != end(category); ++i) {
471 if (findInMany(data, *i)) {
472 continue;
473 }
474
475 // Some emoji were ending with kPostfix and now are not.
476 if (i->back() == kPostfix) {
477 auto other = *i;
478 other.pop_back();
479 if (findInMany(data, other)) {
480 continue;
481 }
482 }
483
484 // Some emoji were not ending with kPostfix and now are.
485 if (i->back() != kPostfix) {
486 auto other = *i;
487 other.push_back(kPostfix);
488 if (findInMany(data, other)) {
489 continue;
490 }
491 }
492
493 common::logError(kErrorBadData, "input")
494 << "Bad data: old emoji '"
495 << InputIdToString(*i).toStdString()
496 << "' (category "
497 << (c - begin(old.categories))
498 << ", index "
499 << (i - begin(category))
500 << ") not found in current.";
501 result = false;
502 continue;
503
504 // Old way - auto add genders. New way - add gender neutral images,
505 // but don't add them to any category in the picker.
506 // // Some emoji were without gender symbol and now have gender symbol.
507 // // Try adding 0x200DU, 0x2640U, 0xFE0FU or 0x200DU, 0x2642U, 0xFE0FU.
508 // const auto otherGenderIndex = [&] {
509 // for (auto g = begin(genders); g != end(genders); ++g) {
510 // auto altered = *i;
511 // altered.push_back(kJoiner);
512 // altered.push_back(*g);
513 // altered.push_back(kPostfix);
514 // if (findInMany(old, altered)) {
515 // return int(g - begin(genders));
516 // }
517 // }
518 // return -1;
519 // }();
520 // if (otherGenderIndex < 0) {
521 // common::logError(kErrorBadData, "input")
522 // << "Bad data: old emoji '"
523 // << InputIdToString(*i).toStdString()
524 // << "' (category "
525 // << (c - begin(old.categories))
526 // << ", index "
527 // << (i - begin(category))
528 // << ") not found in current.";
529 // result = false;
530 // continue;
531 // }
532 //
533 // const auto genderIndex = (1 - otherGenderIndex);
534 // const auto real = addGenderByIndex(*i, genderIndex);
535 // const auto bare = BareIdFromInput(real);
536 // if (!findInMany(data, real)) {
537 // common::logError(kErrorBadData, "input")
538 // << "Bad data: old emoji '"
539 // << InputIdToString(*i).toStdString()
540 // << "' (category "
541 // << (c - begin(old.categories))
542 // << ", index "
543 // << (i - begin(category))
544 // << ") not found in current with added gender: "
545 // << genderIndex
546 // << ".";
547 // result = false;
548 // } else {
549 // AddAlias(bare, BareIdFromInput(*i));
550 // }
551 }
552 }
553 for (auto i = begin(old.doubleColored); i != end(old.doubleColored); ++i) {
554 auto found = false;
555 for (auto j = begin(data.doubleColored); j != end(data.doubleColored); ++j) {
556 if (j->original == i->original) {
557 found = true;
558 if (j->same != i->same || j->different != i->different) {
559 common::logError(kErrorBadData, "input")
560 << "Bad data: old double colored emoji (index "
561 << (i - begin(old.doubleColored))
562 << ") not equal to current.";
563 result = false;
564 }
565 break;
566 }
567 }
568 if (!found) {
569 common::logError(kErrorBadData, "input")
570 << "Bad data: old double colored emoji (index "
571 << (i - begin(old.doubleColored))
572 << ") not found in current.";
573 result = false;
574 }
575 }
576 for (auto i = begin(old.colored); i != end(old.colored); ++i) {
577 if (find(data.colored, *i)) {
578 continue;
579 }
580
581 const auto otherGenderIndex = [&] {
582 for (auto g = begin(genders); g != end(genders); ++g) {
583 auto altered = *i;
584 altered.push_back(kJoiner);
585 altered.push_back(*g);
586 altered.push_back(kPostfix);
587 if (find(old.colored, altered)) {
588 return int(g - begin(genders));
589 }
590 }
591 return -1;
592 }();
593 if (otherGenderIndex < 0) {
594 common::logError(kErrorBadData, "input")
595 << "Bad data: old colored emoji (index "
596 << (i - begin(old.colored))
597 << ") not found in current.";
598 result = false;
599 continue;
600 }
601
602 const auto genderIndex = (1 - otherGenderIndex);
603 const auto real = addGenderByIndex(*i, genderIndex);
604 if (!find(data.colored, real)) {
605 common::logError(kErrorBadData, "input")
606 << "Bad data: old colored emoji (index "
607 << (i - begin(old.colored))
608 << ") not found in current with added gender: "
609 << genderIndex
610 << ".";
611 result = false;
612 continue;
613 } else {
614 for (const auto color : Colors) {
615 if (!emplaceColoredAlias(real, *i, color)) {
616 common::logError(kErrorBadData, "input")
617 << "Bad data: bad colored emoji.";
618 result = false;
619 break;
620 }
621 }
622 }
623 }
624
625 for (const auto &entry : WithoutGenderAliases) {
626 const auto &inputId = entry.first;
627 const auto &gender = entry.second;
628 if (findInMany(data, inputId)) {
629 continue;
630 }
631 const auto real = [&] {
632 auto result = addGender(inputId, gender);
633 if (findInMany(data, result)) {
634 return result;
635 }
636 result.push_back(kPostfix);
637 return result;
638 }();
639 const auto bare = BareIdFromInput(real);
640 if (!findInMany(data, real)) {
641 common::logError(kErrorBadData, "input")
642 << "Bad data: without gender alias not found with gender.";
643 result = false;
644 } else {
645 AddAlias(bare, BareIdFromInput(inputId));
646 }
647 if (variatedIds.find(bare) != variatedIds.end()) {
648 auto colorReal = real;
649 colorReal.insert(colorReal.begin() + 1, Colors[0]);
650 auto colorInput = inputId;
651 colorInput.insert(colorInput.begin() + 1, Colors[0]);
652 for (const auto color : Colors) {
653 if (!emplaceColoredAlias(colorReal, colorInput, color)) {
654 common::logError(kErrorBadData, "input")
655 << "Bad data: bad colored emoji.";
656 result = false;
657 break;
658 }
659 }
660
661 }
662 }
663
664 for (const auto &[inputId, real] : FlagAliases) {
665 AddAlias(BareIdFromInput(real), BareIdFromInput(inputId));
666 }
667
668 return result;
669 }
670
CheckOldInCurrent(const InputData & data,const std::set<Id> & variatedIds,const std::vector<QString> & oldDataPaths)671 bool CheckOldInCurrent(
672 const InputData &data,
673 const std::set<Id> &variatedIds,
674 const std::vector<QString> &oldDataPaths) {
675 if (!CheckOldInCurrent(data, GetDataOld1(), variatedIds)
676 || !CheckOldInCurrent(data, GetDataOld2(), variatedIds)) {
677 return false;
678 }
679 for (const auto &path : oldDataPaths) {
680 const auto old = ReadData(path);
681 if (old.colored.empty() || old.doubleColored.empty()) {
682 return false;
683 } else if (!CheckOldInCurrent(data, old, variatedIds)) {
684 return false;
685 }
686 }
687 return true;
688 }
689
690 } // namespace
691
logDataError()692 common::LogStream logDataError() {
693 return common::logError(kErrorBadData, "input") << "Bad data: ";
694 }
695
PrepareData(const QString & dataPath,const std::vector<QString> & oldDataPaths)696 Data PrepareData(const QString &dataPath, const std::vector<QString> &oldDataPaths) {
697 Data result;
698
699 auto input = ReadData(dataPath);
700 const auto variatedIds = FillVariatedIds(input);
701 const auto doubleVariatedIds = FillDoubleVariatedIds(input);
702 const auto postfixRequiredIds = FillPostfixRequiredIds();
703 if (variatedIds.empty() || doubleVariatedIds.empty() || postfixRequiredIds.empty()) {
704 return Data();
705 }
706
707 if (!CheckOldInCurrent(input, variatedIds, oldDataPaths)) {
708 return Data();
709 }
710
711 for (const auto &category : input.categories) {
712 appendCategory(result, category, variatedIds, doubleVariatedIds, postfixRequiredIds);
713 if (result.list.empty()) {
714 return Data();
715 }
716 }
717 appendCategory(result, input.other, variatedIds, doubleVariatedIds, postfixRequiredIds);
718 if (result.list.empty()) {
719 return Data();
720 }
721 if (AliasesAdded.size() != Aliases.size()) {
722 for (const auto &[key, list] : Aliases) {
723 if (AliasesAdded.find(key) == AliasesAdded.end()) {
724 QStringList expanded;
725 for (const auto &ch : key) {
726 expanded.push_back(QString::number(ch.unicode()));
727 }
728 common::logError(kErrorBadData, "input")
729 << "Bad data: Not added aliases list for: "
730 << expanded.join(QChar(',')).toStdString();
731 }
732 }
733 return Data();
734 }
735
736 fillReplaces(result);
737 if (result.list.empty()) {
738 return Data();
739 }
740
741 return result;
742 }
743
744 } // namespace emoji
745 } // namespace codegen
746