ContactLookupKey.java revision 5870f2dcc2ac7715b2c078a886ee346622e7887e
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
17package com.android.providers.contacts;
18
19import android.net.Uri;
20
21import 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 */
27public class ContactLookupKey {
28
29    public static class LookupKeySegment implements Comparable<LookupKeySegment> {
30        public int accountHashCode;
31        public boolean sourceIdLookup;
32        public String key;
33        public long contactId;
34
35        public int compareTo(LookupKeySegment another) {
36            if (contactId > another.contactId) {
37                return -1;
38            }
39            if (contactId < another.contactId) {
40                return 1;
41            }
42            return 0;
43        }
44    }
45
46    /**
47     * Returns a short hash code that functions as an additional precaution against the exceedingly
48     * improbable collision between sync IDs in different accounts.
49     */
50    public static int getAccountHashCode(String accountType, String accountName) {
51        if (accountType == null || accountName == null) {
52            return 0;
53        }
54
55        return (accountType.hashCode() ^ accountName.hashCode()) & 0xFFF;
56    }
57
58    public static void appendToLookupKey(StringBuffer lookupKey, String accountType,
59            String accountName, String sourceId, String displayName) {
60        if (lookupKey.length() != 0) {
61            lookupKey.append(".");
62        }
63
64        lookupKey.append(getAccountHashCode(accountType, accountName));
65        if (sourceId == null) {
66            lookupKey.append('n').append(NameNormalizer.normalize(displayName));
67        } else {
68            int pos = lookupKey.length();
69            lookupKey.append('i');
70            if (appendEscapedSourceId(lookupKey, sourceId)) {
71                lookupKey.setCharAt(pos, 'e');
72            }
73        }
74    }
75
76    private static boolean appendEscapedSourceId(StringBuffer sb, String sourceId) {
77        boolean escaped = false;
78        int start = 0;
79        while (true) {
80            int index = sourceId.indexOf('.', start);
81            if (index == -1) {
82                sb.append(sourceId, start, sourceId.length());
83                break;
84            }
85
86            escaped = true;
87            sb.append(sourceId, start, index);
88            sb.append("..");
89            start = index + 1;
90        }
91        return escaped;
92    }
93
94    public ArrayList<LookupKeySegment> parse(String lookupKey) {
95        ArrayList<LookupKeySegment> list = new ArrayList<LookupKeySegment>();
96
97        String string = Uri.decode(lookupKey);
98        int offset = 0;
99        int length = string.length();
100        int hashCode = 0;
101        boolean sourceIdLookup = false;
102        boolean escaped;
103        String key;
104
105        while (offset < length) {
106            char c = 0;
107
108            // Parse account hash code
109            hashCode = 0;
110            while (offset < length) {
111                c = string.charAt(offset++);
112                if (c < '0' || c > '9') {
113                    break;
114                }
115                hashCode = hashCode * 10 + (c - '0');
116            }
117
118            // Parse segment type
119            if (c == 'n') {
120                sourceIdLookup = false;
121                escaped = false;
122            } else if (c == 'i') {
123                sourceIdLookup = true;
124                escaped = false;
125            } else if (c == 'e') {
126                sourceIdLookup = true;
127                escaped = true;
128            } else {
129                throw new IllegalArgumentException("Invalid lookup id: " + lookupKey);
130            }
131
132            // Parse the source ID or normalized display name
133            if (escaped) {
134                StringBuffer sb = new StringBuffer();
135                while (offset < length) {
136                    c = string.charAt(offset++);
137
138                    if (c == '.') {
139                        if (offset == length) {
140                            throw new IllegalArgumentException("Invalid lookup id: " + lookupKey);
141                        }
142                        c = string.charAt(offset);
143
144                        if (c == '.') {
145                            sb.append('.');
146                            offset++;
147                        } else {
148                            break;
149                        }
150                    } else {
151                        sb.append(c);
152                    }
153                }
154                key = sb.toString();
155            } else {
156                int start = offset;
157                while (offset < length) {
158                    c = string.charAt(offset++);
159
160                    if (c == '.') {
161                        break;
162                    }
163                }
164                if (offset == length) {
165                    key = string.substring(start);
166                } else {
167                    key = string.substring(start, offset - 1);
168                }
169            }
170
171            LookupKeySegment segment = new LookupKeySegment();
172            segment.accountHashCode = hashCode;
173            segment.key = key;
174            segment.sourceIdLookup = sourceIdLookup;
175            segment.contactId = -1;
176            list.add(segment);
177        }
178
179        return list;
180    }
181}
182