• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.inputmethod.latin;
18 
19 import android.content.Context;
20 import android.content.res.AssetFileDescriptor;
21 import android.content.res.Resources;
22 import android.util.Log;
23 
24 import java.io.File;
25 import java.util.ArrayList;
26 import java.util.LinkedList;
27 import java.util.Locale;
28 
29 /**
30  * Factory for dictionary instances.
31  */
32 public class DictionaryFactory {
33     private static final String TAG = DictionaryFactory.class.getSimpleName();
34     // This class must be located in the same package as LatinIME.java.
35     private static final String RESOURCE_PACKAGE_NAME =
36             DictionaryFactory.class.getPackage().getName();
37 
38     /**
39      * Initializes a main dictionary collection from a dictionary pack, with explicit flags.
40      *
41      * This searches for a content provider providing a dictionary pack for the specified
42      * locale. If none is found, it falls back to the built-in dictionary - if any.
43      * @param context application context for reading resources
44      * @param locale the locale for which to create the dictionary
45      * @param useFullEditDistance whether to use the full edit distance in suggestions
46      * @return an initialized instance of DictionaryCollection
47      */
createMainDictionaryFromManager(final Context context, final Locale locale, final boolean useFullEditDistance)48     public static DictionaryCollection createMainDictionaryFromManager(final Context context,
49             final Locale locale, final boolean useFullEditDistance) {
50         if (null == locale) {
51             Log.e(TAG, "No locale defined for dictionary");
52             return new DictionaryCollection(createBinaryDictionary(context, locale));
53         }
54 
55         final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
56         final ArrayList<AssetFileAddress> assetFileList =
57                 BinaryDictionaryGetter.getDictionaryFiles(locale, context);
58         if (null != assetFileList) {
59             for (final AssetFileAddress f : assetFileList) {
60                 final BinaryDictionary binaryDictionary =
61                         new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength,
62                                 useFullEditDistance, locale);
63                 if (binaryDictionary.isValidDictionary()) {
64                     dictList.add(binaryDictionary);
65                 }
66             }
67         }
68 
69         // If the list is empty, that means we should not use any dictionary (for example, the user
70         // explicitly disabled the main dictionary), so the following is okay. dictList is never
71         // null, but if for some reason it is, DictionaryCollection handles it gracefully.
72         return new DictionaryCollection(dictList);
73     }
74 
75     /**
76      * Initializes a main dictionary collection from a dictionary pack, with default flags.
77      *
78      * This searches for a content provider providing a dictionary pack for the specified
79      * locale. If none is found, it falls back to the built-in dictionary, if any.
80      * @param context application context for reading resources
81      * @param locale the locale for which to create the dictionary
82      * @return an initialized instance of DictionaryCollection
83      */
createMainDictionaryFromManager(final Context context, final Locale locale)84     public static DictionaryCollection createMainDictionaryFromManager(final Context context,
85             final Locale locale) {
86         return createMainDictionaryFromManager(context, locale, false /* useFullEditDistance */);
87     }
88 
89     /**
90      * Initializes a dictionary from a raw resource file
91      * @param context application context for reading resources
92      * @param locale the locale to use for the resource
93      * @return an initialized instance of BinaryDictionary
94      */
createBinaryDictionary(final Context context, final Locale locale)95     protected static BinaryDictionary createBinaryDictionary(final Context context,
96             final Locale locale) {
97         AssetFileDescriptor afd = null;
98         try {
99             final int resId =
100                     getMainDictionaryResourceIdIfAvailableForLocale(context.getResources(), locale);
101             if (0 == resId) return null;
102             afd = context.getResources().openRawResourceFd(resId);
103             if (afd == null) {
104                 Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
105                 return null;
106             }
107             final String sourceDir = context.getApplicationInfo().sourceDir;
108             final File packagePath = new File(sourceDir);
109             // TODO: Come up with a way to handle a directory.
110             if (!packagePath.isFile()) {
111                 Log.e(TAG, "sourceDir is not a file: " + sourceDir);
112                 return null;
113             }
114             return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(),
115                     false /* useFullEditDistance */, locale);
116         } catch (android.content.res.Resources.NotFoundException e) {
117             Log.e(TAG, "Could not find the resource");
118             return null;
119         } finally {
120             if (null != afd) {
121                 try {
122                     afd.close();
123                 } catch (java.io.IOException e) {
124                     /* IOException on close ? What am I supposed to do ? */
125                 }
126             }
127         }
128     }
129 
130     /**
131      * Create a dictionary from passed data. This is intended for unit tests only.
132      * @param context the test context to create this data from.
133      * @param dictionary the file to read
134      * @param startOffset the offset in the file where the data starts
135      * @param length the length of the data
136      * @param useFullEditDistance whether to use the full edit distance in suggestions
137      * @return the created dictionary, or null.
138      */
createDictionaryForTest(Context context, File dictionary, long startOffset, long length, final boolean useFullEditDistance, Locale locale)139     public static Dictionary createDictionaryForTest(Context context, File dictionary,
140             long startOffset, long length, final boolean useFullEditDistance, Locale locale) {
141         if (dictionary.isFile()) {
142             return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
143                     useFullEditDistance, locale);
144         } else {
145             Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
146             return null;
147         }
148     }
149 
150     /**
151      * Find out whether a dictionary is available for this locale.
152      * @param context the context on which to check resources.
153      * @param locale the locale to check for.
154      * @return whether a (non-placeholder) dictionary is available or not.
155      */
isDictionaryAvailable(Context context, Locale locale)156     public static boolean isDictionaryAvailable(Context context, Locale locale) {
157         final Resources res = context.getResources();
158         return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
159     }
160 
161     private static final String DEFAULT_MAIN_DICT = "main";
162     private static final String MAIN_DICT_PREFIX = "main_";
163 
164     /**
165      * Helper method to return a dictionary res id for a locale, or 0 if none.
166      * @param locale dictionary locale
167      * @return main dictionary resource id
168      */
getMainDictionaryResourceIdIfAvailableForLocale(final Resources res, final Locale locale)169     private static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
170             final Locale locale) {
171         int resId;
172         // Try to find main_language_country dictionary.
173         if (!locale.getCountry().isEmpty()) {
174             final String dictLanguageCountry = MAIN_DICT_PREFIX + locale.toString().toLowerCase();
175             if ((resId = res.getIdentifier(
176                     dictLanguageCountry, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
177                 return resId;
178             }
179         }
180 
181         // Try to find main_language dictionary.
182         final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage();
183         if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
184             return resId;
185         }
186 
187         // Not found, return 0
188         return 0;
189     }
190 
191     /**
192      * Returns a main dictionary resource id
193      * @param locale dictionary locale
194      * @return main dictionary resource id
195      */
getMainDictionaryResourceId(final Resources res, final Locale locale)196     public static int getMainDictionaryResourceId(final Resources res, final Locale locale) {
197         int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
198         if (0 != resourceId) return resourceId;
199         return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", RESOURCE_PACKAGE_NAME);
200     }
201 }
202