public String metaphone()

in src/main/java/org/apache/commons/codec/language/Metaphone.java [170:386]


    public String metaphone(final String txt) {
        boolean hard = false;
        final int txtLength;
        if (txt == null || (txtLength = txt.length()) == 0) {
            return "";
        }
        // single character is itself
        if (txtLength == 1) {
            return txt.toUpperCase(java.util.Locale.ENGLISH);
        }

        final char[] inwd = txt.toUpperCase(java.util.Locale.ENGLISH).toCharArray();

        final StringBuilder local = new StringBuilder(40); // manipulate
        final StringBuilder code = new StringBuilder(10); // output
        // handle initial 2 characters exceptions
        switch (inwd[0]) {
        case 'K':
        case 'G':
        case 'P': /* looking for KN, etc */
            if (inwd[1] == 'N') {
                local.append(inwd, 1, inwd.length - 1);
            } else {
                local.append(inwd);
            }
            break;
        case 'A': /* looking for AE */
            if (inwd[1] == 'E') {
                local.append(inwd, 1, inwd.length - 1);
            } else {
                local.append(inwd);
            }
            break;
        case 'W': /* looking for WR or WH */
            if (inwd[1] == 'R') { // WR -> R
                local.append(inwd, 1, inwd.length - 1);
                break;
            }
            if (inwd[1] == 'H') {
                local.append(inwd, 1, inwd.length - 1);
                local.setCharAt(0, 'W'); // WH -> W
            } else {
                local.append(inwd);
            }
            break;
        case 'X': /* initial X becomes S */
            inwd[0] = 'S';
            local.append(inwd);
            break;
        default:
            local.append(inwd);
        } // now local has working string with initials fixed

        final int wdsz = local.length();
        int n = 0;

        while (code.length() < getMaxCodeLen() && n < wdsz) { // max code size of 4 works well
            final char symb = local.charAt(n);
            // remove duplicate letters except C
            if (symb != 'C' && isPreviousChar(local, n, symb)) {
                // empty
            } else { // not dup
                switch (symb) {
                case 'A':
                case 'E':
                case 'I':
                case 'O':
                case 'U':
                    if (n == 0) {
                        code.append(symb);
                    }
                    break; // only use vowel if leading char
                case 'B':
                    if (isPreviousChar(local, n, 'M') && isLastChar(wdsz, n)) { // B is silent if word ends in MB
                        break;
                    }
                    code.append(symb);
                    break;
                case 'C': // lots of C special cases
                    /* discard if SCI, SCE or SCY */
                    if (isPreviousChar(local, n, 'S') && !isLastChar(wdsz, n) && FRONTV.indexOf(local.charAt(n + 1)) >= 0) {
                        break;
                    }
                    if (regionMatch(local, n, "CIA")) { // "CIA" -> X
                        code.append('X');
                        break;
                    }
                    if (!isLastChar(wdsz, n) && FRONTV.indexOf(local.charAt(n + 1)) >= 0) {
                        code.append('S');
                        break; // CI,CE,CY -> S
                    }
                    if (isPreviousChar(local, n, 'S') && isNextChar(local, n, 'H')) { // SCH->sk
                        code.append('K');
                        break;
                    }
                    if (!isNextChar(local, n, 'H') || n == 0 && wdsz >= 3 && isVowel(local, 2)) { // CH consonant -> K consonant
                        code.append('K');
                    } else {
                        code.append('X'); // CHvowel -> X
                    }
                    break;
                case 'D':
                    if (!isLastChar(wdsz, n + 1) && isNextChar(local, n, 'G') && FRONTV.indexOf(local.charAt(n + 2)) >= 0) { // DGE DGI DGY -> J
                        code.append('J');
                        n += 2;
                    } else {
                        code.append('T');
                    }
                    break;
                case 'G': // GH silent at end or before consonant
                    if (isLastChar(wdsz, n + 1) && isNextChar(local, n, 'H')) {
                        break;
                    }
                    if (!isLastChar(wdsz, n + 1) && isNextChar(local, n, 'H') && !isVowel(local, n + 2)) {
                        break;
                    }
                    if (n > 0 && (regionMatch(local, n, "GN") || regionMatch(local, n, "GNED"))) {
                        break; // silent G
                    }
                    // NOTE: Given that duplicated chars are removed, I don't see how this can ever be true
                    hard = isPreviousChar(local, n, 'G');
                    if (!isLastChar(wdsz, n) && FRONTV.indexOf(local.charAt(n + 1)) >= 0 && !hard) {
                        code.append('J');
                    } else {
                        code.append('K');
                    }
                    break;
                case 'H':
                    if (isLastChar(wdsz, n)) {
                        break; // terminal H
                    }
                    if (n > 0 && VARSON.indexOf(local.charAt(n - 1)) >= 0) {
                        break;
                    }
                    if (isVowel(local, n + 1)) {
                        code.append('H'); // Hvowel
                    }
                    break;
                case 'F':
                case 'J':
                case 'L':
                case 'M':
                case 'N':
                case 'R':
                    code.append(symb);
                    break;
                case 'K':
                    if (n > 0) { // not initial
                        if (!isPreviousChar(local, n, 'C')) {
                            code.append(symb);
                        }
                    } else {
                        code.append(symb); // initial K
                    }
                    break;
                case 'P':
                    if (isNextChar(local, n, 'H')) {
                        // PH -> F
                        code.append('F');
                    } else {
                        code.append(symb);
                    }
                    break;
                case 'Q':
                    code.append('K');
                    break;
                case 'S':
                    if (regionMatch(local, n, "SH") || regionMatch(local, n, "SIO") || regionMatch(local, n, "SIA")) {
                        code.append('X');
                    } else {
                        code.append('S');
                    }
                    break;
                case 'T':
                    if (regionMatch(local, n, "TIA") || regionMatch(local, n, "TIO")) {
                        code.append('X');
                        break;
                    }
                    if (regionMatch(local, n, "TCH")) {
                        // Silent if in "TCH"
                        break;
                    }
                    // substitute numeral 0 for TH (resembles theta after all)
                    if (regionMatch(local, n, "TH")) {
                        code.append('0');
                    } else {
                        code.append('T');
                    }
                    break;
                case 'V':
                    code.append('F');
                    break;
                case 'W':
                case 'Y': // silent if not followed by vowel
                    if (!isLastChar(wdsz, n) && isVowel(local, n + 1)) {
                        code.append(symb);
                    }
                    break;
                case 'X':
                    code.append('K');
                    code.append('S');
                    break;
                case 'Z':
                    code.append('S');
                    break;
                default:
                    // do nothing
                    break;
                } // end switch
            } // end else from symb != 'C'
            n++;
            if (code.length() > getMaxCodeLen()) {
                code.setLength(getMaxCodeLen());
            }
        }
        return code.toString();
    }