Home | History | Annotate | Download | only in latin
      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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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