DictionaryFactory.java revision 19bfef6cb0714a46a276abe45329e4abb661f76e
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.Configuration;
22import android.content.res.Resources;
23import android.util.Log;
24
25import java.io.File;
26import java.util.Locale;
27
28/**
29 * Factory for dictionary instances.
30 */
31public class DictionaryFactory {
32
33    private static String TAG = DictionaryFactory.class.getSimpleName();
34
35    /**
36     * Initializes a dictionary from a dictionary pack.
37     *
38     * This searches for a content provider providing a dictionary pack for the specified
39     * locale. If none is found, it falls back to using the resource passed as fallBackResId
40     * as a dictionary.
41     * @param context application context for reading resources
42     * @param locale the locale for which to create the dictionary
43     * @param fallbackResId the id of the resource to use as a fallback if no pack is found
44     * @return an initialized instance of Dictionary
45     */
46    public static Dictionary createDictionaryFromManager(Context context, Locale locale,
47            int fallbackResId) {
48        if (null == locale) {
49            Log.e(TAG, "No locale defined for dictionary");
50            return new DictionaryCollection(createBinaryDictionary(context, fallbackResId));
51        }
52
53        final AssetFileAddress dictFile = BinaryDictionaryGetter.getDictionaryFile(locale,
54                context, fallbackResId);
55        if (null == dictFile) return null;
56        return new DictionaryCollection(new BinaryDictionary(context,
57                dictFile.mFilename, dictFile.mOffset, dictFile.mLength, null));
58    }
59
60    /**
61     * Initializes a dictionary from a raw resource file
62     * @param context application context for reading resources
63     * @param resId the resource containing the raw binary dictionary
64     * @return an initialized instance of BinaryDictionary
65     */
66    protected static BinaryDictionary createBinaryDictionary(Context context, int resId) {
67        AssetFileDescriptor afd = null;
68        try {
69            // TODO: IMPORTANT: Do not create a dictionary from a placeholder.
70            afd = context.getResources().openRawResourceFd(resId);
71            if (afd == null) {
72                Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
73                return null;
74            }
75            if (!isFullDictionary(afd)) return null;
76            final String sourceDir = context.getApplicationInfo().sourceDir;
77            final File packagePath = new File(sourceDir);
78            // TODO: Come up with a way to handle a directory.
79            if (!packagePath.isFile()) {
80                Log.e(TAG, "sourceDir is not a file: " + sourceDir);
81                return null;
82            }
83            return new BinaryDictionary(context,
84                    sourceDir, afd.getStartOffset(), afd.getLength(), null);
85        } catch (android.content.res.Resources.NotFoundException e) {
86            Log.e(TAG, "Could not find the resource. resId=" + resId);
87            return null;
88        } finally {
89            if (null != afd) {
90                try {
91                    afd.close();
92                } catch (java.io.IOException e) {
93                    /* IOException on close ? What am I supposed to do ? */
94                }
95            }
96        }
97    }
98
99    /**
100     * Create a dictionary from passed data. This is intended for unit tests only.
101     * @param context the test context to create this data from.
102     * @param dictionary the file to read
103     * @param startOffset the offset in the file where the data starts
104     * @param length the length of the data
105     * @param flagArray the flags to use with this data for testing
106     * @return the created dictionary, or null.
107     */
108    public static Dictionary createDictionaryForTest(Context context, File dictionary,
109            long startOffset, long length, Flag[] flagArray) {
110        if (dictionary.isFile()) {
111            return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
112                    flagArray);
113        } else {
114            Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
115            return null;
116        }
117    }
118
119    /**
120     * Find out whether a dictionary is available for this locale.
121     * @param context the context on which to check resources.
122     * @param locale the locale to check for.
123     * @return whether a (non-placeholder) dictionary is available or not.
124     */
125    public static boolean isDictionaryAvailable(Context context, Locale locale) {
126        final Resources res = context.getResources();
127        final Locale saveLocale = Utils.setSystemLocale(res, locale);
128
129        final int resourceId = Utils.getMainDictionaryResourceId(res);
130        final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
131        final boolean hasDictionary = isFullDictionary(afd);
132        try {
133            if (null != afd) afd.close();
134        } catch (java.io.IOException e) {
135            /* Um, what can we do here exactly? */
136        }
137
138        Utils.setSystemLocale(res, saveLocale);
139        return hasDictionary;
140    }
141
142    // TODO: Find the Right Way to find out whether the resource is a placeholder or not.
143    // Suggestion : strip the locale, open the placeholder file and store its offset.
144    // Upon opening the file, if it's the same offset, then it's the placeholder.
145    private static final long PLACEHOLDER_LENGTH = 34;
146    /**
147     * Finds out whether the data pointed out by an AssetFileDescriptor is a full
148     * dictionary (as opposed to null, or to a place holder).
149     * @param afd the file descriptor to test, or null
150     * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder
151     */
152    protected static boolean isFullDictionary(final AssetFileDescriptor afd) {
153        return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH);
154    }
155}
156