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