• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.inputmethod.latin.makedict;
18 
19 import com.android.inputmethod.annotations.UsedForTesting;
20 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
21 import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
22 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
23 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
24 import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
25 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
26 import com.android.inputmethod.latin.utils.JniUtils;
27 
28 import android.util.Log;
29 
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 
36 /**
37  * An implementation of DictDecoder for version 3 binary dictionary.
38  */
39 @UsedForTesting
40 public class Ver3DictDecoder extends AbstractDictDecoder {
41     private static final String TAG = Ver3DictDecoder.class.getSimpleName();
42 
43     static {
JniUtils.loadNativeLibrary()44         JniUtils.loadNativeLibrary();
45     }
46 
47     // TODO: implement something sensical instead of just a phony method
doNothing()48     private static native int doNothing();
49 
50     protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
readFrequency(final DictBuffer dictBuffer)51         private static int readFrequency(final DictBuffer dictBuffer) {
52             return dictBuffer.readUnsignedByte();
53         }
54     }
55 
56     protected final File mDictionaryBinaryFile;
57     private final DictionaryBufferFactory mBufferFactory;
58     protected DictBuffer mDictBuffer;
59 
Ver3DictDecoder(final File file, final int factoryFlag)60     /* package */ Ver3DictDecoder(final File file, final int factoryFlag) {
61         mDictionaryBinaryFile = file;
62         mDictBuffer = null;
63 
64         if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
65             mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
66         } else if ((factoryFlag  & MASK_DICTBUFFER) == USE_BYTEARRAY) {
67             mBufferFactory = new DictionaryBufferFromByteArrayFactory();
68         } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) {
69             mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory();
70         } else {
71             mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
72         }
73     }
74 
Ver3DictDecoder(final File file, final DictionaryBufferFactory factory)75     /* package */ Ver3DictDecoder(final File file, final DictionaryBufferFactory factory) {
76         mDictionaryBinaryFile = file;
77         mBufferFactory = factory;
78     }
79 
80     @Override
openDictBuffer()81     public void openDictBuffer() throws FileNotFoundException, IOException {
82         mDictBuffer = mBufferFactory.getDictionaryBuffer(mDictionaryBinaryFile);
83     }
84 
85     @Override
isDictBufferOpen()86     public boolean isDictBufferOpen() {
87         return mDictBuffer != null;
88     }
89 
getDictBuffer()90     /* package */ DictBuffer getDictBuffer() {
91         return mDictBuffer;
92     }
93 
94     @UsedForTesting
openAndGetDictBuffer()95     /* package */ DictBuffer openAndGetDictBuffer() throws FileNotFoundException, IOException {
96         openDictBuffer();
97         return getDictBuffer();
98     }
99 
100     @Override
readHeader()101     public FileHeader readHeader() throws IOException, UnsupportedFormatException {
102         if (mDictBuffer == null) {
103             openDictBuffer();
104         }
105         final FileHeader header = super.readHeader(mDictBuffer);
106         final int version = header.mFormatOptions.mVersion;
107         if (!(version >= 2 && version <= 3)) {
108           throw new UnsupportedFormatException("File header has a wrong version : " + version);
109         }
110         return header;
111     }
112 
113     // TODO: Make this buffer multi thread safe.
114     private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
115     @Override
readPtNode(final int ptNodePos, final FormatOptions options)116     public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) {
117         int addressPointer = ptNodePos;
118         final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
119         addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
120 
121         final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options);
122         if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
123             addressPointer += FormatSpec.PARENT_ADDRESS_SIZE;
124         }
125 
126         final int characters[];
127         if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
128             int index = 0;
129             int character = CharEncoding.readChar(mDictBuffer);
130             addressPointer += CharEncoding.getCharSize(character);
131             while (FormatSpec.INVALID_CHARACTER != character) {
132                 // FusionDictionary is making sure that the length of the word is smaller than
133                 // MAX_WORD_LENGTH.
134                 // So we'll never write past the end of mCharacterBuffer.
135                 mCharacterBuffer[index++] = character;
136                 character = CharEncoding.readChar(mDictBuffer);
137                 addressPointer += CharEncoding.getCharSize(character);
138             }
139             characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
140         } else {
141             final int character = CharEncoding.readChar(mDictBuffer);
142             addressPointer += CharEncoding.getCharSize(character);
143             characters = new int[] { character };
144         }
145         final int frequency;
146         if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
147             frequency = PtNodeReader.readFrequency(mDictBuffer);
148             addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE;
149         } else {
150             frequency = PtNode.NOT_A_TERMINAL;
151         }
152         int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
153         if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
154             childrenAddress += addressPointer;
155         }
156         addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
157         final ArrayList<WeightedString> shortcutTargets;
158         if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
159             // readShortcut will add shortcuts to shortcutTargets.
160             shortcutTargets = new ArrayList<WeightedString>();
161             addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets);
162         } else {
163             shortcutTargets = null;
164         }
165 
166         final ArrayList<PendingAttribute> bigrams;
167         if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
168             bigrams = new ArrayList<PendingAttribute>();
169             addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams,
170                     addressPointer);
171             if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
172                 throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
173                         + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
174             }
175         } else {
176             bigrams = null;
177         }
178         return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency,
179                 parentAddress, childrenAddress, shortcutTargets, bigrams);
180     }
181 
182     @Override
readDictionaryBinary(final FusionDictionary dict, final boolean deleteDictIfBroken)183     public FusionDictionary readDictionaryBinary(final FusionDictionary dict,
184             final boolean deleteDictIfBroken)
185             throws FileNotFoundException, IOException, UnsupportedFormatException {
186         if (mDictBuffer == null) {
187             openDictBuffer();
188         }
189         try {
190             return BinaryDictDecoderUtils.readDictionaryBinary(this, dict);
191         } catch (IOException e) {
192             Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
193             if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
194                 Log.e(TAG, "Failed to delete the broken dictionary.");
195             }
196             throw e;
197         } catch (UnsupportedFormatException e) {
198             Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
199             if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
200                 Log.e(TAG, "Failed to delete the broken dictionary.");
201             }
202             throw e;
203         }
204     }
205 
206     @Override
setPosition(int newPos)207     public void setPosition(int newPos) {
208         mDictBuffer.position(newPos);
209     }
210 
211     @Override
getPosition()212     public int getPosition() {
213         return mDictBuffer.position();
214     }
215 
216     @Override
readPtNodeCount()217     public int readPtNodeCount() {
218         return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer);
219     }
220 
221     @Override
readAndFollowForwardLink()222     public boolean readAndFollowForwardLink() {
223         final int nextAddress = mDictBuffer.readUnsignedInt24();
224         if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) {
225             mDictBuffer.position(nextAddress);
226             return true;
227         }
228         return false;
229     }
230 
231     @Override
hasNextPtNodeArray()232     public boolean hasNextPtNodeArray() {
233         return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
234     }
235 
236     @Override
skipPtNode(final FormatOptions formatOptions)237     public void skipPtNode(final FormatOptions formatOptions) {
238         final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
239         PtNodeReader.readParentAddress(mDictBuffer, formatOptions);
240         BinaryDictIOUtils.skipString(mDictBuffer,
241                 (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
242         PtNodeReader.readChildrenAddress(mDictBuffer, flags, formatOptions);
243         if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readFrequency(mDictBuffer);
244         if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
245             final int shortcutsSize = mDictBuffer.readUnsignedShort();
246             mDictBuffer.position(mDictBuffer.position() + shortcutsSize
247                     - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
248         }
249         if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
250             int bigramCount = 0;
251             while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
252                 final int bigramFlags = mDictBuffer.readUnsignedByte();
253                 switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
254                     case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
255                         mDictBuffer.readUnsignedByte();
256                         break;
257                     case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
258                         mDictBuffer.readUnsignedShort();
259                         break;
260                     case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
261                         mDictBuffer.readUnsignedInt24();
262                         break;
263                 }
264                 if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break;
265             }
266             if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
267                 throw new RuntimeException("Too many bigrams in a PtNode.");
268             }
269         }
270     }
271 }
272