blob: 9e5e470ae6e52ab1405deb73ca0b4e154a847121 [file] [log] [blame]
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +09001/* //device/vmlibs-android/com.android.internal.telephony/PhoneNumberUtils.java
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** 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*/
The Android Open Source Project7790ef52009-03-03 19:30:40 -080017
18#include <string.h>
19
20namespace android {
21
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090022static int MIN_MATCH = 5;
The Android Open Source Project7790ef52009-03-03 19:30:40 -080023
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090024/** True if c is ISO-LATIN characters 0-9 */
25static bool isISODigit (char c)
The Android Open Source Project7790ef52009-03-03 19:30:40 -080026{
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090027 return c >= '0' && c <= '9';
The Android Open Source Project7790ef52009-03-03 19:30:40 -080028}
29
Daisuke Miyakawade430942009-06-26 11:17:34 +090030/** True if c is ISO-LATIN characters 0-9, *, # , + */
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090031static bool isNonSeparator(char c)
The Android Open Source Project7790ef52009-03-03 19:30:40 -080032{
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090033 return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
Daisuke Miyakawade430942009-06-26 11:17:34 +090034}
The Android Open Source Project7790ef52009-03-03 19:30:40 -080035
Daisuke Miyakawade430942009-06-26 11:17:34 +090036/**
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090037 * Phone numbers are stored in "lookup" form in the database
38 * as reversed strings to allow for caller ID lookup
Daisuke Miyakawade430942009-06-26 11:17:34 +090039 *
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090040 * This method takes a phone number and makes a valid SQL "LIKE"
41 * string that will match the lookup form
Daisuke Miyakawade430942009-06-26 11:17:34 +090042 *
43 */
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090044/** all of a up to len must be an international prefix or
45 * separators/non-dialing digits
46 */
47static bool matchIntlPrefix(const char* a, int len)
Daisuke Miyakawade430942009-06-26 11:17:34 +090048{
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090049 /* '([^0-9*#+]\+[^0-9*#+] | [^0-9*#+]0(0|11)[^0-9*#+] )$' */
50 /* 0 1 2 3 45 */
The Android Open Source Project7790ef52009-03-03 19:30:40 -080051
52 int state = 0;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090053 for (int i = 0 ; i < len ; i++) {
54 char c = a[i];
55
The Android Open Source Project7790ef52009-03-03 19:30:40 -080056 switch (state) {
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090057 case 0:
58 if (c == '+') state = 1;
59 else if (c == '0') state = 2;
60 else if (isNonSeparator(c)) return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -080061 break;
62
63 case 2:
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090064 if (c == '0') state = 3;
65 else if (c == '1') state = 4;
66 else if (isNonSeparator(c)) return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -080067 break;
68
69 case 4:
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090070 if (c == '1') state = 5;
71 else if (isNonSeparator(c)) return false;
72 break;
73
74 default:
75 if (isNonSeparator(c)) return false;
76 break;
77
78 }
79 }
80
81 return state == 1 || state == 3 || state == 5;
82}
83
84/** all of 'a' up to len must match non-US trunk prefix ('0') */
85static bool matchTrunkPrefix(const char* a, int len)
86{
87 bool found;
88
89 found = false;
90
91 for (int i = 0 ; i < len ; i++) {
92 char c = a[i];
93
94 if (c == '0' && !found) {
95 found = true;
96 } else if (isNonSeparator(c)) {
97 return false;
98 }
99 }
100
101 return found;
102}
103
104/** all of 'a' up to len must be a (+|00|011)country code)
105 * We're fast and loose with the country code. Any \d{1,3} matches */
106static bool matchIntlPrefixAndCC(const char* a, int len)
107{
108 /* [^0-9*#+]*(\+|0(0|11)\d\d?\d? [^0-9*#+] $ */
109 /* 0 1 2 3 45 6 7 8 */
110
111 int state = 0;
112 for (int i = 0 ; i < len ; i++ ) {
113 char c = a[i];
114
115 switch (state) {
116 case 0:
117 if (c == '+') state = 1;
118 else if (c == '0') state = 2;
119 else if (isNonSeparator(c)) return false;
120 break;
121
122 case 2:
123 if (c == '0') state = 3;
124 else if (c == '1') state = 4;
125 else if (isNonSeparator(c)) return false;
126 break;
127
128 case 4:
129 if (c == '1') state = 5;
130 else if (isNonSeparator(c)) return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800131 break;
132
133 case 1:
134 case 3:
135 case 5:
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900136 if (isISODigit(c)) state = 6;
137 else if (isNonSeparator(c)) return false;
138 break;
139
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800140 case 6:
141 case 7:
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900142 if (isISODigit(c)) state++;
143 else if (isNonSeparator(c)) return false;
144 break;
145
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800146 default:
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900147 if (isNonSeparator(c)) return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800148 }
149 }
150
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900151 return state == 6 || state == 7 || state == 8;
152}
153
154/** or -1 if both are negative */
155static int minPositive(int a, int b)
156{
157 if (a >= 0 && b >= 0) {
158 return (a < b) ? a : b;
159 } else if (a >= 0) { /* && b < 0 */
160 return a;
161 } else if (b >= 0) { /* && a < 0 */
162 return b;
163 } else { /* a < 0 && b < 0 */
164 return -1;
165 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800166}
167
168/**
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900169 * Return the offset into a of the first appearance of b, or -1 if there
170 * is no such character in a.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800171 */
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900172static int indexOf(const char *a, char b) {
173 char *ix = strchr(a, b);
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800174
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900175 if (ix == NULL)
176 return -1;
177 else
178 return ix - a;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800179}
180
181/**
182 * Compare phone numbers a and b, return true if they're identical
183 * enough for caller ID purposes.
184 *
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900185 * - Compares from right to left
186 * - requires MIN_MATCH (5) characters to match
187 * - handles common trunk prefixes and international prefixes
188 * (basically, everything except the Russian trunk prefix)
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800189 *
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900190 * Tolerates nulls
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800191 */
192bool phone_number_compare(const char* a, const char* b)
193{
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900194 int ia, ib;
195 int matched;
196
197 if (a == NULL || b == NULL) {
198 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800199 }
200
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900201 ia = strlen(a);
202 ib = strlen(b);
203 if (ia == 0 || ib == 0) {
204 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800205 }
206
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900207 // Compare from right to left
208 ia--;
209 ib--;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800210
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900211 matched = 0;
212
213 while (ia >= 0 && ib >=0) {
214 char ca, cb;
215 bool skipCmp = false;
216
217 ca = a[ia];
218
219 if (!isNonSeparator(ca)) {
220 ia--;
221 skipCmp = true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800222 }
223
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900224 cb = b[ib];
225
226 if (!isNonSeparator(cb)) {
227 ib--;
228 skipCmp = true;
229 }
230
231 if (!skipCmp) {
232 if (cb != ca) {
233 break;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800234 }
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900235 ia--; ib--; matched++;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800236 }
237 }
238
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900239 if (matched < MIN_MATCH) {
240 int aLen = strlen(a);
241
242 // if the input strings match, but their lengths < MIN_MATCH,
243 // treat them as equal.
244 if (aLen == (int)strlen(b) && aLen == matched) {
245 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800246 }
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900247 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800248 }
249
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900250 // At least one string has matched completely;
251 if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
252 return true;
253 }
254
255 /*
256 * Now, what remains must be one of the following for a
257 * match:
258 *
259 * - a '+' on one and a '00' or a '011' on the other
260 * - a '0' on one and a (+,00)<country code> on the other
261 * (for this, a '0' and a '00' prefix would have succeeded above)
262 */
263
264 if (matchIntlPrefix(a, ia + 1) && matchIntlPrefix(b, ib +1)) {
265 return true;
266 }
267
268 if (matchTrunkPrefix(a, ia + 1) && matchIntlPrefixAndCC(b, ib +1)) {
269 return true;
270 }
271
272 if (matchTrunkPrefix(b, ib + 1) && matchIntlPrefixAndCC(a, ia +1)) {
273 return true;
274 }
275
276 /*
277 * Last resort: if the number of unmatched characters on both sides is less than or equal
278 * to the length of the longest country code and only one number starts with a + accept
279 * the match. This is because some countries like France and Russia have an extra prefix
280 * digit that is used when dialing locally in country that does not show up when you dial
281 * the number using the country code. In France this prefix digit is used to determine
282 * which land line carrier to route the call over.
283 */
284 bool aPlusFirst = (*a == '+');
285 bool bPlusFirst = (*b == '+');
286 if (ia < 4 && ib < 4 && (aPlusFirst || bPlusFirst) && !(aPlusFirst && bPlusFirst)) {
287 return true;
288 }
289
290 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800291}
292
293} // namespace android