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