DictionaryFactory.java revision e150ef98569d61078e0f8c67ded8364a9c3d4a20
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.LinkedList; 26import java.util.List; 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. 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 * @return an initialized instance of Dictionary 46 */ 47 public static Dictionary createDictionaryFromManager(Context context, Locale locale, 48 int fallbackResId) { 49 if (null == locale) { 50 Log.e(TAG, "No locale defined for dictionary"); 51 return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); 52 } 53 54 final List<Dictionary> dictList = new LinkedList<Dictionary>(); 55 final List<AssetFileAddress> assetFileList = 56 BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId); 57 if (null != assetFileList) { 58 for (final AssetFileAddress f : assetFileList) { 59 final BinaryDictionary binaryDictionary = 60 new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null); 61 if (binaryDictionary.isValidDictionary()) { 62 dictList.add(binaryDictionary); 63 } 64 } 65 } 66 67 // null == dictList is not supposed to be possible, but better safe than sorry and it's 68 // safer for future extension. In this case, rather than returning null, it should be safer 69 // to return an empty DictionaryCollection. 70 if (null == dictList) { 71 return new DictionaryCollection(); 72 } else { 73 if (dictList.isEmpty()) { 74 // The list may be empty if no dictionaries have been added. The getter should not 75 // return an empty list, but if it does we end up here. Likewise, if the files 76 // we found could not be opened by the native code for any reason (format mismatch, 77 // file too big to fit in memory, etc) then we could have an empty list. In this 78 // case we want to fall back on the resource. 79 return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, 80 locale)); 81 } else { 82 return new DictionaryCollection(dictList); 83 } 84 } 85 } 86 87 /** 88 * Initializes a dictionary from a raw resource file 89 * @param context application context for reading resources 90 * @param resId the resource containing the raw binary dictionary 91 * @param locale the locale to use for the resource 92 * @return an initialized instance of BinaryDictionary 93 */ 94 protected static BinaryDictionary createBinaryDictionary(final Context context, 95 final int resId, final Locale locale) { 96 AssetFileDescriptor afd = null; 97 try { 98 final Resources res = context.getResources(); 99 if (null != locale) { 100 final Locale savedLocale = Utils.setSystemLocale(res, locale); 101 afd = res.openRawResourceFd(resId); 102 Utils.setSystemLocale(res, savedLocale); 103 } else { 104 afd = res.openRawResourceFd(resId); 105 } 106 if (afd == null) { 107 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); 108 return null; 109 } 110 if (!isFullDictionary(afd)) return null; 111 final String sourceDir = context.getApplicationInfo().sourceDir; 112 final File packagePath = new File(sourceDir); 113 // TODO: Come up with a way to handle a directory. 114 if (!packagePath.isFile()) { 115 Log.e(TAG, "sourceDir is not a file: " + sourceDir); 116 return null; 117 } 118 return new BinaryDictionary(context, 119 sourceDir, afd.getStartOffset(), afd.getLength(), null); 120 } catch (android.content.res.Resources.NotFoundException e) { 121 Log.e(TAG, "Could not find the resource. resId=" + resId); 122 return null; 123 } finally { 124 if (null != afd) { 125 try { 126 afd.close(); 127 } catch (java.io.IOException e) { 128 /* IOException on close ? What am I supposed to do ? */ 129 } 130 } 131 } 132 } 133 134 /** 135 * Create a dictionary from passed data. This is intended for unit tests only. 136 * @param context the test context to create this data from. 137 * @param dictionary the file to read 138 * @param startOffset the offset in the file where the data starts 139 * @param length the length of the data 140 * @param flagArray the flags to use with this data for testing 141 * @return the created dictionary, or null. 142 */ 143 public static Dictionary createDictionaryForTest(Context context, File dictionary, 144 long startOffset, long length, Flag[] flagArray) { 145 if (dictionary.isFile()) { 146 return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length, 147 flagArray); 148 } else { 149 Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); 150 return null; 151 } 152 } 153 154 /** 155 * Find out whether a dictionary is available for this locale. 156 * @param context the context on which to check resources. 157 * @param locale the locale to check for. 158 * @return whether a (non-placeholder) dictionary is available or not. 159 */ 160 public static boolean isDictionaryAvailable(Context context, Locale locale) { 161 final Resources res = context.getResources(); 162 final Locale saveLocale = Utils.setSystemLocale(res, locale); 163 164 final int resourceId = Utils.getMainDictionaryResourceId(res); 165 final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); 166 final boolean hasDictionary = isFullDictionary(afd); 167 try { 168 if (null != afd) afd.close(); 169 } catch (java.io.IOException e) { 170 /* Um, what can we do here exactly? */ 171 } 172 173 Utils.setSystemLocale(res, saveLocale); 174 return hasDictionary; 175 } 176 177 // TODO: Do not use the size of the dictionary as an unique dictionary ID. 178 public static Long getDictionaryId(Context context, Locale locale) { 179 final Resources res = context.getResources(); 180 final Locale saveLocale = Utils.setSystemLocale(res, locale); 181 182 final int resourceId = Utils.getMainDictionaryResourceId(res); 183 final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); 184 final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) 185 ? afd.getLength() 186 : null; 187 try { 188 if (null != afd) afd.close(); 189 } catch (java.io.IOException e) { 190 } 191 192 Utils.setSystemLocale(res, saveLocale); 193 return size; 194 } 195 196 // TODO: Find the Right Way to find out whether the resource is a placeholder or not. 197 // Suggestion : strip the locale, open the placeholder file and store its offset. 198 // Upon opening the file, if it's the same offset, then it's the placeholder. 199 private static final long PLACEHOLDER_LENGTH = 34; 200 /** 201 * Finds out whether the data pointed out by an AssetFileDescriptor is a full 202 * dictionary (as opposed to null, or to a place holder). 203 * @param afd the file descriptor to test, or null 204 * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder 205 */ 206 protected static boolean isFullDictionary(final AssetFileDescriptor afd) { 207 return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); 208 } 209} 210