Dreamine.UI.Wpf.Equipment 1.0.1
Dreamine.UI.Wpf.Equipment 사용자 인터페이스 기능과 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
HangulComposer.cs
이 파일의 문서화 페이지로 가기
2
11internal sealed class HangulComposer
12{
21 private static readonly string[] Cho =
22 [
23 "ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ",
24 "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
25 ];
26
35 private static readonly string[] Jung =
36 [
37 "ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ",
38 "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"
39 ];
40
49 private static readonly string[] Jong =
50 [
51 "", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ",
52 "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ",
53 "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
54 ];
55
64 private static readonly Dictionary<string, int> ChoIndex = Cho
65 .Select((value, index) => (value, index))
66 .ToDictionary(x => x.value, x => x.index);
67
76 private static readonly Dictionary<string, int> JungIndex = Jung
77 .Select((value, index) => (value, index))
78 .ToDictionary(x => x.value, x => x.index);
79
88 private static readonly Dictionary<string, int> JongIndex = Jong
89 .Select((value, index) => (value, index))
90 .Where(x => x.value.Length > 0)
91 .ToDictionary(x => x.value, x => x.index);
92
101 private static readonly Dictionary<(int First, int Second), int> CombinedJung = new()
102 {
103 [(8, 0)] = 9,
104 [(8, 1)] = 10,
105 [(8, 20)] = 11,
106 [(13, 4)] = 14,
107 [(13, 5)] = 15,
108 [(13, 20)] = 16,
109 [(18, 20)] = 19,
110 };
111
120 private static readonly Dictionary<(int First, int Second), int> CombinedJong = new()
121 {
122 [(1, 19)] = 3,
123 [(4, 22)] = 5,
124 [(4, 27)] = 6,
125 [(8, 1)] = 9,
126 [(8, 16)] = 10,
127 [(8, 17)] = 11,
128 [(8, 19)] = 12,
129 [(8, 25)] = 13,
130 [(8, 26)] = 14,
131 [(8, 27)] = 15,
132 [(17, 19)] = 18,
133 };
134
143 private static readonly Dictionary<int, (int First, int Second)> SplitJong = CombinedJong
144 .ToDictionary(x => x.Value, x => x.Key);
145
154 private int _cho = -1;
163 private int _jung = -1;
172 private int _jong;
173
182 public void Reset()
183 {
184 _cho = -1;
185 _jung = -1;
186 _jong = 0;
187 }
188
221 public static bool IsComposableJamo(string text)
222 {
223 return text.Length == 1 && (ChoIndex.ContainsKey(text) || JungIndex.ContainsKey(text));
224 }
225
250 public HangulEdit Input(string text)
251 {
252 return Input(text, string.Empty);
253 }
254
287 public HangulEdit Input(string text, string textBeforeCaret)
288 {
289 if (string.IsNullOrEmpty(text) || text.Length != 1)
290 {
291 Reset();
292 return new HangulEdit(0, text);
293 }
294
295 if (ChoIndex.TryGetValue(text, out var cho))
296 {
297 if (TryComposeConsonantWithTrailingText(textBeforeCaret, text, out var edit))
298 return edit;
299
300 if (!string.IsNullOrEmpty(textBeforeCaret))
301 {
302 Reset();
303 return new HangulEdit(0, text);
304 }
305
306 return InputConsonant(text, cho);
307 }
308
309 if (JungIndex.TryGetValue(text, out var jung))
310 {
311 if (TryComposeVowelWithTrailingText(textBeforeCaret, jung, out var edit))
312 return edit;
313
314 if (!string.IsNullOrEmpty(textBeforeCaret))
315 {
316 Reset();
317 return new HangulEdit(0, text);
318 }
319
320 return InputVowel(text, jung);
321 }
322
323 Reset();
324 return new HangulEdit(0, text);
325 }
326
367 private bool TryComposeConsonantWithTrailingText(string textBeforeCaret, string text, out HangulEdit edit)
368 {
369 edit = default;
370 if (string.IsNullOrEmpty(textBeforeCaret) || !JongIndex.TryGetValue(text, out var jong))
371 return false;
372
373 var last = textBeforeCaret[^1];
374 if (!TryDecompose(last, out var cho, out var jung, out var currentJong))
375 return false;
376
377 if (currentJong == 0)
378 {
379 _cho = cho;
380 _jung = jung;
381 _jong = jong;
382 edit = new HangulEdit(1, Compose(_cho, _jung, _jong));
383 return true;
384 }
385
386 if (CombinedJong.TryGetValue((currentJong, jong), out var combinedJong))
387 {
388 _cho = cho;
389 _jung = jung;
390 _jong = combinedJong;
391 edit = new HangulEdit(1, Compose(_cho, _jung, _jong));
392 return true;
393 }
394
395 return false;
396 }
397
438 private bool TryComposeVowelWithTrailingText(string textBeforeCaret, int jung, out HangulEdit edit)
439 {
440 edit = default;
441 if (string.IsNullOrEmpty(textBeforeCaret))
442 return false;
443
444 var last = textBeforeCaret[^1].ToString();
445 if (ChoIndex.TryGetValue(last, out var trailingCho))
446 {
447 _cho = trailingCho;
448 _jung = jung;
449 _jong = 0;
450 edit = new HangulEdit(1, Compose(_cho, _jung, 0));
451 return true;
452 }
453
454 var lastChar = textBeforeCaret[^1];
455 if (!TryDecompose(lastChar, out var cho, out var currentJung, out var jong) || jong == 0)
456 return false;
457
458 if (SplitJong.TryGetValue(jong, out var split))
459 {
460 var previous = Compose(cho, currentJung, split.First);
461 _cho = ToChoIndex(Jong[split.Second]);
462 _jung = jung;
463 _jong = 0;
464 edit = new HangulEdit(1, previous + Compose(_cho, _jung, 0));
465 return true;
466 }
467
468 _cho = ToChoIndex(Jong[jong]);
469 _jung = jung;
470 _jong = 0;
471 edit = new HangulEdit(1, Compose(cho, currentJung, 0) + Compose(_cho, _jung, 0));
472 return true;
473 }
474
507 private HangulEdit InputConsonant(string text, int cho)
508 {
509 if (_cho < 0)
510 {
511 _cho = cho;
512 return new HangulEdit(0, text);
513 }
514
515 if (_jung < 0)
516 {
517 _cho = cho;
518 return new HangulEdit(0, text);
519 }
520
521 if (_jong == 0)
522 {
523 if (JongIndex.TryGetValue(text, out var jong))
524 {
525 _jong = jong;
526 return new HangulEdit(1, Compose(_cho, _jung, _jong));
527 }
528
529 _cho = cho;
530 _jung = -1;
531 _jong = 0;
532 return new HangulEdit(0, text);
533 }
534
535 if (JongIndex.TryGetValue(text, out var nextJong) &&
536 CombinedJong.TryGetValue((_jong, nextJong), out var combinedJong))
537 {
538 _jong = combinedJong;
539 return new HangulEdit(1, Compose(_cho, _jung, _jong));
540 }
541
542 _cho = cho;
543 _jung = -1;
544 _jong = 0;
545 return new HangulEdit(0, text);
546 }
547
580 private HangulEdit InputVowel(string text, int jung)
581 {
582 if (_cho < 0)
583 {
584 Reset();
585 return new HangulEdit(0, text);
586 }
587
588 if (_jung < 0)
589 {
590 _jung = jung;
591 return new HangulEdit(1, Compose(_cho, _jung, 0));
592 }
593
594 if (_jong == 0)
595 {
596 if (CombinedJung.TryGetValue((_jung, jung), out var combinedJung))
597 {
598 _jung = combinedJung;
599 return new HangulEdit(1, Compose(_cho, _jung, 0));
600 }
601
602 Reset();
603 return new HangulEdit(0, text);
604 }
605
606 var previousCho = _cho;
607 var previousJung = _jung;
608 var previousJong = _jong;
609
610 if (SplitJong.TryGetValue(previousJong, out var split))
611 {
612 var previous = Compose(previousCho, previousJung, split.First);
613 _cho = ToChoIndex(Jong[split.Second]);
614 _jung = jung;
615 _jong = 0;
616 return new HangulEdit(1, previous + Compose(_cho, _jung, 0));
617 }
618
619 _cho = ToChoIndex(Jong[previousJong]);
620 _jung = jung;
621 _jong = 0;
622 return new HangulEdit(1, Compose(previousCho, previousJung, 0) + Compose(_cho, _jung, 0));
623 }
624
649 private static int ToChoIndex(string jong)
650 {
651 return ChoIndex.TryGetValue(jong, out var cho) ? cho : ChoIndex["ㅇ"];
652 }
653
702 private static string Compose(int cho, int jung, int jong)
703 {
704 return char.ConvertFromUtf32(0xAC00 + ((cho * 21) + jung) * 28 + jong);
705 }
706
755 private static bool TryDecompose(char value, out int cho, out int jung, out int jong)
756 {
757 var code = value - 0xAC00;
758 if (code < 0 || code >= 11172)
759 {
760 cho = -1;
761 jung = -1;
762 jong = 0;
763 return false;
764 }
765
766 cho = code / (21 * 28);
767 jung = (code % (21 * 28)) / 28;
768 jong = code % 28;
769 return true;
770 }
771}
772
797internal readonly record struct HangulEdit(int ReplaceCount, string Text);