BinaryDictionaryFileDumper.java revision 7b1f74bb9ddae952f4da6c8d9bbb0057984b0988
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.inputmethod.latin; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.content.res.AssetFileDescriptor; 22import android.content.res.Resources; 23import android.database.Cursor; 24import android.net.Uri; 25import android.text.TextUtils; 26import android.util.Log; 27 28import java.io.FileInputStream; 29import java.io.FileNotFoundException; 30import java.io.FileOutputStream; 31import java.io.IOException; 32import java.io.InputStream; 33import java.util.ArrayList; 34import java.util.Collections; 35import java.util.List; 36import java.util.Locale; 37 38/** 39 * Group class for static methods to help with creation and getting of the binary dictionary 40 * file from the dictionary provider 41 */ 42public class BinaryDictionaryFileDumper { 43 private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName(); 44 45 /** 46 * The size of the temporary buffer to copy files. 47 */ 48 static final int FILE_READ_BUFFER_SIZE = 1024; 49 50 private static final String DICTIONARY_PROJECTION[] = { "id" }; 51 52 // Prevents this class to be accidentally instantiated. 53 private BinaryDictionaryFileDumper() { 54 } 55 56 /** 57 * Return for a given locale or dictionary id the provider URI to get the dictionary. 58 */ 59 private static Uri getProviderUri(String path) { 60 return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) 61 .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( 62 path).build(); 63 } 64 65 /** 66 * Queries a content provider for the list of word lists for a specific locale 67 * available to copy into Latin IME. 68 */ 69 private static List<String> getWordListIds(final Locale locale, final Context context) { 70 final ContentResolver resolver = context.getContentResolver(); 71 final Uri dictionaryPackUri = getProviderUri(locale.toString()); 72 73 final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); 74 if (null == c) return Collections.<String>emptyList(); 75 if (c.getCount() <= 0 || !c.moveToFirst()) { 76 c.close(); 77 return Collections.<String>emptyList(); 78 } 79 80 final List<String> list = new ArrayList<String>(); 81 do { 82 final String id = c.getString(0); 83 if (TextUtils.isEmpty(id)) continue; 84 list.add(id); 85 } while (c.moveToNext()); 86 c.close(); 87 return list; 88 } 89 90 /** 91 * Caches a word list the id of which is passed as an argument. 92 */ 93 private static AssetFileAddress cacheWordList(final String id, final Locale locale, 94 final ContentResolver resolver, final Context context) { 95 final Uri wordListUri = getProviderUri(id); 96 try { 97 final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(wordListUri, "r"); 98 if (null == afd) return null; 99 final String fileName = copyFileTo(afd.createInputStream(), 100 BinaryDictionaryGetter.getCacheFileName(id, locale, context)); 101 afd.close(); 102 if (0 >= resolver.delete(wordListUri, null, null)) { 103 // I'd rather not print the word list ID to the log out of security concerns 104 Log.e(TAG, "Could not have the dictionary pack delete a word list"); 105 } 106 return AssetFileAddress.makeFromFileName(fileName); 107 } catch (FileNotFoundException e) { 108 // This may only come from openAssetFileDescriptor 109 return null; 110 } catch (IOException e) { 111 // Can't read the file for some reason. 112 Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e); 113 } 114 return null; 115 } 116 117 /** 118 * Queries a content provider for word list data for some locale and cache the returned files 119 * 120 * This will query a content provider for word list data for a given locale, and copy the 121 * files locally so that they can be mmap'ed. This may overwrite previously cached word lists 122 * with newer versions if a newer version is made available by the content provider. 123 * @returns the addresses of the word list files, or null if no data could be obtained. 124 * @throw FileNotFoundException if the provider returns non-existent data. 125 * @throw IOException if the provider-returned data could not be read. 126 */ 127 public static List<AssetFileAddress> cacheWordListsFromContentProvider(final Locale locale, 128 final Context context) { 129 final ContentResolver resolver = context.getContentResolver(); 130 final List<String> idList = getWordListIds(locale, context); 131 final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>(); 132 for (String id : idList) { 133 final AssetFileAddress afd = cacheWordList(id, locale, resolver, context); 134 if (null != afd) { 135 fileAddressList.add(afd); 136 } 137 } 138 return fileAddressList; 139 } 140 141 /** 142 * Accepts a resource number as dictionary data for some locale and returns the name of a file. 143 * 144 * This will make the resource the cached dictionary for this locale, overwriting any previous 145 * cached data. 146 */ 147 public static String getDictionaryFileFromResource(int resource, Locale locale, 148 Context context) throws FileNotFoundException, IOException { 149 final Resources res = context.getResources(); 150 final Locale savedLocale = Utils.setSystemLocale(res, locale); 151 final InputStream stream = res.openRawResource(resource); 152 Utils.setSystemLocale(res, savedLocale); 153 return copyFileTo(stream, 154 BinaryDictionaryGetter.getCacheFileName(Integer.toString(resource), 155 locale, context)); 156 } 157 158 /** 159 * Copies the data in an input stream to a target file, creating the file if necessary and 160 * overwriting it if it already exists. 161 * @param input the stream to be copied. 162 * @param outputFileName the name of a file to copy the data to. It is created if necessary. 163 */ 164 private static String copyFileTo(final InputStream input, final String outputFileName) 165 throws FileNotFoundException, IOException { 166 final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE]; 167 final FileOutputStream output = new FileOutputStream(outputFileName); 168 for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) 169 output.write(buffer, 0, readBytes); 170 input.close(); 171 return outputFileName; 172 } 173} 174