Skip to content

Commit 66555de

Browse files
committed
Applying my second patch from CODEC-60; adding the Caverphone 2.0 algorithm
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/codec/trunk@634911 13f79535-47bb-0310-9956-ffa450edef68
1 parent f633664 commit 66555de

2 files changed

Lines changed: 245 additions & 0 deletions

File tree

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
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+
23+
/**
24+
* Encodes a string into a caverphone value.
25+
*
26+
* This is an algorithm created the Caversham Project at the University of Otago.
27+
* It implements the Caverphone 2.0 algorithm:
28+
*
29+
*
30+
* @author Apache Software Foundation
31+
* @version $Id: Caverphone.java 582447 2007-10-06 04:14:43Z bayard $
32+
* @see <a href="http://en.wikipedia.org/wiki/Caverphone">Wikipedia - Caverphone</a>
33+
* @see <a href="http://caversham.otago.ac.nz/files/working/ctp150804.pdf">Caverphone 2.0 specification</a>
34+
*/
35+
public class Caverphone implements StringEncoder {
36+
37+
/**
38+
* Creates an instance of the Caverphone encoder
39+
*/
40+
public Caverphone() {
41+
super();
42+
}
43+
44+
/**
45+
* Find the caverphone value of a String.
46+
*
47+
* @param txt String to find the caverphone code for
48+
* @return A caverphone code corresponding to the String supplied
49+
*/
50+
public String caverphone(String txt) {
51+
// NOTE: Version 1.0 of Caverphone is easily derivable from this code
52+
// by commenting out the 2.0 lines and adding in the 1.0 lines
53+
54+
if( txt == null || txt.length() == 0 ) {
55+
return "1111111111";
56+
}
57+
58+
// 1. Convert to lowercase
59+
txt = txt.toLowerCase();
60+
61+
// 2. Remove anything not A-Z
62+
txt = txt.replaceAll("[^a-z]", "");
63+
64+
// 2.5. Remove final e
65+
txt = txt.replace("e$", ""); // 2.0 only
66+
67+
// 3. Handle various start options
68+
txt = txt.replace("^cough", "cou2f");
69+
txt = txt.replace("^rough", "rou2f");
70+
txt = txt.replace("^tough", "tou2f");
71+
txt = txt.replace("^enough", "enou2f"); // 2.0 only
72+
txt = txt.replace("^trough", "trou2f"); // 2.0 only - note the spec says ^enough here again, c+p error I assume
73+
txt = txt.replace("^gn", "2n");
74+
txt = txt.replace("^mb", "m2");
75+
76+
// 4. Handle replacements
77+
txt = txt.replaceAll("cq", "2q");
78+
txt = txt.replaceAll("ci", "si");
79+
txt = txt.replaceAll("ce", "se");
80+
txt = txt.replaceAll("cy", "sy");
81+
txt = txt.replaceAll("tch", "2ch");
82+
txt = txt.replaceAll("c", "k");
83+
txt = txt.replaceAll("q", "k");
84+
txt = txt.replaceAll("x", "k");
85+
txt = txt.replaceAll("v", "f");
86+
txt = txt.replaceAll("dg", "2g");
87+
txt = txt.replaceAll("tio", "sio");
88+
txt = txt.replaceAll("tia", "sia");
89+
txt = txt.replaceAll("d", "t");
90+
txt = txt.replaceAll("ph", "fh");
91+
txt = txt.replaceAll("b", "p");
92+
txt = txt.replaceAll("sh", "s2");
93+
txt = txt.replaceAll("z", "s");
94+
txt = txt.replace("^[aeiou]", "A");
95+
txt = txt.replaceAll("[aeiou]", "3");
96+
txt = txt.replaceAll("j", "y"); // 2.0 only
97+
txt = txt.replaceAll("^y3", "Y3"); // 2.0 only
98+
txt = txt.replaceAll("^y", "A"); // 2.0 only
99+
txt = txt.replaceAll("y", "3"); // 2.0 only
100+
txt = txt.replaceAll("3gh3", "3kh3");
101+
txt = txt.replaceAll("gh", "22");
102+
txt = txt.replaceAll("g", "k");
103+
txt = txt.replaceAll("s+", "S");
104+
txt = txt.replaceAll("t+", "T");
105+
txt = txt.replaceAll("p+", "P");
106+
txt = txt.replaceAll("k+", "K");
107+
txt = txt.replaceAll("f+", "F");
108+
txt = txt.replaceAll("m+", "M");
109+
txt = txt.replaceAll("n+", "N");
110+
txt = txt.replaceAll("w3", "W3");
111+
//txt = txt.replaceAll("wy", "Wy"); // 1.0 only
112+
txt = txt.replaceAll("wh3", "Wh3");
113+
txt = txt.replaceAll("w$", "3"); // 2.0 only
114+
//txt = txt.replaceAll("why", "Why"); // 1.0 only
115+
txt = txt.replaceAll("w", "2");
116+
txt = txt.replace("^h", "A");
117+
txt = txt.replaceAll("h", "2");
118+
txt = txt.replaceAll("r3", "R3");
119+
txt = txt.replaceAll("r$", "3"); // 2.0 only
120+
//txt = txt.replaceAll("ry", "Ry"); // 1.0 only
121+
txt = txt.replaceAll("r", "2");
122+
txt = txt.replaceAll("l3", "L3");
123+
txt = txt.replaceAll("l$", "3"); // 2.0 only
124+
//txt = txt.replaceAll("ly", "Ly"); // 1.0 only
125+
txt = txt.replaceAll("l", "2");
126+
//txt = txt.replaceAll("j", "y"); // 1.0 only
127+
//txt = txt.replaceAll("y3", "Y3"); // 1.0 only
128+
//txt = txt.replaceAll("y", "2"); // 1.0 only
129+
130+
// 5. Handle removals
131+
txt = txt.replaceAll("2", "");
132+
txt = txt.replaceAll("3$", "A"); // 2.0 only
133+
txt = txt.replaceAll("3", "");
134+
135+
// 6. put ten 1s on the end
136+
txt = txt + "111111" + "1111"; // 1.0 only has 6 1s
137+
138+
// 7. take the first six characters as the code
139+
return txt.substring(0, 10); // 1.0 truncates to 6
140+
}
141+
142+
/**
143+
* Encodes an Object using the caverphone algorithm. This method
144+
* is provided in order to satisfy the requirements of the
145+
* Encoder interface, and will throw an EncoderException if the
146+
* supplied object is not of type java.lang.String.
147+
*
148+
* @param pObject Object to encode
149+
* @return An object (or type java.lang.String) containing the
150+
* caverphone code which corresponds to the String supplied.
151+
* @throws EncoderException if the parameter supplied is not
152+
* of type java.lang.String
153+
*/
154+
public Object encode(Object pObject) throws EncoderException {
155+
if (!(pObject instanceof java.lang.String)) {
156+
throw new EncoderException("Parameter supplied to Caverphone encode is not of type java.lang.String");
157+
}
158+
return caverphone((String) pObject);
159+
}
160+
161+
/**
162+
* Encodes a String using the Caverphone algorithm.
163+
*
164+
* @param pString String object to encode
165+
* @return The caverphone code corresponding to the String supplied
166+
*/
167+
public String encode(String pString) {
168+
return caverphone(pString);
169+
}
170+
171+
/**
172+
* Tests if the caverphones of two strings are identical.
173+
*
174+
* @param str1 First of two strings to compare
175+
* @param str2 Second of two strings to compare
176+
* @return <code>true</code> if the caverphones of these strings are identical,
177+
* <code>false</code> otherwise.
178+
*/
179+
public boolean isCaverphoneEqual(String str1, String str2) {
180+
return caverphone(str1).equals(caverphone(str2));
181+
}
182+
183+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.codec.language;
19+
20+
import junit.framework.Test;
21+
import junit.framework.TestSuite;
22+
23+
import org.apache.commons.codec.StringEncoder;
24+
import org.apache.commons.codec.StringEncoderAbstractTest;
25+
26+
/**
27+
* @author Apache Software Foundation
28+
* @version $Id: CaverphoneTest.java 588074 2007-10-24 23:04:56Z ggregory $
29+
*/
30+
public class CaverphoneTest extends StringEncoderAbstractTest {
31+
32+
public static Test suite() {
33+
return new TestSuite(CaverphoneTest.class);
34+
}
35+
36+
public CaverphoneTest(String name) {
37+
super(name);
38+
}
39+
40+
protected StringEncoder makeEncoder() {
41+
return new Caverphone();
42+
}
43+
44+
public void testWikipediaExamples() {
45+
Caverphone caverphone = new Caverphone();
46+
String[][] data = {
47+
{"Stevenson", "STFNSN1111"},
48+
{"Peter", "PTA1111111"},
49+
{"ready", "RTA1111111"},
50+
{"social", "SSA1111111"},
51+
{"able", "APA1111111"},
52+
{"Tedder", "TTA1111111"},
53+
{"Karleen", "KLN1111111"},
54+
{"Dyun", "TN11111111"},
55+
};
56+
57+
for(int i=0; i<data.length; i++) {
58+
assertEquals( data[i][1], caverphone.caverphone(data[i][0]) );
59+
}
60+
}
61+
62+
}

0 commit comments

Comments
 (0)