177fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard/*
277fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * Copyright (C) 2012 The Android Open Source Project
377fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard *
477fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * Licensed under the Apache License, Version 2.0 (the "License"); you may not
577fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * use this file except in compliance with the License. You may obtain a copy of
677fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * the License at
777fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard *
877fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * http://www.apache.org/licenses/LICENSE-2.0
977fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard *
1077fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * Unless required by applicable law or agreed to in writing, software
1177fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1277fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1377fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * License for the specific language governing permissions and limitations under
1477fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard * the License.
1577fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard */
1677fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard
1777fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalardpackage com.android.inputmethod.latin.dicttool;
1877fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard
1977bce05e6f6e3a988253f9305ae22e51f56f5b1aYuichiro Hanadaimport com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
2093cda5bb396c22f1781e390debaf75d54cf7c0dcKeisuke Kuroyanagiimport com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
21e9a10ff0f026b5ec458f116afc7a75806574cbcdYuichiro Hanadaimport com.android.inputmethod.latin.makedict.DictDecoder;
226ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalardimport com.android.inputmethod.latin.makedict.FusionDictionary;
236ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalardimport com.android.inputmethod.latin.makedict.UnsupportedFormatException;
246ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard
256ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalardimport org.xml.sax.SAXException;
26b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
27b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardimport java.io.BufferedInputStream;
28b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardimport java.io.BufferedOutputStream;
29fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaokaimport java.io.BufferedReader;
30a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaokaimport java.io.File;
31b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardimport java.io.FileInputStream;
32b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardimport java.io.FileOutputStream;
3377fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalardimport java.io.IOException;
3477fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalardimport java.io.InputStream;
35fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaokaimport java.io.InputStreamReader;
3677fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalardimport java.io.OutputStream;
37b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardimport java.util.ArrayList;
3877fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard
396ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalardimport javax.xml.parsers.ParserConfigurationException;
406ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard
4177fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard/**
42b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard * Class grouping utilities for offline dictionary making.
43b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard *
44b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard * Those should not be used on-device, essentially because they are quite
45b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard * liberal about I/O and performance.
46b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard */
47b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalardpublic final class BinaryDictOffdeviceUtils {
48b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    // Prefix and suffix are arbitrary, the values do not really matter
49b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    private final static String PREFIX = "dicttool";
50b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    private final static String SUFFIX = ".tmp";
51b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
52f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard    public final static String COMPRESSION = "compressed";
53f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard    public final static String ENCRYPTION = "encrypted";
54b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
55a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard    private final static int MAX_DECODE_DEPTH = 8;
56fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka    private final static int COPY_BUFFER_SIZE = 8192;
57a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard
58b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    public static class DecoderChainSpec {
59a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        ArrayList<String> mDecoderSpec = new ArrayList<>();
60b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        File mFile;
61fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka
62b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        public DecoderChainSpec addStep(final String stepDescription) {
63b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            mDecoderSpec.add(stepDescription);
64b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            return this;
65b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        }
66fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka
67f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard        public String describeChain() {
68f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard            final StringBuilder s = new StringBuilder("raw");
69f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard            for (final String step : mDecoderSpec) {
70f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard                s.append(" > ");
71f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard                s.append(step);
72f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard            }
73f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard            return s.toString();
74f1d35ac5dc0cca2b357940cab1001cadca37bcb4Jean Chalard        }
75b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    }
76b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
7777fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard    public static void copy(final InputStream input, final OutputStream output) throws IOException {
78fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        final byte[] buffer = new byte[COPY_BUFFER_SIZE];
79fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) {
8077fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard            output.write(buffer, 0, readBytes);
81fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        }
8277fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard    }
83b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
84b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    /**
8548e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada     * Returns a decrypted/uncompressed dictionary.
86b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     *
8748e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada     * This will decrypt/uncompress any number of times as necessary until it finds the
88b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     * dictionary signature, and copy the decoded file to a temporary place.
8948e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada     * If this is not a dictionary, the method returns null.
90b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     */
9148e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada    public static DecoderChainSpec getRawDictionaryOrNull(final File src) {
9248e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada        return getRawDictionaryOrNullInternal(new DecoderChainSpec(), src, 0);
93b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    }
94b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
9548e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada    private static DecoderChainSpec getRawDictionaryOrNullInternal(
96a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard            final DecoderChainSpec spec, final File src, final int depth) {
97a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        // Unfortunately the decoding scheme we use can consider any data to be encrypted
98a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        // and will product some output, meaning it's not possible to reliably detect encrypted
99a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        // data. Thus, some non-dictionary files (especially small) ones may successfully decrypt
100a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        // over and over, ending in a stack overflow. Hence we limit the depth at which we try
101a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        // decoding the file.
102a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard        if (depth > MAX_DECODE_DEPTH) return null;
10348e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada        if (BinaryDictDecoderUtils.isBinaryDictionary(src)
10448e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada                || CombinedInputOutput.isCombinedDictionary(src.getAbsolutePath())) {
105b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            spec.mFile = src;
106b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            return spec;
107b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        }
108b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        // It's not a raw dictionary - try to see if it's compressed.
109b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        final File uncompressedFile = tryGetUncompressedFile(src);
110b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        if (null != uncompressedFile) {
111b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            final DecoderChainSpec newSpec =
11248e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada                    getRawDictionaryOrNullInternal(spec, uncompressedFile, depth + 1);
113b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            if (null == newSpec) return null;
114b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            return newSpec.addStep(COMPRESSION);
115b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        }
1160044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        // It's not a compressed either - try to see if it's crypted.
1170044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        final File decryptedFile = tryGetDecryptedFile(src);
1180044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        if (null != decryptedFile) {
1190044df6cf2f4ef00d78e530220565b8272187446Jean Chalard            final DecoderChainSpec newSpec =
12048e01ec1110ce591b9c5258f17262d7cb4b6c903Yuichiro Hanada                    getRawDictionaryOrNullInternal(spec, decryptedFile, depth + 1);
1210044df6cf2f4ef00d78e530220565b8272187446Jean Chalard            if (null == newSpec) return null;
1220044df6cf2f4ef00d78e530220565b8272187446Jean Chalard            return newSpec.addStep(ENCRYPTION);
1230044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        }
124b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        return null;
125b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    }
126b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard
127b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    /* Try to uncompress the file passed as an argument.
128b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     *
129b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     * If the file can be uncompressed, the uncompressed version is returned. Otherwise, null
130b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     * is returned.
131b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard     */
132b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    private static File tryGetUncompressedFile(final File src) {
133b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        try {
134b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            final File dst = File.createTempFile(PREFIX, SUFFIX);
135a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard            dst.deleteOnExit();
136fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            try (
137fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                final InputStream input = Compress.getUncompressedStream(
138fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                        new BufferedInputStream(new FileInputStream(src)));
139fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))
140fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            ) {
141fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                copy(input, output);
142fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                return dst;
143fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            }
144fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        } catch (final IOException e) {
145b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            // Could not uncompress the file: presumably the file is simply not a compressed file
146b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard            return null;
147b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard        }
148b3c98901c5fc1460b54cdf27d74405f27c88e74bJean Chalard    }
1490044df6cf2f4ef00d78e530220565b8272187446Jean Chalard
1500044df6cf2f4ef00d78e530220565b8272187446Jean Chalard    /* Try to decrypt the file passed as an argument.
1510044df6cf2f4ef00d78e530220565b8272187446Jean Chalard     *
1520044df6cf2f4ef00d78e530220565b8272187446Jean Chalard     * If the file can be decrypted, the decrypted version is returned. Otherwise, null
1530044df6cf2f4ef00d78e530220565b8272187446Jean Chalard     * is returned.
1540044df6cf2f4ef00d78e530220565b8272187446Jean Chalard     */
1550044df6cf2f4ef00d78e530220565b8272187446Jean Chalard    private static File tryGetDecryptedFile(final File src) {
1560044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        try {
1570044df6cf2f4ef00d78e530220565b8272187446Jean Chalard            final File dst = File.createTempFile(PREFIX, SUFFIX);
158a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard            dst.deleteOnExit();
159fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            try (
160fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                final InputStream input = Crypt.getDecryptedStream(
161fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                        new BufferedInputStream(new FileInputStream(src)));
162fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))
163fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            ) {
164fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                copy(input, output);
165fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                return dst;
166fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            }
167fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        } catch (final IOException e) {
168a8058d169dad450eca428ca76c5a0f44e45f41a7Jean Chalard            // Could not decrypt the file: presumably the file is simply not a crypted file
1690044df6cf2f4ef00d78e530220565b8272187446Jean Chalard            return null;
1700044df6cf2f4ef00d78e530220565b8272187446Jean Chalard        }
1710044df6cf2f4ef00d78e530220565b8272187446Jean Chalard    }
1726ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard
1736ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard    static FusionDictionary getDictionary(final String filename, final boolean report) {
1746ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard        final File file = new File(filename);
1756ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard        if (report) {
1766ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard            System.out.println("Dictionary : " + file.getAbsolutePath());
1776ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard            System.out.println("Size : " + file.length() + " bytes");
1786ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard        }
1796ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard        try {
1806ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard            if (XmlDictInputOutput.isXmlUnigramDictionary(filename)) {
181fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                if (report) {
182fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    System.out.println("Format : XML unigram list");
183fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                }
1846ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard                return XmlDictInputOutput.readDictionaryXml(
1856ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard                        new BufferedInputStream(new FileInputStream(file)),
1866ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard                        null /* shortcuts */, null /* bigrams */);
187fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            }
188fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            final DecoderChainSpec decodedSpec = getRawDictionaryOrNull(file);
189fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            if (null == decodedSpec) {
190fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                throw new RuntimeException("Does not seem to be a dictionary file " + filename);
191fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            }
192fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mFile.getAbsolutePath())) {
193fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                if (report) {
194fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    System.out.println("Format : Combined format");
195fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    System.out.println("Packaging : " + decodedSpec.describeChain());
196fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
1976ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard                }
198fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                try (final BufferedReader reader = new BufferedReader(
199fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                        new InputStreamReader(new FileInputStream(decodedSpec.mFile), "UTF-8"))) {
200fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    return CombinedInputOutput.readDictionaryCombined(reader);
201fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                }
202fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            }
203fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(
204fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    decodedSpec.mFile, 0, decodedSpec.mFile.length(),
205fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                    DictDecoder.USE_BYTEARRAY);
206fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            if (report) {
207fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                System.out.println("Format : Binary dictionary format");
208fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                System.out.println("Packaging : " + decodedSpec.describeChain());
209fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
2106ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard            }
211fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
212fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka        } catch (final IOException | SAXException | ParserConfigurationException |
213fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka                UnsupportedFormatException e) {
214fec4769e0b5eb43e1fad795a986acb0af8bb8a8fTadashi G. Takaoka            throw new RuntimeException("Can't read file " + filename, e);
2156ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard        }
2166ecc50a867dc09eb1d9dafe62f40e73de01b30cbJean Chalard    }
21777fe603a3d82f5fc28816520bac479ff48bf15e5Jean Chalard}
218