• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.providers.contacts;
18 
19 import android.net.Uri;
20 
21 import java.util.ArrayList;
22 
23 /**
24  * Contacts lookup key. Used for generation and parsing of contact lookup keys as well
25  * as doing the actual lookup.
26  */
27 public class ContactLookupKey {
28 
29     public static final int LOOKUP_TYPE_SOURCE_ID = 0;
30     public static final int LOOKUP_TYPE_DISPLAY_NAME = 1;
31     public static final int LOOKUP_TYPE_RAW_CONTACT_ID = 2;
32 
33     public static class LookupKeySegment implements Comparable<LookupKeySegment> {
34         public int accountHashCode;
35         public int lookupType;
36         public String rawContactId;
37         public String key;
38         public long contactId;
39 
compareTo(LookupKeySegment another)40         public int compareTo(LookupKeySegment another) {
41             if (contactId > another.contactId) {
42                 return -1;
43             }
44             if (contactId < another.contactId) {
45                 return 1;
46             }
47             return 0;
48         }
49     }
50 
51     /**
52      * Returns a short hash code that functions as an additional precaution against the exceedingly
53      * improbable collision between sync IDs in different accounts.
54      */
getAccountHashCode(String accountType, String accountName)55     public static int getAccountHashCode(String accountType, String accountName) {
56         if (accountType == null || accountName == null) {
57             return 0;
58         }
59 
60         return (accountType.hashCode() ^ accountName.hashCode()) & 0xFFF;
61     }
62 
appendToLookupKey(StringBuilder lookupKey, String accountType, String accountName, long rawContactId, String sourceId, String displayName)63     public static void appendToLookupKey(StringBuilder lookupKey, String accountType,
64             String accountName, long rawContactId, String sourceId, String displayName) {
65         if (displayName == null) {
66             displayName = "";
67         }
68 
69         if (lookupKey.length() != 0) {
70             lookupKey.append(".");
71         }
72 
73         lookupKey.append(getAccountHashCode(accountType, accountName));
74         if (sourceId == null) {
75             lookupKey.append('r').append(rawContactId).append('-').append(
76                     NameNormalizer.normalize(displayName));
77         } else {
78             int pos = lookupKey.length();
79             lookupKey.append('i');
80             if (appendEscapedSourceId(lookupKey, sourceId)) {
81                 lookupKey.setCharAt(pos, 'e');
82             }
83         }
84     }
85 
appendEscapedSourceId(StringBuilder sb, String sourceId)86     private static boolean appendEscapedSourceId(StringBuilder sb, String sourceId) {
87         boolean escaped = false;
88         int start = 0;
89         while (true) {
90             int index = sourceId.indexOf('.', start);
91             if (index == -1) {
92                 sb.append(sourceId, start, sourceId.length());
93                 break;
94             }
95 
96             escaped = true;
97             sb.append(sourceId, start, index);
98             sb.append("..");
99             start = index + 1;
100         }
101         return escaped;
102     }
103 
parse(String lookupKey)104     public ArrayList<LookupKeySegment> parse(String lookupKey) {
105         ArrayList<LookupKeySegment> list = new ArrayList<LookupKeySegment>();
106 
107         String string = Uri.decode(lookupKey);
108         int offset = 0;
109         int length = string.length();
110         int hashCode = 0;
111         int lookupType = -1;
112         boolean escaped = false;
113         String rawContactId = null;
114         String key;
115 
116         while (offset < length) {
117             char c = 0;
118 
119             // Parse account hash code
120             hashCode = 0;
121             while (offset < length) {
122                 c = string.charAt(offset++);
123                 if (c < '0' || c > '9') {
124                     break;
125                 }
126                 hashCode = hashCode * 10 + (c - '0');
127             }
128 
129             // Parse segment type
130             if (c == 'i') {
131                 lookupType = LOOKUP_TYPE_SOURCE_ID;
132                 escaped = false;
133             } else if (c == 'e') {
134                 lookupType = LOOKUP_TYPE_SOURCE_ID;
135                 escaped = true;
136             } else if (c == 'n') {
137                 lookupType = LOOKUP_TYPE_DISPLAY_NAME;
138             } else if (c == 'r') {
139                 lookupType = LOOKUP_TYPE_RAW_CONTACT_ID;
140             } else {
141                 throw new IllegalArgumentException("Invalid lookup id: " + lookupKey);
142             }
143 
144             // Parse the source ID or normalized display name
145             switch (lookupType) {
146                 case LOOKUP_TYPE_SOURCE_ID: {
147                     if (escaped) {
148                         StringBuffer sb = new StringBuffer();
149                         while (offset < length) {
150                             c = string.charAt(offset++);
151 
152                             if (c == '.') {
153                                 if (offset == length) {
154                                     throw new IllegalArgumentException("Invalid lookup id: " +
155                                             lookupKey);
156                                 }
157                                 c = string.charAt(offset);
158 
159                                 if (c == '.') {
160                                     sb.append('.');
161                                     offset++;
162                                 } else {
163                                     break;
164                                 }
165                             } else {
166                                 sb.append(c);
167                             }
168                         }
169                         key = sb.toString();
170                     } else {
171                         int start = offset;
172                         while (offset < length) {
173                             c = string.charAt(offset++);
174                             if (c == '.') {
175                                 break;
176                             }
177                         }
178                         if (offset == length) {
179                             key = string.substring(start);
180                         } else {
181                             key = string.substring(start, offset - 1);
182                         }
183                     }
184                     break;
185                 }
186                 case LOOKUP_TYPE_DISPLAY_NAME: {
187                     int start = offset;
188                     while (offset < length) {
189                         c = string.charAt(offset++);
190                         if (c == '.') {
191                             break;
192                         }
193                     }
194                     if (offset == length) {
195                         key = string.substring(start);
196                     } else {
197                         key = string.substring(start, offset - 1);
198                     }
199                     break;
200                 }
201                 case LOOKUP_TYPE_RAW_CONTACT_ID: {
202                     int dash = -1;
203                     int start = offset;
204                     while (offset < length) {
205                         c = string.charAt(offset);
206                         if (c == '-' && dash == -1) {
207                             dash = offset;
208                         }
209                         offset++;
210                         if (c == '.') {
211                             break;
212                         }
213                     }
214                     if (dash != -1) {
215                         rawContactId = string.substring(start, dash);
216                         start = dash + 1;
217                     }
218                     if (offset == length) {
219                         key = string.substring(start);
220                     } else {
221                         key = string.substring(start, offset - 1);
222                     }
223                     break;
224                 }
225                 default:
226                     // Will never happen
227                     throw new IllegalStateException();
228             }
229 
230             LookupKeySegment segment = new LookupKeySegment();
231             segment.accountHashCode = hashCode;
232             segment.lookupType = lookupType;
233             segment.rawContactId = rawContactId;
234             segment.key = key;
235             segment.contactId = -1;
236             list.add(segment);
237         }
238 
239         return list;
240     }
241 }
242