Dreamine.UI.Blazor 1.0.1
Dreamine.UI.Blazor 사용자 인터페이스 기능과 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
HangulComposer.cs
이 파일의 문서화 페이지로 가기
1namespace Dreamine.UI.Blazor;
2
19internal sealed class HangulComposer
20{
29 private static readonly string[] Cho =
30 [
31 "ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ",
32 "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
33 ];
34
43 private static readonly string[] Jung =
44 [
45 "ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ",
46 "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"
47 ];
48
57 private static readonly string[] Jong =
58 [
59 "", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ",
60 "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ",
61 "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
62 ];
63
72 private static readonly Dictionary<string, int> ChoIndex = Cho
73 .Select((value, index) => (value, index))
74 .ToDictionary(x => x.value, x => x.index);
75
84 private static readonly Dictionary<string, int> JungIndex = Jung
85 .Select((value, index) => (value, index))
86 .ToDictionary(x => x.value, x => x.index);
87
96 private static readonly Dictionary<string, int> JongIndex = Jong
97 .Select((value, index) => (value, index))
98 .Where(x => x.value.Length > 0)
99 .ToDictionary(x => x.value, x => x.index);
100
109 private static readonly Dictionary<(int First, int Second), int> CombinedJung = new()
110 {
111 [(8, 0)] = 9,
112 [(8, 1)] = 10,
113 [(8, 20)] = 11,
114 [(13, 4)] = 14,
115 [(13, 5)] = 15,
116 [(13, 20)] = 16,
117 [(18, 20)] = 19,
118 };
119
128 private static readonly Dictionary<(int First, int Second), int> CombinedJong = new()
129 {
130 [(1, 19)] = 3,
131 [(4, 22)] = 5,
132 [(4, 27)] = 6,
133 [(8, 1)] = 9,
134 [(8, 16)] = 10,
135 [(8, 17)] = 11,
136 [(8, 19)] = 12,
137 [(8, 25)] = 13,
138 [(8, 26)] = 14,
139 [(8, 27)] = 15,
140 [(17, 19)] = 18,
141 };
142
151 private static readonly Dictionary<int, (int First, int Second)> SplitJong = CombinedJong
152 .ToDictionary(x => x.Value, x => x.Key);
153
162 private int _cho = -1;
171 private int _jung = -1;
180 private int _jong;
181
190 public void Reset()
191 {
192 _cho = -1;
193 _jung = -1;
194 _jong = 0;
195 }
196
229 public static bool IsComposableJamo(string text)
230 {
231 return text.Length == 1 && (ChoIndex.ContainsKey(text) || JungIndex.ContainsKey(text));
232 }
233
258 public HangulEdit Input(string text)
259 {
260 return Input(text, string.Empty);
261 }
262
295 public HangulEdit Input(string text, string textBeforeCaret)
296 {
297 if (string.IsNullOrEmpty(text) || text.Length != 1)
298 {
299 Reset();
300 return new HangulEdit(0, text);
301 }
302
303 if (ChoIndex.TryGetValue(text, out var cho))
304 {
305 if (TryComposeConsonantWithTrailingText(textBeforeCaret, text, out var edit))
306 return edit;
307
308 if (!string.IsNullOrEmpty(textBeforeCaret))
309 {
310 Reset();
311 return new HangulEdit(0, text);
312 }
313
314 return InputConsonant(text, cho);
315 }
316
317 if (JungIndex.TryGetValue(text, out var jung))
318 {
319 if (TryComposeVowelWithTrailingText(textBeforeCaret, jung, out var edit))
320 return edit;
321
322 if (!string.IsNullOrEmpty(textBeforeCaret))
323 {
324 Reset();
325 return new HangulEdit(0, text);
326 }
327
328 return InputVowel(text, jung);
329 }
330
331 Reset();
332 return new HangulEdit(0, text);
333 }
334
375 private bool TryComposeConsonantWithTrailingText(string textBeforeCaret, string text, out HangulEdit edit)
376 {
377 edit = default;
378 if (string.IsNullOrEmpty(textBeforeCaret) || !JongIndex.TryGetValue(text, out var jong))
379 return false;
380
381 var last = textBeforeCaret[^1];
382 if (!TryDecompose(last, out var cho, out var jung, out var currentJong))
383 return false;
384
385 if (currentJong == 0)
386 {
387 _cho = cho;
388 _jung = jung;
389 _jong = jong;
390 edit = new HangulEdit(1, Compose(_cho, _jung, _jong));
391 return true;
392 }
393
394 if (CombinedJong.TryGetValue((currentJong, jong), out var combinedJong))
395 {
396 _cho = cho;
397 _jung = jung;
398 _jong = combinedJong;
399 edit = new HangulEdit(1, Compose(_cho, _jung, _jong));
400 return true;
401 }
402
403 return false;
404 }
405
446 private bool TryComposeVowelWithTrailingText(string textBeforeCaret, int jung, out HangulEdit edit)
447 {
448 edit = default;
449 if (string.IsNullOrEmpty(textBeforeCaret))
450 return false;
451
452 var last = textBeforeCaret[^1].ToString();
453 if (ChoIndex.TryGetValue(last, out var trailingCho))
454 {
455 _cho = trailingCho;
456 _jung = jung;
457 _jong = 0;
458 edit = new HangulEdit(1, Compose(_cho, _jung, 0));
459 return true;
460 }
461
462 var lastChar = textBeforeCaret[^1];
463 if (!TryDecompose(lastChar, out var cho, out var currentJung, out var jong) || jong == 0)
464 return false;
465
466 if (SplitJong.TryGetValue(jong, out var split))
467 {
468 var previous = Compose(cho, currentJung, split.First);
469 _cho = ToChoIndex(Jong[split.Second]);
470 _jung = jung;
471 _jong = 0;
472 edit = new HangulEdit(1, previous + Compose(_cho, _jung, 0));
473 return true;
474 }
475
476 _cho = ToChoIndex(Jong[jong]);
477 _jung = jung;
478 _jong = 0;
479 edit = new HangulEdit(1, Compose(cho, currentJung, 0) + Compose(_cho, _jung, 0));
480 return true;
481 }
482
515 private HangulEdit InputConsonant(string text, int cho)
516 {
517 if (_cho < 0)
518 {
519 _cho = cho;
520 return new HangulEdit(0, text);
521 }
522
523 if (_jung < 0)
524 {
525 _cho = cho;
526 return new HangulEdit(0, text);
527 }
528
529 if (_jong == 0)
530 {
531 if (JongIndex.TryGetValue(text, out var jong))
532 {
533 _jong = jong;
534 return new HangulEdit(1, Compose(_cho, _jung, _jong));
535 }
536
537 _cho = cho;
538 _jung = -1;
539 _jong = 0;
540 return new HangulEdit(0, text);
541 }
542
543 if (JongIndex.TryGetValue(text, out var nextJong) &&
544 CombinedJong.TryGetValue((_jong, nextJong), out var combinedJong))
545 {
546 _jong = combinedJong;
547 return new HangulEdit(1, Compose(_cho, _jung, _jong));
548 }
549
550 _cho = cho;
551 _jung = -1;
552 _jong = 0;
553 return new HangulEdit(0, text);
554 }
555
588 private HangulEdit InputVowel(string text, int jung)
589 {
590 if (_cho < 0)
591 {
592 Reset();
593 return new HangulEdit(0, text);
594 }
595
596 if (_jung < 0)
597 {
598 _jung = jung;
599 return new HangulEdit(1, Compose(_cho, _jung, 0));
600 }
601
602 if (_jong == 0)
603 {
604 if (CombinedJung.TryGetValue((_jung, jung), out var combinedJung))
605 {
606 _jung = combinedJung;
607 return new HangulEdit(1, Compose(_cho, _jung, 0));
608 }
609
610 Reset();
611 return new HangulEdit(0, text);
612 }
613
614 var previousCho = _cho;
615 var previousJung = _jung;
616 var previousJong = _jong;
617
618 if (SplitJong.TryGetValue(previousJong, out var split))
619 {
620 var previous = Compose(previousCho, previousJung, split.First);
621 _cho = ToChoIndex(Jong[split.Second]);
622 _jung = jung;
623 _jong = 0;
624 return new HangulEdit(1, previous + Compose(_cho, _jung, 0));
625 }
626
627 _cho = ToChoIndex(Jong[previousJong]);
628 _jung = jung;
629 _jong = 0;
630 return new HangulEdit(1, Compose(previousCho, previousJung, 0) + Compose(_cho, _jung, 0));
631 }
632
657 private static int ToChoIndex(string jong)
658 {
659 return ChoIndex.TryGetValue(jong, out var cho) ? cho : ChoIndex["ㅇ"];
660 }
661
710 private static string Compose(int cho, int jung, int jong)
711 {
712 return char.ConvertFromUtf32(0xAC00 + ((cho * 21) + jung) * 28 + jong);
713 }
714
763 private static bool TryDecompose(char value, out int cho, out int jung, out int jong)
764 {
765 var code = value - 0xAC00;
766 if (code < 0 || code >= 11172)
767 {
768 cho = -1;
769 jung = -1;
770 jong = 0;
771 return false;
772 }
773
774 cho = code / (21 * 28);
775 jung = (code % (21 * 28)) / 28;
776 jong = code % 28;
777 return true;
778 }
779}
780
805internal readonly record struct HangulEdit(int ReplaceCount, string Text);