DictionaryFactory.java revision 0730bbfbf5e37bbcb5c287aeff71b304c833a36e
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.Locale; 26 27/** 28 * Factory for dictionary instances. 29 */ 30public class DictionaryFactory { 31 32 private static String TAG = DictionaryFactory.class.getSimpleName(); 33 34 /** 35 * Initializes a dictionary from a dictionary pack. 36 * 37 * This searches for a content provider providing a dictionary pack for the specified 38 * locale. If none is found, it falls back to using the resource passed as fallBackResId 39 * as a dictionary. 40 * @param context application context for reading resources 41 * @param locale the locale for which to create the dictionary 42 * @param fallbackResId the id of the resource to use as a fallback if no pack is found 43 * @return an initialized instance of Dictionary 44 */ 45 public static Dictionary createDictionaryFromManager(Context context, Locale locale, 46 int fallbackResId) { 47 if (null == locale) { 48 Log.e(TAG, "No locale defined for dictionary"); 49 return new DictionaryCollection(createBinaryDictionary(context, fallbackResId)); 50 } 51 52 final AssetFileAddress dictFile = BinaryDictionaryGetter.getDictionaryFile(locale, 53 context, fallbackResId); 54 if (null == dictFile) return null; 55 return new DictionaryCollection(new BinaryDictionary(context, 56 dictFile.mFilename, dictFile.mOffset, dictFile.mLength, null)); 57 } 58 59 /** 60 * Initializes a dictionary from a raw resource file 61 * @param context application context for reading resources 62 * @param resId the resource containing the raw binary dictionary 63 * @return an initialized instance of BinaryDictionary 64 */ 65 protected static BinaryDictionary createBinaryDictionary(Context context, int resId) { 66 AssetFileDescriptor afd = null; 67 try { 68 // TODO: IMPORTANT: Do not create a dictionary from a placeholder. 69 afd = context.getResources().openRawResourceFd(resId); 70 if (afd == null) { 71 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); 72 return null; 73 } 74 if (!isFullDictionary(afd)) return null; 75 final String sourceDir = context.getApplicationInfo().sourceDir; 76 final File packagePath = new File(sourceDir); 77 // TODO: Come up with a way to handle a directory. 78 if (!packagePath.isFile()) { 79 Log.e(TAG, "sourceDir is not a file: " + sourceDir); 80 return null; 81 } 82 return new BinaryDictionary(context, 83 sourceDir, afd.getStartOffset(), afd.getLength(), null); 84 } catch (android.content.res.Resources.NotFoundException e) { 85 Log.e(TAG, "Could not find the resource. resId=" + resId); 86 return null; 87 } finally { 88 if (null != afd) { 89 try { 90 afd.close(); 91 } catch (java.io.IOException e) { 92 /* IOException on close ? What am I supposed to do ? */ 93 } 94 } 95 } 96 } 97 98 /** 99 * Create a dictionary from passed data. This is intended for unit tests only. 100 * @param context the test context to create this data from. 101 * @param dictionary the file to read 102 * @param startOffset the offset in the file where the data starts 103 * @param length the length of the data 104 * @param flagArray the flags to use with this data for testing 105 * @return the created dictionary, or null. 106 */ 107 public static Dictionary createDictionaryForTest(Context context, File dictionary, 108 long startOffset, long length, Flag[] flagArray) { 109 if (dictionary.isFile()) { 110 return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length, 111 flagArray); 112 } else { 113 Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); 114 return null; 115 } 116 } 117 118 /** 119 * Find out whether a dictionary is available for this locale. 120 * @param context the context on which to check resources. 121 * @param locale the locale to check for. 122 * @return whether a (non-placeholder) dictionary is available or not. 123 */ 124 public static boolean isDictionaryAvailable(Context context, Locale locale) { 125 final Resources res = context.getResources(); 126 final Locale saveLocale = Utils.setSystemLocale(res, locale); 127 128 final int resourceId = Utils.getMainDictionaryResourceId(res); 129 final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); 130 final boolean hasDictionary = isFullDictionary(afd); 131 try { 132 if (null != afd) afd.close(); 133 } catch (java.io.IOException e) { 134 /* Um, what can we do here exactly? */ 135 } 136 137 Utils.setSystemLocale(res, saveLocale); 138 return hasDictionary; 139 } 140 141 // TODO: Find the Right Way to find out whether the resource is a placeholder or not. 142 // Suggestion : strip the locale, open the placeholder file and store its offset. 143 // Upon opening the file, if it's the same offset, then it's the placeholder. 144 private static final long PLACEHOLDER_LENGTH = 34; 145 /** 146 * Finds out whether the data pointed out by an AssetFileDescriptor is a full 147 * dictionary (as opposed to null, or to a place holder). 148 * @param afd the file descriptor to test, or null 149 * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder 150 */ 151 protected static boolean isFullDictionary(final AssetFileDescriptor afd) { 152 return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); 153 } 154} 155