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