1069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project/*
2069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Copyright 2001-2004 The Apache Software Foundation.
3069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
4069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * you may not use this file except in compliance with the License.
6069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * You may obtain a copy of the License at
7069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
8069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
10069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * See the License for the specific language governing permissions and
14069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * limitations under the License.
15069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project */
16069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
17069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectpackage org.apache.commons.codec.language;
18069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
19069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.commons.codec.EncoderException;
20069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectimport org.apache.commons.codec.StringEncoder;
21069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
22069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project/**
23069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Encodes a string into a metaphone value.
24069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <p>
25069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Initial Java implementation by <CITE>William B. Brogden. December, 1997</CITE>.
26069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * Permission given by <CITE>wbrogden</CITE> for code to be used anywhere.
27069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * </p>
28069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <p>
29069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * <CITE>Hanging on the Metaphone</CITE> by <CITE>Lawrence Philips</CITE> in <CITE>Computer Language of Dec. 1990, p
30069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * 39.</CITE>
31069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * </p>
32069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project *
33069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @author Apache Software Foundation
34069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project * @version $Id: Metaphone.java,v 1.20 2004/06/05 18:32:04 ggregory Exp $
35069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project */
36069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Projectpublic class Metaphone implements StringEncoder {
37069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
38069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
39069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Five values in the English language
40069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
41069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private String vowels = "AEIOU" ;
42069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
43069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
44069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Variable used in Metaphone algorithm
45069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
46069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private String frontv = "EIY"   ;
47069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
48069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
49069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Variable used in Metaphone algorithm
50069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
51069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private String varson = "CSPTG" ;
52069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
53069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
54069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * The max code length for metaphone is 4
55069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
56069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private int maxCodeLen = 4 ;
57069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
58069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
59069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Creates an instance of the Metaphone encoder
60069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
61069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public Metaphone() {
62069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        super();
63069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
64069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
65069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
66069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Find the metaphone value of a String. This is similar to the
67069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * soundex algorithm, but better at finding similar sounding words.
68069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * All input is converted to upper case.
69069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Limitations: Input format is expected to be a single ASCII word
70069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * with only characters in the A - Z range, no punctuation or numbers.
71069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
72069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param txt String to find the metaphone code for
73069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return A metaphone code corresponding to the String supplied
74069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
75069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public String metaphone(String txt) {
76069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        boolean hard = false ;
77069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if ((txt == null) || (txt.length() == 0)) {
78069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            return "" ;
79069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
80069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        // single character is itself
81069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (txt.length() == 1) {
82069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            return txt.toUpperCase() ;
83069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
84069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
85069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        char[] inwd = txt.toUpperCase().toCharArray() ;
86069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
87069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        StringBuffer local = new StringBuffer(40); // manipulate
88069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        StringBuffer code = new StringBuffer(10) ; //   output
89069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        // handle initial 2 characters exceptions
90069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        switch(inwd[0]) {
91069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'K' :
92069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'G' :
93069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'P' : /* looking for KN, etc*/
94069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (inwd[1] == 'N') {
95069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd, 1, inwd.length - 1);
96069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } else {
97069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd);
98069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
99069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            break;
100069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'A': /* looking for AE */
101069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (inwd[1] == 'E') {
102069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd, 1, inwd.length - 1);
103069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } else {
104069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd);
105069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
106069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            break;
107069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'W' : /* looking for WR or WH */
108069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (inwd[1] == 'R') {   // WR -> R
109069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd, 1, inwd.length - 1);
110069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                break ;
111069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
112069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (inwd[1] == 'H') {
113069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd, 1, inwd.length - 1);
114069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.setCharAt(0, 'W'); // WH -> W
115069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } else {
116069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                local.append(inwd);
117069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
118069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            break;
119069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        case 'X' : /* initial X becomes S */
120069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            inwd[0] = 'S';
121069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            local.append(inwd);
122069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            break ;
123069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        default :
124069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            local.append(inwd);
125069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        } // now local has working string with initials fixed
126069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
127069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        int wdsz = local.length();
128069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        int n = 0 ;
129069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
130069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        while ((code.length() < this.getMaxCodeLen()) &&
131069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project               (n < wdsz) ) { // max code size of 4 works well
132069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            char symb = local.charAt(n) ;
133069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            // remove duplicate letters except C
134069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if ((symb != 'C') && (isPreviousChar( local, n, symb )) ) {
135069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                n++ ;
136069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } else { // not dup
137069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                switch(symb) {
138069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'A' : case 'E' : case 'I' : case 'O' : case 'U' :
139069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (n == 0) {
140069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append(symb);
141069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
142069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ; // only use vowel if leading char
143069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'B' :
144069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if ( isPreviousChar(local, n, 'M') &&
145069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                         isLastChar(wdsz, n) ) { // B is silent if word ends in MB
146069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
147069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
148069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append(symb);
149069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break;
150069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'C' : // lots of C special cases
151069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    /* discard if SCI, SCE or SCY */
152069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if ( isPreviousChar(local, n, 'S') &&
153069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                         !isLastChar(wdsz, n) &&
154069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                         (this.frontv.indexOf(local.charAt(n + 1)) >= 0) ) {
155069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
156069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
157069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (regionMatch(local, n, "CIA")) { // "CIA" -> X
158069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('X');
159069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
160069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
161069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (!isLastChar(wdsz, n) &&
162069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        (this.frontv.indexOf(local.charAt(n + 1)) >= 0)) {
163069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('S');
164069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break; // CI,CE,CY -> S
165069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
166069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isPreviousChar(local, n, 'S') &&
167069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        isNextChar(local, n, 'H') ) { // SCH->sk
168069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('K') ;
169069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break ;
170069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
171069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isNextChar(local, n, 'H')) { // detect CH
172069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        if ((n == 0) &&
173069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                            (wdsz >= 3) &&
174069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                            isVowel(local,2) ) { // CH consonant -> K consonant
175069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                            code.append('K');
176069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        } else {
177069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                            code.append('X'); // CHvowel -> X
178069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        }
179069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
180069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('K');
181069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
182069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
183069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'D' :
184069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (!isLastChar(wdsz, n + 1) &&
185069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        isNextChar(local, n, 'G') &&
186069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        (this.frontv.indexOf(local.charAt(n + 2)) >= 0)) { // DGE DGI DGY -> J
187069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('J'); n += 2 ;
188069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
189069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('T');
190069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
191069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
192069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'G' : // GH silent at end or before consonant
193069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isLastChar(wdsz, n + 1) &&
194069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        isNextChar(local, n, 'H')) {
195069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
196069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
197069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (!isLastChar(wdsz, n + 1) &&
198069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        isNextChar(local,n,'H') &&
199069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        !isVowel(local,n+2)) {
200069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
201069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
202069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if ((n > 0) &&
203069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        ( regionMatch(local, n, "GN") ||
204069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                          regionMatch(local, n, "GNED") ) ) {
205069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break; // silent G
206069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
207069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isPreviousChar(local, n, 'G')) {
208069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        hard = true ;
209069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
210069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        hard = false ;
211069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
212069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (!isLastChar(wdsz, n) &&
213069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        (this.frontv.indexOf(local.charAt(n + 1)) >= 0) &&
214069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        (!hard)) {
215069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('J');
216069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
217069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('K');
218069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
219069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
220069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'H':
221069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isLastChar(wdsz, n)) {
222069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break ; // terminal H
223069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
224069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if ((n > 0) &&
225069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        (this.varson.indexOf(local.charAt(n - 1)) >= 0)) {
226069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
227069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
228069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isVowel(local,n+1)) {
229069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('H'); // Hvowel
230069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
231069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break;
232069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'F':
233069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'J' :
234069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'L' :
235069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'M':
236069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'N' :
237069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'R' :
238069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append(symb);
239069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break;
240069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'K' :
241069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (n > 0) { // not initial
242069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        if (!isPreviousChar(local, n, 'C')) {
243069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                            code.append(symb);
244069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        }
245069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
246069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append(symb); // initial K
247069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
248069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
249069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'P' :
250069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (isNextChar(local,n,'H')) {
251069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        // PH -> F
252069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('F');
253069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
254069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append(symb);
255069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
256069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
257069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'Q' :
258069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append('K');
259069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break;
260069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'S' :
261069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (regionMatch(local,n,"SH") ||
262069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        regionMatch(local,n,"SIO") ||
263069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        regionMatch(local,n,"SIA")) {
264069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('X');
265069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
266069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('S');
267069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
268069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break;
269069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'T' :
270069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (regionMatch(local,n,"TIA") ||
271069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        regionMatch(local,n,"TIO")) {
272069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('X');
273069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
274069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
275069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (regionMatch(local,n,"TCH")) {
276069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        // Silent if in "TCH"
277069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        break;
278069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
279069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    // substitute numeral 0 for TH (resembles theta after all)
280069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (regionMatch(local,n,"TH")) {
281069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('0');
282069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    } else {
283069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append('T');
284069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
285069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
286069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'V' :
287069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append('F'); break ;
288069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'W' : case 'Y' : // silent if not followed by vowel
289069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    if (!isLastChar(wdsz,n) &&
290069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        isVowel(local,n+1)) {
291069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                        code.append(symb);
292069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    }
293069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
294069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'X' :
295069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append('K'); code.append('S');
296069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    break ;
297069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                case 'Z' :
298069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                    code.append('S'); break ;
299069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                } // end switch
300069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                n++ ;
301069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            } // end else from symb != 'C'
302069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            if (code.length() > this.getMaxCodeLen()) {
303069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project                code.setLength(this.getMaxCodeLen());
304069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            }
305069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
306069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return code.toString();
307069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
308069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
309069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean isVowel(StringBuffer string, int index) {
310069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return (this.vowels.indexOf(string.charAt(index)) >= 0);
311069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
312069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
313069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean isPreviousChar(StringBuffer string, int index, char c) {
314069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        boolean matches = false;
315069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if( index > 0 &&
316069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            index < string.length() ) {
317069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            matches = string.charAt(index - 1) == c;
318069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
319069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return matches;
320069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
321069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
322069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean isNextChar(StringBuffer string, int index, char c) {
323069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        boolean matches = false;
324069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if( index >= 0 &&
325069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            index < string.length() - 1 ) {
326069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            matches = string.charAt(index + 1) == c;
327069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
328069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return matches;
329069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
330069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
331069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean regionMatch(StringBuffer string, int index, String test) {
332069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        boolean matches = false;
333069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if( index >= 0 &&
334069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            (index + test.length() - 1) < string.length() ) {
335069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            String substring = string.substring( index, index + test.length());
336069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            matches = substring.equals( test );
337069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
338069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return matches;
339069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
340069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
341069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    private boolean isLastChar(int wdsz, int n) {
342069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return n + 1 == wdsz;
343069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
344069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
345069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
346069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
347069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Encodes an Object using the metaphone algorithm.  This method
348069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * is provided in order to satisfy the requirements of the
349069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Encoder interface, and will throw an EncoderException if the
350069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * supplied object is not of type java.lang.String.
351069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
352069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param pObject Object to encode
353069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return An object (or type java.lang.String) containing the
354069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *         metaphone code which corresponds to the String supplied.
355069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @throws EncoderException if the parameter supplied is not
356069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *                          of type java.lang.String
357069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
358069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public Object encode(Object pObject) throws EncoderException {
359069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        if (!(pObject instanceof java.lang.String)) {
360069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project            throw new EncoderException("Parameter supplied to Metaphone encode is not of type java.lang.String");
361069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        }
362069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return metaphone((String) pObject);
363069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
364069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
365069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
366069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Encodes a String using the Metaphone algorithm.
367069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
368069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param pString String object to encode
369069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return The metaphone code corresponding to the String supplied
370069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
371069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public String encode(String pString) {
372069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return metaphone(pString);
373069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
374069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
375069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
376069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Tests is the metaphones of two strings are identical.
377069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *
378069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param str1 First of two strings to compare
379069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param str2 Second of two strings to compare
380069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return true if the metaphones of these strings are identical,
381069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     *         false otherwise.
382069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
383069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public boolean isMetaphoneEqual(String str1, String str2) {
384069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project        return metaphone(str1).equals(metaphone(str2));
385069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    }
386069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
387069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
388069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Returns the maxCodeLen.
389069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @return int
390069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
391069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public int getMaxCodeLen() { return this.maxCodeLen; }
392069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
393069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    /**
394069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * Sets the maxCodeLen.
395069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     * @param maxCodeLen The maxCodeLen to set
396069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project     */
397069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project    public void setMaxCodeLen(int maxCodeLen) { this.maxCodeLen = maxCodeLen; }
398069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project
399069490a5ca2fd1988d29daf45d892f47ad665115The Android Open Source Project}
400