Ver4DictEncoder.java revision e708b1bc2e11285ad404133b8de21719ce08acb5
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.latin.makedict;
18
19import com.android.inputmethod.annotations.UsedForTesting;
20import com.android.inputmethod.latin.BinaryDictionary;
21import com.android.inputmethod.latin.Dictionary;
22import com.android.inputmethod.latin.PrevWordsInfo;
23import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
24import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
25import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
26import com.android.inputmethod.latin.utils.LocaleUtils;
27
28import java.io.File;
29import java.io.IOException;
30
31/**
32 * An implementation of DictEncoder for version 4 binary dictionary.
33 */
34@UsedForTesting
35public class Ver4DictEncoder implements DictEncoder {
36    private final File mDictPlacedDir;
37
38    @UsedForTesting
39    public Ver4DictEncoder(final File dictPlacedDir) {
40        mDictPlacedDir = dictPlacedDir;
41    }
42
43    // TODO: This builds a FusionDictionary first and iterates it to add words to the binary
44    // dictionary. However, it is possible to just add words directly to the binary dictionary
45    // instead.
46    // In the long run, when we stop supporting version 2, FusionDictionary will become deprecated
47    // and we can remove it. Then we'll be able to just call BinaryDictionary directly.
48    @Override
49    public void writeDictionary(FusionDictionary dict, FormatOptions formatOptions)
50            throws IOException, UnsupportedFormatException {
51        if (formatOptions.mVersion != FormatSpec.VERSION4) {
52            throw new UnsupportedFormatException("File header has a wrong version number : "
53                    + formatOptions.mVersion);
54        }
55        if (!mDictPlacedDir.isDirectory()) {
56            throw new UnsupportedFormatException("Given path is not a directory.");
57        }
58        if (!BinaryDictionaryUtils.createEmptyDictFile(mDictPlacedDir.getAbsolutePath(),
59                FormatSpec.VERSION4, LocaleUtils.constructLocaleFromString(
60                dict.mOptions.mAttributes.get(DictionaryHeader.DICTIONARY_LOCALE_KEY)),
61                dict.mOptions.mAttributes)) {
62            throw new IOException("Cannot create dictionary file : "
63                + mDictPlacedDir.getAbsolutePath());
64        }
65        final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(),
66                0l, mDictPlacedDir.length(), true /* useFullEditDistance */,
67                LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get(
68                        DictionaryHeader.DICTIONARY_LOCALE_KEY)),
69                Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */,
70                true /* isUpdatable */);
71        if (!binaryDict.isValidDictionary()) {
72            // Somehow createEmptyDictFile returned true, but the file was not created correctly
73            throw new IOException("Cannot create dictionary file");
74        }
75        for (final WordProperty wordProperty : dict) {
76            // TODO: switch to addMultipleDictionaryEntries when they support shortcuts
77            if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) {
78                if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(),
79                        null /* shortcutTarget */, 0 /* shortcutProbability */,
80                        wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
81                        wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
82                    MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord);
83                }
84            } else {
85                for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
86                    if (!binaryDict.addUnigramEntry(wordProperty.mWord,
87                            wordProperty.getProbability(),
88                            shortcutTarget.mWord, shortcutTarget.getProbability(),
89                            wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord,
90                            wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) {
91                        MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord
92                                + ", shortcutTarget: " + shortcutTarget.mWord);
93                        return;
94                    }
95                }
96            }
97            if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
98                if (!binaryDict.flushWithGC()) {
99                    MakedictLog.e("Cannot flush dict with GC.");
100                    return;
101                }
102            }
103        }
104        for (final WordProperty word0Property : dict) {
105            if (null == word0Property.mBigrams) continue;
106            for (final WeightedString word1 : word0Property.mBigrams) {
107                final PrevWordsInfo prevWordsInfo =
108                        new PrevWordsInfo(new PrevWordsInfo.WordInfo(word0Property.mWord));
109                if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord,
110                        word1.getProbability(), 0 /* timestamp */)) {
111                    MakedictLog.e("Cannot add n-gram entry for "
112                            + prevWordsInfo + " -> " + word1.mWord);
113                    return;
114                }
115                if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) {
116                    if (!binaryDict.flushWithGC()) {
117                        MakedictLog.e("Cannot flush dict with GC.");
118                        return;
119                    }
120                }
121            }
122        }
123        if (!binaryDict.flushWithGC()) {
124            MakedictLog.e("Cannot flush dict with GC.");
125            return;
126        }
127        binaryDict.close();
128    }
129
130    @Override
131    public void setPosition(int position) {
132    }
133
134    @Override
135    public int getPosition() {
136        return 0;
137    }
138
139    @Override
140    public void writePtNodeCount(int ptNodeCount) {
141    }
142
143    @Override
144    public void writeForwardLinkAddress(int forwardLinkAddress) {
145    }
146
147    @Override
148    public void writePtNode(PtNode ptNode, FusionDictionary dict) {
149    }
150}
151