• 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.LinkedList;
26 import java.util.List;
27 import java.util.Locale;
28 
29 /**
30  * Factory for dictionary instances.
31  */
32 public 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      */
createDictionaryFromManager(final Context context, final Locale locale, final int fallbackResId, final Flag[] flagArray)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 List<Dictionary> dictList = new LinkedList<Dictionary>();
56         final List<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      */
createDictionaryFromManager(final Context context, final Locale locale, final int fallbackResId)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      */
createBinaryDictionary(final Context context, final int resId, final Locale locale)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      */
createDictionaryForTest(Context context, File dictionary, long startOffset, long length, Flag[] flagArray)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      */
isDictionaryAvailable(Context context, Locale locale)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 = Utils.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.
getDictionaryId(final Context context, final Locale locale)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 = Utils.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      */
isFullDictionary(final AssetFileDescriptor afd)209     protected static boolean isFullDictionary(final AssetFileDescriptor afd) {
210         return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH);
211     }
212 }
213