DictionaryFactory.java revision 660776e09b9a3b321074a94721d901a035ca1b9f
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.Context; 20import android.content.res.AssetFileDescriptor; 21import android.content.res.Resources; 22import android.util.Log; 23 24import java.io.File; 25import java.util.ArrayList; 26import java.util.LinkedList; 27import java.util.Locale; 28 29/** 30 * Factory for dictionary instances. 31 */ 32public class DictionaryFactory { 33 34 private static String TAG = DictionaryFactory.class.getSimpleName(); 35 36 /** 37 * Initializes a dictionary from a dictionary pack, with explicit flags. 38 * 39 * This searches for a content provider providing a dictionary pack for the specified 40 * locale. If none is found, it falls back to using the resource passed as fallBackResId 41 * as a dictionary. 42 * @param context application context for reading resources 43 * @param locale the locale for which to create the dictionary 44 * @param fallbackResId the id of the resource to use as a fallback if no pack is found 45 * @param flagArray an array of flags to use 46 * @return an initialized instance of DictionaryCollection 47 */ 48 public static DictionaryCollection createDictionaryFromManager(final Context context, 49 final Locale locale, final int fallbackResId, final Flag[] flagArray) { 50 if (null == locale) { 51 Log.e(TAG, "No locale defined for dictionary"); 52 return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); 53 } 54 55 final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>(); 56 final ArrayList<AssetFileAddress> assetFileList = 57 BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId); 58 if (null != assetFileList) { 59 for (final AssetFileAddress f : assetFileList) { 60 final BinaryDictionary binaryDictionary = 61 new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, flagArray); 62 if (binaryDictionary.isValidDictionary()) { 63 dictList.add(binaryDictionary); 64 } 65 } 66 } 67 68 // If the list is empty, that means we should not use any dictionary (for example, the user 69 // explicitly disabled the main dictionary), so the following is okay. dictList is never 70 // null, but if for some reason it is, DictionaryCollection handles it gracefully. 71 return new DictionaryCollection(dictList); 72 } 73 74 /** 75 * Initializes a dictionary from a dictionary pack, with default flags. 76 * 77 * This searches for a content provider providing a dictionary pack for the specified 78 * locale. If none is found, it falls back to using the resource passed as fallBackResId 79 * as a dictionary. 80 * @param context application context for reading resources 81 * @param locale the locale for which to create the dictionary 82 * @param fallbackResId the id of the resource to use as a fallback if no pack is found 83 * @return an initialized instance of DictionaryCollection 84 */ 85 public static DictionaryCollection createDictionaryFromManager(final Context context, 86 final Locale locale, final int fallbackResId) { 87 return createDictionaryFromManager(context, locale, fallbackResId, null); 88 } 89 90 /** 91 * Initializes a dictionary from a raw resource file 92 * @param context application context for reading resources 93 * @param resId the resource containing the raw binary dictionary 94 * @param locale the locale to use for the resource 95 * @return an initialized instance of BinaryDictionary 96 */ 97 protected static BinaryDictionary createBinaryDictionary(final Context context, 98 final int resId, final Locale locale) { 99 AssetFileDescriptor afd = null; 100 try { 101 final Resources res = context.getResources(); 102 if (null != locale) { 103 final Locale savedLocale = LocaleUtils.setSystemLocale(res, locale); 104 afd = res.openRawResourceFd(resId); 105 LocaleUtils.setSystemLocale(res, savedLocale); 106 } else { 107 afd = res.openRawResourceFd(resId); 108 } 109 if (afd == null) { 110 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); 111 return null; 112 } 113 if (!isFullDictionary(afd)) return null; 114 final String sourceDir = context.getApplicationInfo().sourceDir; 115 final File packagePath = new File(sourceDir); 116 // TODO: Come up with a way to handle a directory. 117 if (!packagePath.isFile()) { 118 Log.e(TAG, "sourceDir is not a file: " + sourceDir); 119 return null; 120 } 121 return new BinaryDictionary(context, 122 sourceDir, afd.getStartOffset(), afd.getLength(), null); 123 } catch (android.content.res.Resources.NotFoundException e) { 124 Log.e(TAG, "Could not find the resource. resId=" + resId); 125 return null; 126 } finally { 127 if (null != afd) { 128 try { 129 afd.close(); 130 } catch (java.io.IOException e) { 131 /* IOException on close ? What am I supposed to do ? */ 132 } 133 } 134 } 135 } 136 137 /** 138 * Create a dictionary from passed data. This is intended for unit tests only. 139 * @param context the test context to create this data from. 140 * @param dictionary the file to read 141 * @param startOffset the offset in the file where the data starts 142 * @param length the length of the data 143 * @param flagArray the flags to use with this data for testing 144 * @return the created dictionary, or null. 145 */ 146 public static Dictionary createDictionaryForTest(Context context, File dictionary, 147 long startOffset, long length, Flag[] flagArray) { 148 if (dictionary.isFile()) { 149 return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length, 150 flagArray); 151 } else { 152 Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); 153 return null; 154 } 155 } 156 157 /** 158 * Find out whether a dictionary is available for this locale. 159 * @param context the context on which to check resources. 160 * @param locale the locale to check for. 161 * @return whether a (non-placeholder) dictionary is available or not. 162 */ 163 public static boolean isDictionaryAvailable(Context context, Locale locale) { 164 final Resources res = context.getResources(); 165 final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale); 166 167 final int resourceId = getMainDictionaryResourceId(res); 168 final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); 169 final boolean hasDictionary = isFullDictionary(afd); 170 try { 171 if (null != afd) afd.close(); 172 } catch (java.io.IOException e) { 173 /* Um, what can we do here exactly? */ 174 } 175 176 LocaleUtils.setSystemLocale(res, saveLocale); 177 return hasDictionary; 178 } 179 180 // TODO: Do not use the size of the dictionary as an unique dictionary ID. 181 public static Long getDictionaryId(final Context context, final Locale locale) { 182 final Resources res = context.getResources(); 183 final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale); 184 185 final int resourceId = getMainDictionaryResourceId(res); 186 final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); 187 final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) 188 ? afd.getLength() 189 : null; 190 try { 191 if (null != afd) afd.close(); 192 } catch (java.io.IOException e) { 193 } 194 195 LocaleUtils.setSystemLocale(res, saveLocale); 196 return size; 197 } 198 199 // TODO: Find the Right Way to find out whether the resource is a placeholder or not. 200 // Suggestion : strip the locale, open the placeholder file and store its offset. 201 // Upon opening the file, if it's the same offset, then it's the placeholder. 202 private static final long PLACEHOLDER_LENGTH = 34; 203 /** 204 * Finds out whether the data pointed out by an AssetFileDescriptor is a full 205 * dictionary (as opposed to null, or to a place holder). 206 * @param afd the file descriptor to test, or null 207 * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder 208 */ 209 protected static boolean isFullDictionary(final AssetFileDescriptor afd) { 210 return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); 211 } 212 213 /** 214 * Returns a main dictionary resource id 215 * @return main dictionary resource id 216 */ 217 public static int getMainDictionaryResourceId(Resources res) { 218 final String MAIN_DIC_NAME = "main"; 219 String packageName = LatinIME.class.getPackage().getName(); 220 return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName); 221 } 222} 223