1f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov/*
2f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * Copyright (C) 2009 The Android Open Source Project
3f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov *
4f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
5f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * you may not use this file except in compliance with the License.
6f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * You may obtain a copy of the License at
7f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov *
8f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
9f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov *
10f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
11f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
12f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * See the License for the specific language governing permissions and
14f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * limitations under the License
15f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov */
16f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
17f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovpackage com.android.providers.contacts;
18f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
1938210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport android.provider.ContactsContract.FullNameStyle;
2038210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki
21b38ed2c5ffeb20efc677b4a9229db4a00603aa8dDmitri Plotnikovimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
2292ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikovimport com.android.providers.contacts.SearchIndexManager.IndexBuilder;
23f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
24c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikovimport java.util.Arrays;
25c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikovimport java.util.Comparator;
26d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikovimport java.util.Iterator;
27c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
28f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov/**
29f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov * Given a full name, constructs all possible variants of the name.
30f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov */
31f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikovpublic abstract class NameLookupBuilder {
32f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
33c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov    private static final int MAX_NAME_TOKENS = 4;
34c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
35f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private final NameSplitter mSplitter;
36c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov    private String[][] mNicknameClusters = new String[MAX_NAME_TOKENS][];
37339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov    private StringBuilder mStringBuilder = new StringBuilder();
38f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private String[] mNames = new String[NameSplitter.MAX_TOKENS];
39f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
4056f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee    private static final int[] KOREAN_JAUM_CONVERT_MAP = {
41f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        // JAUM in Hangul Compatibility Jamo area 0x3131 ~ 0x314E to
42f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        // in Hangul Jamo area 0x1100 ~ 0x1112
43f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1100, // 0x3131 HANGUL LETTER KIYEOK
44f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1101, // 0x3132 HANGUL LETTER SSANGKIYEOK
45f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x3133 HANGUL LETTER KIYEOKSIOS (Ignored)
46f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1102, // 0x3134 HANGUL LETTER NIEUN
47f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x3135 HANGUL LETTER NIEUNCIEUC (Ignored)
48f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x3136 HANGUL LETTER NIEUNHIEUH (Ignored)
49f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1103, // 0x3137 HANGUL LETTER TIKEUT
50f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1104, // 0x3138 HANGUL LETTER SSANGTIKEUT
51f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1105, // 0x3139 HANGUL LETTER RIEUL
52f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313A HANGUL LETTER RIEULKIYEOK (Ignored)
53f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313B HANGUL LETTER RIEULMIEUM (Ignored)
54f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313C HANGUL LETTER RIEULPIEUP (Ignored)
55f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313D HANGUL LETTER RIEULSIOS (Ignored)
56f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313E HANGUL LETTER RIEULTHIEUTH (Ignored)
57f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x313F HANGUL LETTER RIEULPHIEUPH (Ignored)
58f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x3140 HANGUL LETTER RIEULHIEUH (Ignored)
59f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1106, // 0x3141 HANGUL LETTER MIEUM
60f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1107, // 0x3142 HANGUL LETTER PIEUP
61f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1108, // 0x3143 HANGUL LETTER SSANGPIEUP
62f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x00,   // 0x3144 HANGUL LETTER PIEUPSIOS (Ignored)
63f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1109, // 0x3145 HANGUL LETTER SIOS
64f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110A, // 0x3146 HANGUL LETTER SSANGSIOS
65f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110B, // 0x3147 HANGUL LETTER IEUNG
66f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110C, // 0x3148 HANGUL LETTER CIEUC
67f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110D, // 0x3149 HANGUL LETTER SSANGCIEUC
68f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110E, // 0x314A HANGUL LETTER CHIEUCH
69f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x110F, // 0x314B HANGUL LETTER KHIEUKH
70f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1110, // 0x314C HANGUL LETTER THIEUTH
71f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1111, // 0x314D HANGUL LETTER PHIEUPH
72f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        0x1112  // 0x314E HANGUL LETTER HIEUH
73f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee    };
74f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee
75f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    public NameLookupBuilder(NameSplitter splitter) {
76f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        mSplitter = splitter;
77f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
78f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
79f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
80f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a name lookup record with the supplied column values.
81f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
82f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected abstract void insertNameLookup(long rawContactId, long dataId, int lookupType,
83f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String string);
84f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
85f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
86f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Returns common nickname cluster IDs for a given name. For example, it
87f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * will return the same value for "Robert", "Bob" and "Rob". Some names belong to multiple
88f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * clusters, e.g. Leo could be Leonard or Leopold.
89f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *
90f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * May return null.
91f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *
92f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * @param normalizedName A normalized first name, see {@link NameNormalizer#normalize}.
93f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
94f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected abstract String[] getCommonNicknameClusters(String normalizedName);
95f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
96f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
97f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts name lookup records for the given structured name.
98f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
99d806946b6561dca3f34ded156c6ee89a5113996eDmitri Plotnikov    public void insertNameLookup(long rawContactId, long dataId, String name, int fullNameStyle) {
100f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        int tokenCount = mSplitter.tokenize(mNames, name);
101f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (tokenCount == 0) {
102f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
103f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
104f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
105f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        for (int i = 0; i < tokenCount; i++) {
106c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            mNames[i] = normalizeName(mNames[i]);
107c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov        }
108c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
109c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov        boolean tooManyTokens = tokenCount > MAX_NAME_TOKENS;
110c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov        if (tooManyTokens) {
111c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            insertNameVariant(rawContactId, dataId, tokenCount, NameLookupType.NAME_EXACT, true);
112c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
113c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            // Favor longer parts of the name
114c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            Arrays.sort(mNames, 0, tokenCount, new Comparator<String>() {
115c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
116c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov                public int compare(String s1, String s2) {
117c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov                    return s2.length() - s1.length();
118c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov                }
119c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            });
120c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov
121339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            // Insert a collation key for each extra word - useful for contact filtering
122339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            // and suggestions
123339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            String firstToken = mNames[0];
124339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            for (int i = MAX_NAME_TOKENS; i < tokenCount; i++) {
125339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov                mNames[0] = mNames[i];
126339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov                insertCollationKey(rawContactId, dataId, MAX_NAME_TOKENS);
127339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            }
128339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            mNames[0] = firstToken;
129339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov
130c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov            tokenCount = MAX_NAME_TOKENS;
131f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
132f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
133f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        // Phase I: insert all variants not involving nickname clusters
134f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        for (int i = 0; i < tokenCount; i++) {
135f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            mNicknameClusters[i] = getCommonNicknameClusters(mNames[i]);
136f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
137f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
138c91ffc49403818f280c382f01bbfd5cd9a9bca6bDmitri Plotnikov        insertNameVariants(rawContactId, dataId, 0, tokenCount, !tooManyTokens, true);
139f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        insertNicknamePermutations(rawContactId, dataId, 0, tokenCount);
14092ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov    }
14192ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov
14292ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov    public void appendToSearchIndex(IndexBuilder builder, String name, int fullNameStyle) {
14392ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        int tokenCount = mSplitter.tokenize(mNames, name);
14492ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        if (tokenCount == 0) {
14592ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            return;
14692ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        }
14792ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov
14892ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        for (int i = 0; i < tokenCount; i++) {
149155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov            builder.appendName(mNames[i]);
15092ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        }
15192ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov
15292ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        appendNameShorthandLookup(builder, name, fullNameStyle);
15392ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov        appendNameLookupForLocaleBasedName(builder, name, fullNameStyle);
154f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee    }
155f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee
15656f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee    /**
15756f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee     * Insert more name indexes according to locale specifies.
15856f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee     */
15992ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov    private void appendNameLookupForLocaleBasedName(IndexBuilder builder,
16056f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            String fullName, int fullNameStyle) {
161f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        if (fullNameStyle == FullNameStyle.KOREAN) {
16256f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            NameSplitter.Name name = new NameSplitter.Name();
16356f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            mSplitter.split(name, fullName, fullNameStyle);
16456f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            if (name.givenNames != null) {
165155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                builder.appendName(name.givenNames);
16692ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov                appendKoreanNameConsonantsLookup(builder, name.givenNames);
16756f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            }
16892ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov            appendKoreanNameConsonantsLookup(builder, fullName);
169f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        }
170f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee    }
171f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee
172f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee    /**
173f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee     * Inserts Korean lead consonants records of name for the given structured name.
174f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee     */
17592ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100Dmitri Plotnikov    private void appendKoreanNameConsonantsLookup(IndexBuilder builder, String name) {
176f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        int position = 0;
177f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        int consonantLength = 0;
178f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        int character;
179f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee
180f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        final int stringLength = name.length();
181f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        mStringBuilder.setLength(0);
182f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        do {
183f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            character = name.codePointAt(position++);
18456f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee            if ((character == 0x20) || (character == 0x2c) || (character == 0x2E)) {
18556f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee                // Skip spaces, commas and periods.
186f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                continue;
187f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            }
188f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            // Exclude characters that are not in Korean leading consonants area
189f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            // and Korean characters area.
190f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            if ((character < 0x1100) || (character > 0x1112 && character < 0x3131) ||
191f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    (character > 0x314E && character < 0xAC00) ||
192f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    (character > 0xD7A3)) {
193f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                break;
194f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            }
195f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            // Decompose and take a only lead-consonant for composed Korean characters.
196f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            if (character >= 0xAC00) {
197f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                // Lead consonant = "Lead consonant base" +
198f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                //      (character - "Korean Character base") /
199f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                //          ("Lead consonant count" * "middle Vowel count")
200f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                character = 0x1100 + (character - 0xAC00) / 588;
201f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            } else if (character >= 0x3131) {
202f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                // Hangul Compatibility Jamo area 0x3131 ~ 0x314E :
203f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                // Convert to Hangul Jamo area 0x1100 ~ 0x1112
20456f2638b49e6bca97f6aa7b0768a8f1fe6e7b72eSang-il, Lee                if (character - 0x3131 >= KOREAN_JAUM_CONVERT_MAP.length) {
205f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    // This is not lead-consonant
206f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    break;
207f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                }
208f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                character = KOREAN_JAUM_CONVERT_MAP[character - 0x3131];
209f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                if (character == 0) {
210f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    // This is not lead-consonant
211f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                    break;
212f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee                }
213f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            }
214f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            mStringBuilder.appendCodePoint(character);
215f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee            consonantLength++;
216f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        } while (position < stringLength);
217f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee
218f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        // At least, insert consonants when Korean characters are two or more.
219f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        // Only one character cases are covered by NAME_COLLATION_KEY
220f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        if (consonantLength > 1) {
221155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov            builder.appendName(mStringBuilder.toString());
222f84478382761d74b9fb98c4189de66002c04cef8Sang-il, Lee        }
223f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
224f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
225f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    protected String normalizeName(String name) {
226f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        return NameNormalizer.normalize(name);
227f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
228f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
229f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
230f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts all name variants based on permutations of tokens between
231f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * fromIndex and toIndex
232f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *
233f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * @param initiallyExact true if the name without permutations is the exact
234f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *            original name
235f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * @param buildCollationKey true if a collation key makes sense for these
236f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *            permutations (false if at least one of the tokens is a
237f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     *            nickname cluster key)
238f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
239f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private void insertNameVariants(long rawContactId, long dataId, int fromIndex, int toIndex,
240f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            boolean initiallyExact, boolean buildCollationKey) {
241f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (fromIndex == toIndex) {
242f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameVariant(rawContactId, dataId, toIndex,
243f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    initiallyExact ? NameLookupType.NAME_EXACT : NameLookupType.NAME_VARIANT,
244f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    buildCollationKey);
245f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            return;
246f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
247f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
248f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        // Swap the first token with each other token (including itself, which is a no-op)
249f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        // and recursively insert all permutations for the remaining tokens
250f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        String firstToken = mNames[fromIndex];
251f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        for (int i = fromIndex; i < toIndex; i++) {
252f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            mNames[fromIndex] = mNames[i];
253f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            mNames[i] = firstToken;
254f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
255f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            insertNameVariants(rawContactId, dataId, fromIndex + 1, toIndex,
256f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    initiallyExact && i == fromIndex, buildCollationKey);
257f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
258f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            mNames[i] = mNames[fromIndex];
259f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            mNames[fromIndex] = firstToken;
260f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
261f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
262f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
263f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
264f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * Inserts a single name variant and optionally its collation key counterpart.
265f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
266f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private void insertNameVariant(long rawContactId, long dataId, int tokenCount,
267f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int lookupType, boolean buildCollationKey) {
268339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        mStringBuilder.setLength(0);
269f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
270f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        for (int i = 0; i < tokenCount; i++) {
271f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (i != 0) {
272339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov                mStringBuilder.append('.');
273f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
274339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            mStringBuilder.append(mNames[i]);
275f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
276f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
277339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        insertNameLookup(rawContactId, dataId, lookupType, mStringBuilder.toString());
278f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
279f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        if (buildCollationKey) {
280339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            insertCollationKey(rawContactId, dataId, tokenCount);
281339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        }
282339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov    }
283f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
284339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov    /**
285339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov     * Inserts a collation key for the current contents of {@link #mNames}.
286339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov     */
287339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov    private void insertCollationKey(long rawContactId, long dataId, int tokenCount) {
288339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        mStringBuilder.setLength(0);
289f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
290339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        for (int i = 0; i < tokenCount; i++) {
291339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov            mStringBuilder.append(mNames[i]);
292f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
293339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov
294339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov        insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
295339603c831c0f0440312c40bee7008bef93dac95Dmitri Plotnikov                mStringBuilder.toString());
296f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
297f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
298f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    /**
299f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * For all tokens that correspond to nickname clusters, substitutes each cluster key
300f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     * and inserts all permutations with that key.
301f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov     */
302f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    private void insertNicknamePermutations(long rawContactId, long dataId, int fromIndex,
303f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            int tokenCount) {
304f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        for (int i = fromIndex; i < tokenCount; i++) {
305f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            String[] clusters = mNicknameClusters[i];
306f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            if (clusters != null) {
307f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                String token = mNames[i];
308f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                for (int j = 0; j < clusters.length; j++) {
309f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    mNames[i] = clusters[j];
310f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
311f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    // Insert all permutations with this nickname cluster
312f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    insertNameVariants(rawContactId, dataId, 0, tokenCount, false, false);
313f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov
314f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    // Repeat recursively for other nickname clusters
315f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                    insertNicknamePermutations(rawContactId, dataId, i + 1, tokenCount);
316f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                }
317f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov                mNames[i] = token;
318f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov            }
319f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov        }
320f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov    }
321d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov
3220f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner    /**
3230f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner     * Insert more name indexes according to locale specifies for those locales
3240f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner     * for which we have alternative shorthand name methods (eg, Pinyin for
3250f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner     * Chinese, Romaji for Japanese).
3260f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner     */
3270f4b7a9bfe4b2079a7c5bb22b4114b5672639b05Jay Shrauner    public void appendNameShorthandLookup(IndexBuilder builder, String name, int fullNameStyle) {
328cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao        Iterator<String> it =
329a6a9fa802d1b56c206c670ca1d313bc64effcb5dJay Shrauner                ContactLocaleUtils.getInstance().getNameLookupKeys(name, fullNameStyle);
33004b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov        if (it != null) {
33104b7ce026c73077d9d982742bc662ea4b3ac74e7Dmitri Plotnikov            while (it.hasNext()) {
332155accbcb95fc13b984cf0ea8e5498a9c619cbf5Dmitri Plotnikov                builder.appendName(it.next());
333d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov            }
334d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov        }
335d0569511c4b9eb961d5a73be16edb9767fa9c2ebDmitri Plotnikov    }
336f23764675b35b5262a39c79aad8e9842460274b2Dmitri Plotnikov}
337