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