diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index f9f22ecb4..0f5fc71cb 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -21,8 +21,12 @@ import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.util.Log; +import com.android.inputmethod.latin.makedict.BinaryDictDecoder; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.LocaleUtils; @@ -227,23 +231,14 @@ final public class BinaryDictionaryGetter { // those do not include whitelist entries, the new code with an old version of the dictionary // would lose whitelist functionality. private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) { - FileInputStream inStream = null; try { // Read the version of the file - inStream = new FileInputStream(f); - final BinaryDictDecoderUtils.ByteBufferDictBuffer dictBuffer = - new BinaryDictDecoderUtils.ByteBufferDictBuffer(inStream.getChannel().map( - FileChannel.MapMode.READ_ONLY, 0, f.length())); - final int magic = dictBuffer.readInt(); - if (magic != FormatSpec.MAGIC_NUMBER) { - return false; - } - final int formatVersion = dictBuffer.readInt(); - final int headerSize = dictBuffer.readInt(); - final HashMap options = CollectionUtils.newHashMap(); - BinaryDictDecoderUtils.populateOptions(dictBuffer, headerSize, options); + final BinaryDictDecoder dictDecoder = new BinaryDictDecoder(f); + dictDecoder.openDictBuffer( + new BinaryDictDecoder.DictionaryBufferFromReadOnlyByteBufferFactory()); + final FileHeader header = dictDecoder.readHeader(); - final String version = options.get(VERSION_KEY); + final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY); if (null == version) { // No version in the options : the format is unexpected return false; @@ -259,14 +254,8 @@ final public class BinaryDictionaryGetter { return false; } catch (BufferUnderflowException e) { return false; - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } + } catch (UnsupportedFormatException e) { + return false; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoder.java index b86dfe552..2007b6284 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoder.java @@ -19,7 +19,8 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; -import com.android.inputmethod.latin.makedict.decoder.HeaderReader; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; import com.android.inputmethod.latin.utils.JniUtils; @@ -32,8 +33,9 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.HashMap; +// TODO: Rename this class to "Ver3DictDecoder" or something, and make an interface "DictDecoder". @UsedForTesting -public class BinaryDictDecoder implements HeaderReader { +public class BinaryDictDecoder { static { JniUtils.loadNativeLibrary(); @@ -132,6 +134,35 @@ public class BinaryDictDecoder implements HeaderReader { } } + private final static class HeaderReader { + protected static int readVersion(final DictBuffer dictBuffer) + throws IOException, UnsupportedFormatException { + return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer); + } + + protected static int readOptionFlags(final DictBuffer dictBuffer) { + return dictBuffer.readUnsignedShort(); + } + + protected static int readHeaderSize(final DictBuffer dictBuffer) { + return dictBuffer.readInt(); + } + + protected static HashMap readAttributes(final DictBuffer dictBuffer, + final int headerSize) { + final HashMap attributes = new HashMap(); + while (dictBuffer.position() < headerSize) { + // We can avoid an infinite loop here since dictBuffer.position() is always + // increased by calling CharEncoding.readString. + final String key = CharEncoding.readString(dictBuffer); + final String value = CharEncoding.readString(dictBuffer); + attributes.put(key, value); + } + dictBuffer.position(headerSize); + return attributes; + } + } + private final File mDictionaryBinaryFile; private DictBuffer mDictBuffer; @@ -157,33 +188,26 @@ public class BinaryDictDecoder implements HeaderReader { return getDictBuffer(); } - // The implementation of HeaderReader - @Override - public int readVersion() throws IOException, UnsupportedFormatException { - return BinaryDictDecoderUtils.checkFormatVersion(mDictBuffer); - } + // TODO : Define public functions of decoders + public FileHeader readHeader() throws IOException, UnsupportedFormatException { + final int version = HeaderReader.readVersion(mDictBuffer); + final int optionsFlags = HeaderReader.readOptionFlags(mDictBuffer); - @Override - public int readOptionFlags() { - return mDictBuffer.readUnsignedShort(); - } + final int headerSize = HeaderReader.readHeaderSize(mDictBuffer); - @Override - public int readHeaderSize() { - return mDictBuffer.readInt(); - } - - @Override - public HashMap readAttributes(final int headerSize) { - final HashMap attributes = new HashMap(); - while (mDictBuffer.position() < headerSize) { - // We can avoid infinite loop here since mFusionDictonary.position() is always increased - // by calling CharEncoding.readString. - final String key = CharEncoding.readString(mDictBuffer); - final String value = CharEncoding.readString(mDictBuffer); - attributes.put(key, value); + if (headerSize < 0) { + throw new UnsupportedFormatException("header size can't be negative."); } - mDictBuffer.position(headerSize); - return attributes; + + final HashMap attributes = HeaderReader.readAttributes(mDictBuffer, + headerSize); + + final FileHeader header = new FileHeader(headerSize, + new FusionDictionary.DictionaryOptions(attributes, + 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), + 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), + new FormatOptions(version, + 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); + return header; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index efa491099..d1974c8d4 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -22,7 +22,6 @@ import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; -import com.android.inputmethod.latin.makedict.decoder.HeaderReader; import java.io.ByteArrayOutputStream; import java.io.File; @@ -33,7 +32,6 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; import java.util.TreeMap; @@ -639,49 +637,6 @@ public final class BinaryDictDecoderUtils { return version; } - /** - * Reads a header from a buffer. - * @param headerReader the header reader - * @throws IOException - * @throws UnsupportedFormatException - */ - public static FileHeader readHeader(final HeaderReader headerReader) - throws IOException, UnsupportedFormatException { - final int version = headerReader.readVersion(); - final int optionsFlags = headerReader.readOptionFlags(); - - final int headerSize = headerReader.readHeaderSize(); - - if (headerSize < 0) { - throw new UnsupportedFormatException("header size can't be negative."); - } - - final HashMap attributes = headerReader.readAttributes(headerSize); - - final FileHeader header = new FileHeader(headerSize, - new FusionDictionary.DictionaryOptions(attributes, - 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), - 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), - new FormatOptions(version, - 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); - return header; - } - - /** - * Reads options from a buffer and populate a map with their contents. - * - * The buffer is read at the current position, so the caller must take care the pointer - * is in the right place before calling this. - */ - public static void populateOptions(final DictBuffer dictBuffer, - final int headerSize, final HashMap options) { - while (dictBuffer.position() < headerSize) { - final String key = CharEncoding.readString(dictBuffer); - final String value = CharEncoding.readString(dictBuffer); - options.put(key, value); - } - } - /** * Reads a buffer and returns the memory representation of the dictionary. * @@ -706,7 +661,7 @@ public final class BinaryDictDecoderUtils { } // Read header - final FileHeader fileHeader = readHeader(dictDecoder); + final FileHeader fileHeader = dictDecoder.readHeader(); Map reverseNodeArrayMapping = new TreeMap(); Map reverseGroupMapping = new TreeMap(); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 4cf72e915..1abc779d0 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -153,7 +153,7 @@ public final class BinaryDictIOUtils { final Map> bigrams) throws IOException, UnsupportedFormatException { // Read header - final FileHeader header = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader header = dictDecoder.readHeader(); readUnigramsAndBigramsBinaryInner(dictDecoder.getDictBuffer(), header.mHeaderSize, words, frequencies, bigrams, header.mFormatOptions); } @@ -175,7 +175,7 @@ public final class BinaryDictIOUtils { if (word == null) return FormatSpec.NOT_VALID_WORD; if (dictBuffer.position() != 0) dictBuffer.position(0); - final FileHeader header = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader header = dictDecoder.readHeader(); int wordPos = 0; final int wordLen = word.codePointCount(0, word.length()); for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) { @@ -523,7 +523,7 @@ public final class BinaryDictIOUtils { final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); if (position != FormatSpec.NOT_VALID_WORD) { dictBuffer.position(0); - final FileHeader header = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader header = dictDecoder.readHeader(); dictBuffer.position(position); return BinaryDictDecoderUtils.readCharGroup(dictBuffer, position, header.mFormatOptions); @@ -559,7 +559,7 @@ public final class BinaryDictIOUtils { } } }); - return BinaryDictDecoderUtils.readHeader(dictDecoder); + return dictDecoder.readHeader(); } public static FileHeader getDictionaryFileHeaderOrNull(final File file, final long offset, diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java index 5361d2eba..6c1e75cbb 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java @@ -59,7 +59,7 @@ public final class DynamicBinaryDictIOUtils { throws IOException, UnsupportedFormatException { final DictBuffer dictBuffer = dictDecoder.getDictBuffer(); dictBuffer.position(0); - final FileHeader header = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader header = dictDecoder.readHeader(); final int wordPosition = BinaryDictIOUtils.getTerminalPosition(dictDecoder, word); if (wordPosition == FormatSpec.NOT_VALID_WORD) return; @@ -278,7 +278,7 @@ public final class DynamicBinaryDictIOUtils { // find the insert position of the word. if (dictBuffer.position() != 0) dictBuffer.position(0); - final FileHeader fileHeader = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader fileHeader = dictDecoder.readHeader(); int wordPos = 0, address = dictBuffer.position(), nodeOriginAddress = dictBuffer.position(); final int[] codePoints = FusionDictionary.getCodePoints(word); diff --git a/java/src/com/android/inputmethod/latin/makedict/decoder/HeaderReader.java b/java/src/com/android/inputmethod/latin/makedict/decoder/HeaderReader.java deleted file mode 100644 index f2badb444..000000000 --- a/java/src/com/android/inputmethod/latin/makedict/decoder/HeaderReader.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.makedict.decoder; - -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; - -import java.io.IOException; -import java.util.HashMap; - -/** - * An interface to read a binary dictionary file header. - */ -public interface HeaderReader { - public int readVersion() throws IOException, UnsupportedFormatException; - public int readOptionFlags(); - public int readHeaderSize(); - public HashMap readAttributes(final int headerSize); -} diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index 77397a68c..028e089f8 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -505,7 +505,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { FileHeader fileHeader = null; try { - fileHeader = BinaryDictDecoderUtils.readHeader(dictDecoder); + fileHeader = dictDecoder.readHeader(); } catch (IOException e) { return null; } catch (UnsupportedFormatException e) { diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java index 7c0f07d43..b6e439561 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java @@ -130,7 +130,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { private static void printBinaryFile(final BinaryDictDecoder dictDecoder) throws IOException, UnsupportedFormatException { - final FileHeader fileHeader = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader fileHeader = dictDecoder.readHeader(); final DictBuffer buffer = dictDecoder.getDictBuffer(); while (buffer.position() < buffer.limit()) { printNode(buffer, fileHeader.mFormatOptions); @@ -225,7 +225,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { try { final DictBuffer dictBuffer = dictDecoder.openAndGetDictBuffer( new BinaryDictDecoder.DictionaryBufferFromReadOnlyByteBufferFactory()); - final FileHeader fileHeader = BinaryDictDecoderUtils.readHeader(dictDecoder); + final FileHeader fileHeader = dictDecoder.readHeader(); assertEquals(word, BinaryDictDecoderUtils.getWordAtAddress(dictDecoder.getDictBuffer(), fileHeader.mHeaderSize, position - fileHeader.mHeaderSize,