1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.codec.language;
19
20 import org.apache.commons.codec.EncoderException;
21 import org.apache.commons.codec.StringEncoder;
22 import org.apache.commons.codec.binary.StringUtils;
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public class DoubleMetaphone implements StringEncoder {
39
40
41
42
43 private static final String VOWELS = "AEIOUY";
44
45
46
47
48 private static final String[] SILENT_START =
49 { "GN", "KN", "PN", "WR", "PS" };
50 private static final String[] L_R_N_M_B_H_F_V_W_SPACE =
51 { "L", "R", "N", "M", "B", "H", "F", "V", "W", " " };
52 private static final String[] ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER =
53 { "ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER" };
54 private static final String[] L_T_K_S_N_M_B_Z =
55 { "L", "T", "K", "S", "N", "M", "B", "Z" };
56
57
58
59
60 private int maxCodeLen = 4;
61
62
63
64
65 public DoubleMetaphone() {
66 super();
67 }
68
69
70
71
72
73
74
75 public String doubleMetaphone(final String value) {
76 return doubleMetaphone(value, false);
77 }
78
79
80
81
82
83
84
85
86 public String doubleMetaphone(String value, final boolean alternate) {
87 value = cleanInput(value);
88 if (value == null) {
89 return null;
90 }
91
92 final boolean slavoGermanic = isSlavoGermanic(value);
93 int index = isSilentStart(value) ? 1 : 0;
94
95 final DoubleMetaphoneResult result = new DoubleMetaphoneResult(this.getMaxCodeLen());
96
97 while (!result.isComplete() && index <= value.length() - 1) {
98 switch (value.charAt(index)) {
99 case 'A':
100 case 'E':
101 case 'I':
102 case 'O':
103 case 'U':
104 case 'Y':
105 index = handleAEIOUY(result, index);
106 break;
107 case 'B':
108 result.append('P');
109 index = charAt(value, index + 1) == 'B' ? index + 2 : index + 1;
110 break;
111 case '\u00C7':
112
113 result.append('S');
114 index++;
115 break;
116 case 'C':
117 index = handleC(value, result, index);
118 break;
119 case 'D':
120 index = handleD(value, result, index);
121 break;
122 case 'F':
123 result.append('F');
124 index = charAt(value, index + 1) == 'F' ? index + 2 : index + 1;
125 break;
126 case 'G':
127 index = handleG(value, result, index, slavoGermanic);
128 break;
129 case 'H':
130 index = handleH(value, result, index);
131 break;
132 case 'J':
133 index = handleJ(value, result, index, slavoGermanic);
134 break;
135 case 'K':
136 result.append('K');
137 index = charAt(value, index + 1) == 'K' ? index + 2 : index + 1;
138 break;
139 case 'L':
140 index = handleL(value, result, index);
141 break;
142 case 'M':
143 result.append('M');
144 index = conditionM0(value, index) ? index + 2 : index + 1;
145 break;
146 case 'N':
147 result.append('N');
148 index = charAt(value, index + 1) == 'N' ? index + 2 : index + 1;
149 break;
150 case '\u00D1':
151
152 result.append('N');
153 index++;
154 break;
155 case 'P':
156 index = handleP(value, result, index);
157 break;
158 case 'Q':
159 result.append('K');
160 index = charAt(value, index + 1) == 'Q' ? index + 2 : index + 1;
161 break;
162 case 'R':
163 index = handleR(value, result, index, slavoGermanic);
164 break;
165 case 'S':
166 index = handleS(value, result, index, slavoGermanic);
167 break;
168 case 'T':
169 index = handleT(value, result, index);
170 break;
171 case 'V':
172 result.append('F');
173 index = charAt(value, index + 1) == 'V' ? index + 2 : index + 1;
174 break;
175 case 'W':
176 index = handleW(value, result, index);
177 break;
178 case 'X':
179 index = handleX(value, result, index);
180 break;
181 case 'Z':
182 index = handleZ(value, result, index, slavoGermanic);
183 break;
184 default:
185 index++;
186 break;
187 }
188 }
189
190 return alternate ? result.getAlternate() : result.getPrimary();
191 }
192
193
194
195
196
197
198
199
200
201 @Override
202 public Object encode(final Object obj) throws EncoderException {
203 if (!(obj instanceof String)) {
204 throw new EncoderException("DoubleMetaphone encode parameter is not of type String");
205 }
206 return doubleMetaphone((String) obj);
207 }
208
209
210
211
212
213
214
215 @Override
216 public String encode(final String value) {
217 return doubleMetaphone(value);
218 }
219
220
221
222
223
224
225
226
227
228
229
230 public boolean isDoubleMetaphoneEqual(final String value1, final String value2) {
231 return isDoubleMetaphoneEqual(value1, value2, false);
232 }
233
234
235
236
237
238
239
240
241
242
243
244 public boolean isDoubleMetaphoneEqual(final String value1, final String value2, final boolean alternate) {
245 return StringUtils.equals(doubleMetaphone(value1, alternate), doubleMetaphone(value2, alternate));
246 }
247
248
249
250
251
252 public int getMaxCodeLen() {
253 return this.maxCodeLen;
254 }
255
256
257
258
259
260 public void setMaxCodeLen(final int maxCodeLen) {
261 this.maxCodeLen = maxCodeLen;
262 }
263
264
265
266
267
268
269 private int handleAEIOUY(final DoubleMetaphoneResult result, final int index) {
270 if (index == 0) {
271 result.append('A');
272 }
273 return index + 1;
274 }
275
276
277
278
279 private int handleC(final String value, final DoubleMetaphoneResult result, int index) {
280 if (conditionC0(value, index)) {
281 result.append('K');
282 index += 2;
283 } else if (index == 0 && contains(value, index, 6, "CAESAR")) {
284 result.append('S');
285 index += 2;
286 } else if (contains(value, index, 2, "CH")) {
287 index = handleCH(value, result, index);
288 } else if (contains(value, index, 2, "CZ") &&
289 !contains(value, index - 2, 4, "WICZ")) {
290
291 result.append('S', 'X');
292 index += 2;
293 } else if (contains(value, index + 1, 3, "CIA")) {
294
295 result.append('X');
296 index += 3;
297 } else if (contains(value, index, 2, "CC") &&
298 !(index == 1 && charAt(value, 0) == 'M')) {
299
300 return handleCC(value, result, index);
301 } else if (contains(value, index, 2, "CK", "CG", "CQ")) {
302 result.append('K');
303 index += 2;
304 } else if (contains(value, index, 2, "CI", "CE", "CY")) {
305
306 if (contains(value, index, 3, "CIO", "CIE", "CIA")) {
307 result.append('S', 'X');
308 } else {
309 result.append('S');
310 }
311 index += 2;
312 } else {
313 result.append('K');
314 if (contains(value, index + 1, 2, " C", " Q", " G")) {
315
316 index += 3;
317 } else if (contains(value, index + 1, 1, "C", "K", "Q") &&
318 !contains(value, index + 1, 2, "CE", "CI")) {
319 index += 2;
320 } else {
321 index++;
322 }
323 }
324
325 return index;
326 }
327
328
329
330
331 private int handleCC(final String value, final DoubleMetaphoneResult result, int index) {
332 if (contains(value, index + 2, 1, "I", "E", "H") &&
333 !contains(value, index + 2, 2, "HU")) {
334
335 if ((index == 1 && charAt(value, index - 1) == 'A') ||
336 contains(value, index - 1, 5, "UCCEE", "UCCES")) {
337
338 result.append("KS");
339 } else {
340
341 result.append('X');
342 }
343 index += 3;
344 } else {
345 result.append('K');
346 index += 2;
347 }
348
349 return index;
350 }
351
352
353
354
355 private int handleCH(final String value, final DoubleMetaphoneResult result, final int index) {
356 if (index > 0 && contains(value, index, 4, "CHAE")) {
357 result.append('K', 'X');
358 return index + 2;
359 } else if (conditionCH0(value, index)) {
360
361 result.append('K');
362 return index + 2;
363 } else if (conditionCH1(value, index)) {
364
365 result.append('K');
366 return index + 2;
367 } else {
368 if (index > 0) {
369 if (contains(value, 0, 2, "MC")) {
370 result.append('K');
371 } else {
372 result.append('X', 'K');
373 }
374 } else {
375 result.append('X');
376 }
377 return index + 2;
378 }
379 }
380
381
382
383
384 private int handleD(final String value, final DoubleMetaphoneResult result, int index) {
385 if (contains(value, index, 2, "DG")) {
386
387 if (contains(value, index + 2, 1, "I", "E", "Y")) {
388 result.append('J');
389 index += 3;
390
391 } else {
392 result.append("TK");
393 index += 2;
394 }
395 } else if (contains(value, index, 2, "DT", "DD")) {
396 result.append('T');
397 index += 2;
398 } else {
399 result.append('T');
400 index++;
401 }
402 return index;
403 }
404
405
406
407
408 private int handleG(final String value, final DoubleMetaphoneResult result, int index,
409 final boolean slavoGermanic) {
410 if (charAt(value, index + 1) == 'H') {
411 index = handleGH(value, result, index);
412 } else if (charAt(value, index + 1) == 'N') {
413 if (index == 1 && isVowel(charAt(value, 0)) && !slavoGermanic) {
414 result.append("KN", "N");
415 } else if (!contains(value, index + 2, 2, "EY") &&
416 charAt(value, index + 1) != 'Y' && !slavoGermanic) {
417 result.append("N", "KN");
418 } else {
419 result.append("KN");
420 }
421 index = index + 2;
422 } else if (contains(value, index + 1, 2, "LI") && !slavoGermanic) {
423 result.append("KL", "L");
424 index += 2;
425 } else if (index == 0 &&
426 (charAt(value, index + 1) == 'Y' ||
427 contains(value, index + 1, 2, ES_EP_EB_EL_EY_IB_IL_IN_IE_EI_ER))) {
428
429 result.append('K', 'J');
430 index += 2;
431 } else if ((contains(value, index + 1, 2, "ER") ||
432 charAt(value, index + 1) == 'Y') &&
433 !contains(value, 0, 6, "DANGER", "RANGER", "MANGER") &&
434 !contains(value, index - 1, 1, "E", "I") &&
435 !contains(value, index - 1, 3, "RGY", "OGY")) {
436
437 result.append('K', 'J');
438 index += 2;
439 } else if (contains(value, index + 1, 1, "E", "I", "Y") ||
440 contains(value, index - 1, 4, "AGGI", "OGGI")) {
441
442 if (contains(value, 0 ,4, "VAN ", "VON ") ||
443 contains(value, 0, 3, "SCH") ||
444 contains(value, index + 1, 2, "ET")) {
445
446 result.append('K');
447 } else if (contains(value, index + 1, 3, "IER")) {
448 result.append('J');
449 } else {
450 result.append('J', 'K');
451 }
452 index += 2;
453 } else if (charAt(value, index + 1) == 'G') {
454 index += 2;
455 result.append('K');
456 } else {
457 index++;
458 result.append('K');
459 }
460 return index;
461 }
462
463
464
465
466 private int handleGH(final String value, final DoubleMetaphoneResult result, int index) {
467 if (index > 0 && !isVowel(charAt(value, index - 1))) {
468 result.append('K');
469 index += 2;
470 } else if (index == 0) {
471 if (charAt(value, index + 2) == 'I') {
472 result.append('J');
473 } else {
474 result.append('K');
475 }
476 index += 2;
477 } else if ((index > 1 && contains(value, index - 2, 1, "B", "H", "D")) ||
478 (index > 2 && contains(value, index - 3, 1, "B", "H", "D")) ||
479 (index > 3 && contains(value, index - 4, 1, "B", "H"))) {
480
481 index += 2;
482 } else {
483 if (index > 2 && charAt(value, index - 1) == 'U' &&
484 contains(value, index - 3, 1, "C", "G", "L", "R", "T")) {
485
486 result.append('F');
487 } else if (index > 0 && charAt(value, index - 1) != 'I') {
488 result.append('K');
489 }
490 index += 2;
491 }
492 return index;
493 }
494
495
496
497
498 private int handleH(final String value, final DoubleMetaphoneResult result, int index) {
499
500 if ((index == 0 || isVowel(charAt(value, index - 1))) &&
501 isVowel(charAt(value, index + 1))) {
502 result.append('H');
503 index += 2;
504
505 } else {
506 index++;
507 }
508 return index;
509 }
510
511
512
513
514 private int handleJ(final String value, final DoubleMetaphoneResult result, int index,
515 final boolean slavoGermanic) {
516 if (contains(value, index, 4, "JOSE") || contains(value, 0, 4, "SAN ")) {
517
518 if ((index == 0 && (charAt(value, index + 4) == ' ') ||
519 value.length() == 4) || contains(value, 0, 4, "SAN ")) {
520 result.append('H');
521 } else {
522 result.append('J', 'H');
523 }
524 index++;
525 } else {
526 if (index == 0 && !contains(value, index, 4, "JOSE")) {
527 result.append('J', 'A');
528 } else if (isVowel(charAt(value, index - 1)) && !slavoGermanic &&
529 (charAt(value, index + 1) == 'A' || charAt(value, index + 1) == 'O')) {
530 result.append('J', 'H');
531 } else if (index == value.length() - 1) {
532 result.append('J', ' ');
533 } else if (!contains(value, index + 1, 1, L_T_K_S_N_M_B_Z) &&
534 !contains(value, index - 1, 1, "S", "K", "L")) {
535 result.append('J');
536 }
537
538 if (charAt(value, index + 1) == 'J') {
539 index += 2;
540 } else {
541 index++;
542 }
543 }
544 return index;
545 }
546
547
548
549
550 private int handleL(final String value, final DoubleMetaphoneResult result, int index) {
551 if (charAt(value, index + 1) == 'L') {
552 if (conditionL0(value, index)) {
553 result.appendPrimary('L');
554 } else {
555 result.append('L');
556 }
557 index += 2;
558 } else {
559 index++;
560 result.append('L');
561 }
562 return index;
563 }
564
565
566
567
568 private int handleP(final String value, final DoubleMetaphoneResult result, int index) {
569 if (charAt(value, index + 1) == 'H') {
570 result.append('F');
571 index += 2;
572 } else {
573 result.append('P');
574 index = contains(value, index + 1, 1, "P", "B") ? index + 2 : index + 1;
575 }
576 return index;
577 }
578
579
580
581
582 private int handleR(final String value, final DoubleMetaphoneResult result, final int index,
583 final boolean slavoGermanic) {
584 if (index == value.length() - 1 && !slavoGermanic &&
585 contains(value, index - 2, 2, "IE") &&
586 !contains(value, index - 4, 2, "ME", "MA")) {
587 result.appendAlternate('R');
588 } else {
589 result.append('R');
590 }
591 return charAt(value, index + 1) == 'R' ? index + 2 : index + 1;
592 }
593
594
595
596
597 private int handleS(final String value, final DoubleMetaphoneResult result, int index,
598 final boolean slavoGermanic) {
599 if (contains(value, index - 1, 3, "ISL", "YSL")) {
600
601 index++;
602 } else if (index == 0 && contains(value, index, 5, "SUGAR")) {
603
604 result.append('X', 'S');
605 index++;
606 } else if (contains(value, index, 2, "SH")) {
607 if (contains(value, index + 1, 4, "HEIM", "HOEK", "HOLM", "HOLZ")) {
608
609 result.append('S');
610 } else {
611 result.append('X');
612 }
613 index += 2;
614 } else if (contains(value, index, 3, "SIO", "SIA") || contains(value, index, 4, "SIAN")) {
615
616 if (slavoGermanic) {
617 result.append('S');
618 } else {
619 result.append('S', 'X');
620 }
621 index += 3;
622 } else if ((index == 0 && contains(value, index + 1, 1, "M", "N", "L", "W")) ||
623 contains(value, index + 1, 1, "Z")) {
624
625
626
627
628 result.append('S', 'X');
629 index = contains(value, index + 1, 1, "Z") ? index + 2 : index + 1;
630 } else if (contains(value, index, 2, "SC")) {
631 index = handleSC(value, result, index);
632 } else {
633 if (index == value.length() - 1 && contains(value, index - 2, 2, "AI", "OI")) {
634
635 result.appendAlternate('S');
636 } else {
637 result.append('S');
638 }
639 index = contains(value, index + 1, 1, "S", "Z") ? index + 2 : index + 1;
640 }
641 return index;
642 }
643
644
645
646
647 private int handleSC(final String value, final DoubleMetaphoneResult result, final int index) {
648 if (charAt(value, index + 2) == 'H') {
649
650 if (contains(value, index + 3, 2, "OO", "ER", "EN", "UY", "ED", "EM")) {
651
652 if (contains(value, index + 3, 2, "ER", "EN")) {
653
654 result.append("X", "SK");
655 } else {
656 result.append("SK");
657 }
658 } else {
659 if (index == 0 && !isVowel(charAt(value, 3)) && charAt(value, 3) != 'W') {
660 result.append('X', 'S');
661 } else {
662 result.append('X');
663 }
664 }
665 } else if (contains(value, index + 2, 1, "I", "E", "Y")) {
666 result.append('S');
667 } else {
668 result.append("SK");
669 }
670 return index + 3;
671 }
672
673
674
675
676 private int handleT(final String value, final DoubleMetaphoneResult result, int index) {
677 if (contains(value, index, 4, "TION")) {
678 result.append('X');
679 index += 3;
680 } else if (contains(value, index, 3, "TIA", "TCH")) {
681 result.append('X');
682 index += 3;
683 } else if (contains(value, index, 2, "TH") || contains(value, index, 3, "TTH")) {
684 if (contains(value, index + 2, 2, "OM", "AM") ||
685
686 contains(value, 0, 4, "VAN ", "VON ") ||
687 contains(value, 0, 3, "SCH")) {
688 result.append('T');
689 } else {
690 result.append('0', 'T');
691 }
692 index += 2;
693 } else {
694 result.append('T');
695 index = contains(value, index + 1, 1, "T", "D") ? index + 2 : index + 1;
696 }
697 return index;
698 }
699
700
701
702
703 private int handleW(final String value, final DoubleMetaphoneResult result, int index) {
704 if (contains(value, index, 2, "WR")) {
705
706 result.append('R');
707 index += 2;
708 } else {
709 if (index == 0 && (isVowel(charAt(value, index + 1)) ||
710 contains(value, index, 2, "WH"))) {
711 if (isVowel(charAt(value, index + 1))) {
712
713 result.append('A', 'F');
714 } else {
715
716 result.append('A');
717 }
718 index++;
719 } else if ((index == value.length() - 1 && isVowel(charAt(value, index - 1))) ||
720 contains(value, index - 1, 5, "EWSKI", "EWSKY", "OWSKI", "OWSKY") ||
721 contains(value, 0, 3, "SCH")) {
722
723 result.appendAlternate('F');
724 index++;
725 } else if (contains(value, index, 4, "WICZ", "WITZ")) {
726
727 result.append("TS", "FX");
728 index += 4;
729 } else {
730 index++;
731 }
732 }
733 return index;
734 }
735
736
737
738
739 private int handleX(final String value, final DoubleMetaphoneResult result, int index) {
740 if (index == 0) {
741 result.append('S');
742 index++;
743 } else {
744 if (!((index == value.length() - 1) &&
745 (contains(value, index - 3, 3, "IAU", "EAU") ||
746 contains(value, index - 2, 2, "AU", "OU")))) {
747
748 result.append("KS");
749 }
750 index = contains(value, index + 1, 1, "C", "X") ? index + 2 : index + 1;
751 }
752 return index;
753 }
754
755
756
757
758 private int handleZ(final String value, final DoubleMetaphoneResult result, int index,
759 final boolean slavoGermanic) {
760 if (charAt(value, index + 1) == 'H') {
761
762 result.append('J');
763 index += 2;
764 } else {
765 if (contains(value, index + 1, 2, "ZO", "ZI", "ZA") ||
766 (slavoGermanic && (index > 0 && charAt(value, index - 1) != 'T'))) {
767 result.append("S", "TS");
768 } else {
769 result.append('S');
770 }
771 index = charAt(value, index + 1) == 'Z' ? index + 2 : index + 1;
772 }
773 return index;
774 }
775
776
777
778
779
780
781 private boolean conditionC0(final String value, final int index) {
782 if (contains(value, index, 4, "CHIA")) {
783 return true;
784 } else if (index <= 1) {
785 return false;
786 } else if (isVowel(charAt(value, index - 2))) {
787 return false;
788 } else if (!contains(value, index - 1, 3, "ACH")) {
789 return false;
790 } else {
791 final char c = charAt(value, index + 2);
792 return (c != 'I' && c != 'E') ||
793 contains(value, index - 2, 6, "BACHER", "MACHER");
794 }
795 }
796
797
798
799
800 private boolean conditionCH0(final String value, final int index) {
801 if (index != 0) {
802 return false;
803 } else if (!contains(value, index + 1, 5, "HARAC", "HARIS") &&
804 !contains(value, index + 1, 3, "HOR", "HYM", "HIA", "HEM")) {
805 return false;
806 } else if (contains(value, 0, 5, "CHORE")) {
807 return false;
808 } else {
809 return true;
810 }
811 }
812
813
814
815
816 private boolean conditionCH1(final String value, final int index) {
817 return ((contains(value, 0, 4, "VAN ", "VON ") || contains(value, 0, 3, "SCH")) ||
818 contains(value, index - 2, 6, "ORCHES", "ARCHIT", "ORCHID") ||
819 contains(value, index + 2, 1, "T", "S") ||
820 ((contains(value, index - 1, 1, "A", "O", "U", "E") || index == 0) &&
821 (contains(value, index + 2, 1, L_R_N_M_B_H_F_V_W_SPACE) || index + 1 == value.length() - 1)));
822 }
823
824
825
826
827 private boolean conditionL0(final String value, final int index) {
828 if (index == value.length() - 3 &&
829 contains(value, index - 1, 4, "ILLO", "ILLA", "ALLE")) {
830 return true;
831 } else if ((contains(value, value.length() - 2, 2, "AS", "OS") ||
832 contains(value, value.length() - 1, 1, "A", "O")) &&
833 contains(value, index - 1, 4, "ALLE")) {
834 return true;
835 } else {
836 return false;
837 }
838 }
839
840
841
842
843 private boolean conditionM0(final String value, final int index) {
844 if (charAt(value, index + 1) == 'M') {
845 return true;
846 }
847 return contains(value, index - 1, 3, "UMB") &&
848 ((index + 1) == value.length() - 1 || contains(value, index + 2, 2, "ER"));
849 }
850
851
852
853
854
855
856
857 private boolean isSlavoGermanic(final String value) {
858 return value.indexOf('W') > -1 || value.indexOf('K') > -1 ||
859 value.indexOf("CZ") > -1 || value.indexOf("WITZ") > -1;
860 }
861
862
863
864
865 private boolean isVowel(final char ch) {
866 return VOWELS.indexOf(ch) != -1;
867 }
868
869
870
871
872
873
874 private boolean isSilentStart(final String value) {
875 boolean result = false;
876 for (final String element : SILENT_START) {
877 if (value.startsWith(element)) {
878 result = true;
879 break;
880 }
881 }
882 return result;
883 }
884
885
886
887
888 private String cleanInput(String input) {
889 if (input == null) {
890 return null;
891 }
892 input = input.trim();
893 if (input.length() == 0) {
894 return null;
895 }
896 return input.toUpperCase(java.util.Locale.ENGLISH);
897 }
898
899
900
901
902
903
904 protected char charAt(final String value, final int index) {
905 if (index < 0 || index >= value.length()) {
906 return Character.MIN_VALUE;
907 }
908 return value.charAt(index);
909 }
910
911
912
913
914
915 protected static boolean contains(final String value, final int start, final int length,
916 final String... criteria) {
917 boolean result = false;
918 if (start >= 0 && start + length <= value.length()) {
919 final String target = value.substring(start, start + length);
920
921 for (final String element : criteria) {
922 if (target.equals(element)) {
923 result = true;
924 break;
925 }
926 }
927 }
928 return result;
929 }
930
931
932
933
934
935
936 public class DoubleMetaphoneResult {
937
938 private final StringBuilder primary = new StringBuilder(getMaxCodeLen());
939 private final StringBuilder alternate = new StringBuilder(getMaxCodeLen());
940 private final int maxLength;
941
942 public DoubleMetaphoneResult(final int maxLength) {
943 this.maxLength = maxLength;
944 }
945
946 public void append(final char value) {
947 appendPrimary(value);
948 appendAlternate(value);
949 }
950
951 public void append(final char primary, final char alternate) {
952 appendPrimary(primary);
953 appendAlternate(alternate);
954 }
955
956 public void appendPrimary(final char value) {
957 if (this.primary.length() < this.maxLength) {
958 this.primary.append(value);
959 }
960 }
961
962 public void appendAlternate(final char value) {
963 if (this.alternate.length() < this.maxLength) {
964 this.alternate.append(value);
965 }
966 }
967
968 public void append(final String value) {
969 appendPrimary(value);
970 appendAlternate(value);
971 }
972
973 public void append(final String primary, final String alternate) {
974 appendPrimary(primary);
975 appendAlternate(alternate);
976 }
977
978 public void appendPrimary(final String value) {
979 final int addChars = this.maxLength - this.primary.length();
980 if (value.length() <= addChars) {
981 this.primary.append(value);
982 } else {
983 this.primary.append(value.substring(0, addChars));
984 }
985 }
986
987 public void appendAlternate(final String value) {
988 final int addChars = this.maxLength - this.alternate.length();
989 if (value.length() <= addChars) {
990 this.alternate.append(value);
991 } else {
992 this.alternate.append(value.substring(0, addChars));
993 }
994 }
995
996 public String getPrimary() {
997 return this.primary.toString();
998 }
999
1000 public String getAlternate() {
1001 return this.alternate.toString();
1002 }
1003
1004 public boolean isComplete() {
1005 return this.primary.length() >= this.maxLength &&
1006 this.alternate.length() >= this.maxLength;
1007 }
1008 }
1009 }